diff --git a/.flake8 b/.flake8 index 36ab602783e..5fa352709da 100644 --- a/.flake8 +++ b/.flake8 @@ -2,6 +2,8 @@ ignore = # whitespace before ':' (Black) E203, + # E501 line too long + E501, # line break before binary operator (Black) W503, @@ -20,50 +22,25 @@ per-file-ignores = # E741 ambiguous variable name 'l' man/build_html.py: E501 man/build_md.py: E501 - doc/python/m.distance.py: E501 + doc/examples/python/m.distance.py: E501 gui/scripts/d.wms.py: E501 gui/wxpython/image2target/g.gui.image2target.py: E501 gui/wxpython/photo2image/g.gui.photo2image.py: E501 gui/wxpython/psmap/*: E501 - gui/wxpython/vdigit/*: F841, E722, F405, F403 gui/wxpython/animation/g.gui.animation.py: E501 gui/wxpython/tplot/g.gui.tplot.py: E501 - gui/wxpython/iclass/frame.py: F405, F403 gui/wxpython/iclass/g.gui.iclass.py: E501 - gui/wxpython/iclass/statistics.py: F841, F405, F403 - gui/wxpython/location_wizard/wizard.py: E722 - gui/wxpython/mapdisp/main.py: E722 gui/wxpython/mapdisp/test_mapdisp.py: E501 gui/wxpython/mapswipe/g.gui.mapswipe.py: E501 - gui/wxpython/mapwin/base.py: E722 - gui/wxpython/mapwin/buffered.py: E722 - gui/wxpython/mapwin/graphics.py: E722 gui/wxpython/timeline/g.gui.timeline.py: E501 # Generated file gui/wxpython/menustrings.py: E501 - # F821 undefined name 'cmp' - # https://github.com/OSGeo/grass/issues/1809 - python/grass/pydispatch/saferef.py: F821 # C wrappers call libgis.G_gisinit before importing other modules. # TODO: Is this really needed? - python/grass/pygrass/vector/__init__.py: E402 - python/grass/pygrass/vector/__init__.py: E402 - python/grass/temporal/abstract_space_time_dataset.py: E722 - python/grass/temporal/c_libraries_interface.py: E722 - python/grass/temporal/core.py: E722 - python/grass/temporal/datetime_math.py: E722 - python/grass/temporal/spatial_topology_dataset_connector.py: E722 - python/grass/temporal/temporal_algebra.py: E722 - python/grass/temporal/temporal_granularity.py: E722 - # Current benchmarks/tests are changing sys.path before import. - # Possibly, a different approach should be taken there anyway. - python/grass/pygrass/tests/benchmark.py: F821 + python/grass/jupyter/__init__.py: E501 # Configuration file for Sphinx: # Ignoring import/code mix and line length. # Files not managed by Black - # Unused imports in init files - # F403 star import used; unable to detect undefined names - python/grass/temporal/__init__.py: F401, F403 # E402 module level import not at top of file scripts/r.semantic.label/r.semantic.label.py: E501 scripts/g.extension/g.extension.py: E501 diff --git a/.github/actions/create-upload-suggestions/action.yml b/.github/actions/create-upload-suggestions/action.yml index 791df86541b..fe6a111fcbe 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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 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@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 id: upload-changes if: >- ${{ always() && diff --git a/.github/labeler.yml b/.github/labeler.yml index 464ccdce2b8..8a9a307d29f 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -96,7 +96,7 @@ Windows: macOS: - changed-files: - any-glob-to-any-file: - - macosx/** + - macos/** Linux: - changed-files: - any-glob-to-any-file: @@ -145,7 +145,7 @@ notebook: - changed-files: - any-glob-to-any-file: - '**/*.ipynb' - - doc/notebooks/** + - doc/examples/notebooks/** - python/grass/jupyter/** C: - changed-files: diff --git a/.github/workflows/additional_checks.yml b/.github/workflows/additional_checks.yml index 76554f24db3..a8c4ea5966c 100644 --- a/.github/workflows/additional_checks.yml +++ b/.github/workflows/additional_checks.yml @@ -17,6 +17,8 @@ concurrency: group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} cancel-in-progress: true +permissions: {} + jobs: additional-checks: name: Additional checks @@ -43,7 +45,7 @@ jobs: exclude: mswindows .*\.bat .*/testsuite/data/.* - name: Set up Python - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 + uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 with: python-version: '3.10' diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index ce80ab353d2..7e5a036ec54 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -31,6 +31,7 @@ jobs: language: - c-cpp - python + - actions concurrency: group: ${{ github.workflow }}-${{ @@ -42,7 +43,7 @@ jobs: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up Python - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 + uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 with: python-version: '3.x' - name: Install non-Python dependencies @@ -52,11 +53,11 @@ jobs: sudo apt-get install -y wget git gawk findutils xargs -a <(awk '! /^ *(#|$)/' ".github/workflows/apt.txt") -r -- \ sudo apt-get install -y --no-install-recommends --no-install-suggests - - uses: rui314/setup-mold@b015f7e3f2938ad3a5ed6e5111a8c6c7c1d6db6e # v1 + - uses: rui314/setup-mold@f80524ca6eeaa76759b57fb78ddce5d87a20c720 # v1 if: ${{ matrix.language == 'c-cpp' }} - name: Initialize CodeQL - uses: github/codeql-action/init@babb554ede22fd5605947329c4d04d8e7a0b8155 # v3.27.7 + uses: github/codeql-action/init@dd746615b3b9d728a6a37ca2045b68ca76d4841a # v3.28.8 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/codeql-config.yml @@ -81,6 +82,6 @@ jobs: run: .github/workflows/build_ubuntu-22.04.sh "${HOME}/install" - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@babb554ede22fd5605947329c4d04d8e7a0b8155 # v3.27.7 + uses: github/codeql-action/analyze@dd746615b3b9d728a6a37ca2045b68ca76d4841a # v3.28.8 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/create_release_draft.yml b/.github/workflows/create_release_draft.yml index 37585bb7ebb..954637ef5a5 100644 --- a/.github/workflows/create_release_draft.yml +++ b/.github/workflows/create_release_draft.yml @@ -35,7 +35,7 @@ jobs: ref: ${{ github.ref }} fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 + uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 with: python-version: '3.11' - name: Create output directory @@ -74,7 +74,7 @@ jobs: sha256sum ${{ env.GRASS }}.tar.xz > ${{ env.GRASS }}.tar.xz.sha256 - name: Publish draft distribution to GitHub (for tags only) if: startsWith(github.ref, 'refs/tags/') - uses: softprops/action-gh-release@7b4da11513bf3f43f9999e90eabced41ab8bb048 # v2.2.0 + uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2.2.1 with: name: GRASS GIS ${{ github.ref_name }} body: | diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 577a9702011..e7b406d56df 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -22,8 +22,9 @@ on: release: types: [published] -jobs: +permissions: {} +jobs: # Run for push to configured branches and all published releases. # Take care of different os. # For main branch, created tags are: @@ -47,6 +48,10 @@ jobs: - ubuntu_wxgui fail-fast: false + permissions: + contents: read + packages: write + steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -66,9 +71,9 @@ jobs: latest=false suffix=-${{ matrix.os }} - name: Set up QEMU - uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0 + uses: docker/setup-qemu-action@53851d14592bedcffcf25ea515637cff71ef929a # v3.3.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3.7.1 + uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0 - name: Login to DockerHub uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 with: @@ -76,7 +81,7 @@ jobs: password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push id: docker_build - uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0 + uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0 with: push: true pull: true diff --git a/.github/workflows/gcc.yml b/.github/workflows/gcc.yml index 6b6286ef3f0..9e508baf81f 100644 --- a/.github/workflows/gcc.yml +++ b/.github/workflows/gcc.yml @@ -8,6 +8,8 @@ on: - releasebranch_* pull_request: +permissions: {} + jobs: build: name: ${{ matrix.c }} & ${{ matrix.cpp }} diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index ff40402fda4..0bf2158a7e7 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -14,6 +14,9 @@ env: concurrency: group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} cancel-in-progress: true + +permissions: {} + jobs: macos_build: name: macOS build @@ -49,11 +52,23 @@ jobs: id: date # Year and week of year so cache key changes weekly run: echo "date=$(date +%Y-%U)" >> "${GITHUB_OUTPUT}" + - name: Prepare Conda environment file + env: + test_dependencies: | + ipython + pytest + pytest-github-actions-annotate-failures + pytest-timeout + pytest-xdist + pyyaml + run: | + cp ./macos/files/conda-requirements-dev-arm64.txt "${RUNNER_TEMP}/macos_dependencies.txt" + echo "$test_dependencies" | sed "s/ /\n/g" >> "${RUNNER_TEMP}/macos_dependencies.txt" - name: Setup Mamba - uses: mamba-org/setup-micromamba@068f1ab4b37ed9b3d9f73da7db90a0cda0a48d29 # v2.0.3 + uses: mamba-org/setup-micromamba@0dea6379afdaffa5d528b3d1dabc45da37f443fc # v2.0.4 with: init-shell: bash - environment-file: .github/workflows/macos_dependencies.txt + environment-file: ${{ runner.temp }}/macos_dependencies.txt environment-name: grass-env # Persist on the same period (date). cache-environment-key: environment-${{ steps.date.outputs.date }} @@ -107,7 +122,7 @@ jobs: nc_spm_full_v2alpha2.tar.gz" - name: Make HTML test report available if: ${{ !cancelled() }} - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: name: testreport-macOS path: testreport diff --git a/.github/workflows/milestones.yml b/.github/workflows/milestones.yml index e2ade4eb091..4e9fa634a75 100644 --- a/.github/workflows/milestones.yml +++ b/.github/workflows/milestones.yml @@ -5,12 +5,17 @@ on: pull_request_target: types: [closed] +permissions: {} + jobs: assign-milestone: runs-on: ubuntu-latest if: github.event.pull_request.merged + permissions: + contents: read + pull-requests: write steps: - # Retreiving the current milestoone from API instead of github context, + # Retrieving the current milestone 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 }} diff --git a/.github/workflows/osgeo4w.yml b/.github/workflows/osgeo4w.yml index b58b168cb3d..5eb97761c03 100644 --- a/.github/workflows/osgeo4w.yml +++ b/.github/workflows/osgeo4w.yml @@ -8,6 +8,8 @@ on: - releasebranch_* pull_request: +permissions: {} + jobs: build: name: ${{ matrix.os }} build and tests @@ -55,7 +57,6 @@ jobs: gcc gettext libiconv - libpng libsystre libtre-git libwinpthread-git @@ -72,20 +73,15 @@ jobs: package-dir: "D:/OSGeo4W_pkg" packages: | cairo-devel - fftw freetype-devel gdal-devel - gdal-ecw - gdal-mrsid geos-devel + libjpeg-turbo-devel liblas-devel libpng-devel libpq-devel libtiff-devel - libxdr netcdf-devel - pdal-devel - pdcurses proj-devel python3-core python3-jupyter @@ -95,8 +91,9 @@ jobs: python3-ply python3-pytest python3-pywin32 + python3-six python3-wxpython - regex-devel + sqlite3-devel zstd-devel - name: Set number of cores for compilation @@ -107,8 +104,6 @@ jobs: - name: Compile GRASS GIS shell: msys2 {0} run: | - export CFLAGS="${CFLAGS} -pipe" - export CXXFLAGS="${CXXFLAGS} -pipe" .github/workflows/build_osgeo4w.sh - name: Print installed versions @@ -145,7 +140,7 @@ jobs: - name: Make HTML test report available if: ${{ always() }} - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: name: testreport-${{ matrix.os }} path: testreport diff --git a/.github/workflows/periodic_update.yml b/.github/workflows/periodic_update.yml index d27b8664c55..a60d8a8e159 100644 --- a/.github/workflows/periodic_update.yml +++ b/.github/workflows/periodic_update.yml @@ -10,12 +10,18 @@ on: # See https://crontab.guru/#32_10_*/100,1-7_*_WED - cron: "32 10 */100,1-7 * WED" +permissions: {} + # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: update-configure: # The type of runner that the job will run on runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + # Steps represent a sequence of tasks that will be executed as part of the job steps: - name: Create URL to the run output @@ -33,7 +39,7 @@ jobs: run: git status --ignored - name: Create Pull Request id: cpr - uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7.0.5 + uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6 with: commit-message: "config.guess + config.sub: updated from http://git.savannah.gnu.org/cgit/config.git/plain/" branch: periodic/update-configure diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 5ccf299f3ef..33ad627661b 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -8,6 +8,8 @@ on: - releasebranch_* pull_request: +permissions: {} + jobs: pytest: concurrency: @@ -35,7 +37,7 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up Python - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 + uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 with: python-version: ${{ matrix.python-version }} cache: pip @@ -48,7 +50,7 @@ jobs: xargs -a <(awk '! /^ *(#|$)/' ".github/workflows/apt.txt") -r -- \ sudo apt-get install -y --no-install-recommends --no-install-suggests - - uses: rui314/setup-mold@b015f7e3f2938ad3a5ed6e5111a8c6c7c1d6db6e # v1 + - uses: rui314/setup-mold@f80524ca6eeaa76759b57fb78ddce5d87a20c720 # v1 - name: Install Python dependencies run: | @@ -115,7 +117,7 @@ jobs: coverage html - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@7f8b4b4bde536c465e797be725718b88c5d95e0e # v5.1.1 + uses: codecov/codecov-action@13ce06bfc6bbe3ecf90edbbf1bc32fe5978ca1d3 # v5.3.1 with: verbose: true flags: pytest-python-${{ matrix.python-version }} @@ -123,7 +125,7 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} - name: Make python-only code coverage test report available if: ${{ !cancelled() }} - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 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 442a9bb11a8..a5a3d6a5474 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -8,6 +8,8 @@ on: - releasebranch_* pull_request: +permissions: {} + jobs: python-checks: name: Python Code Quality Checks @@ -25,18 +27,16 @@ jobs: env: # renovate: datasource=python-version depName=python - PYTHON_VERSION: "3.10" - MIN_PYTHON_VERSION: "3.8" - # renovate: datasource=pypi depName=black - BLACK_VERSION: "24.10.0" + PYTHON_VERSION: "3.13" + MIN_PYTHON_VERSION: "3.9" # renovate: datasource=pypi depName=flake8 FLAKE8_VERSION: "7.1.1" # renovate: datasource=pypi depName=pylint - PYLINT_VERSION: "2.12.2" + PYLINT_VERSION: "3.3.4" # renovate: datasource=pypi depName=bandit - BANDIT_VERSION: "1.8.0" + BANDIT_VERSION: "1.8.2" # renovate: datasource=pypi depName=ruff - RUFF_VERSION: "0.8.2" + RUFF_VERSION: "0.9.4" runs-on: ${{ matrix.os }} permissions: @@ -57,7 +57,7 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up Python - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 + uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 with: python-version: ${{ env.PYTHON_VERSION }} cache: pip @@ -71,6 +71,14 @@ jobs: continue-on-error: true - name: Run Ruff (apply fixes for suggestions) run: ruff check . --preview --fix --unsafe-fixes + - name: Run `ruff format` showing diff without failing + continue-on-error: true + if: ${{ !cancelled() }} + run: ruff format --diff + - name: Run `ruff format` fixing files + # Run `ruff format` even when `ruff check` fixed files: fixes can require formatting + if: ${{ !cancelled() }} + run: ruff format --diff - name: Create and uploads code suggestions to apply for Ruff # Will fail fast here if there are changes required id: diff-ruff @@ -82,21 +90,6 @@ jobs: # To keep repo's file structure in formatted changes artifact extra-upload-changes: pyproject.toml - - name: Install Black only - run: pip install black[jupyter]==${{ env.BLACK_VERSION }} - - - name: Run Black - run: black . - - - name: Create and uploads code suggestions to apply for Black - # Will fail fast here if there are changes required - id: diff-black - uses: ./.github/actions/create-upload-suggestions - with: - tool-name: black - # To keep repo's file structure in formatted changes artifact - extra-upload-changes: .clang-format - - name: Install non-Python dependencies run: | sudo apt-get update -y @@ -129,13 +122,13 @@ jobs: bandit -c pyproject.toml -iii -r . -f sarif -o bandit.sarif --exit-zero - name: Upload Bandit Scan Results - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: name: bandit.sarif path: bandit.sarif - name: Upload SARIF File into Security Tab - uses: github/codeql-action/upload-sarif@babb554ede22fd5605947329c4d04d8e7a0b8155 # v3.27.7 + uses: github/codeql-action/upload-sarif@dd746615b3b9d728a6a37ca2045b68ca76d4841a # v3.28.8 with: sarif_file: bandit.sarif @@ -147,7 +140,7 @@ jobs: run: | echo "MAKEFLAGS=-j$(nproc)" >> $GITHUB_ENV - - uses: rui314/setup-mold@b015f7e3f2938ad3a5ed6e5111a8c6c7c1d6db6e # v1 + - uses: rui314/setup-mold@f80524ca6eeaa76759b57fb78ddce5d87a20c720 # v1 - name: Build run: .github/workflows/build_${{ matrix.os }}.sh $HOME/install @@ -156,42 +149,44 @@ jobs: echo "$HOME/install/bin" >> $GITHUB_PATH - name: Run Pylint on grass package + # Until slower checks (like similarity) are reenabled, running in one step is faster + if: false run: | export PYTHONPATH=`grass --config python_path`:$PYTHONPATH export LD_LIBRARY_PATH=$(grass --config path)/lib:$LD_LIBRARY_PATH - cd python pylint --persistent=no --py-version=${{ env.MIN_PYTHON_VERSION }} --jobs=$(nproc) grass + - name: Run Pylint on other files using pytest + # Until slower checks (like similarity) are reenabled, running in one step is faster + if: false + run: | + pipx inject --include-apps pylint pytest + pipx inject pylint pytest-pylint pytest-github-actions-annotate-failures pytest-timeout + export PYTHONPATH=`grass --config python_path`:$PYTHONPATH + export LD_LIBRARY_PATH=$(grass --config path)/lib:$LD_LIBRARY_PATH + pytest --pylint -m pylint --pylint-jobs=$(nproc) \ + --pylint-ignore-patterns="${{ env.PylintIgnore }}" + env: + PylintIgnore: "python/.*,gui/.*" + - name: Run Pylint on wxGUI + # Until slower checks (like similarity) are reenabled, running in one step is faster + if: false run: | export PYTHONPATH=`grass --config python_path`:$PYTHONPATH export LD_LIBRARY_PATH=$(grass --config path)/lib:$LD_LIBRARY_PATH - cd gui/wxpython - pylint --persistent=no --py-version=${{ env.MIN_PYTHON_VERSION }} --jobs=$(nproc) * + pylint --persistent=no --py-version=${{ env.MIN_PYTHON_VERSION }} --jobs=$(nproc) gui - - name: Run Pylint on other files using pytest + - name: Run Pylint all in one pass run: | - pipx inject --include-apps pylint pytest==7.4.4 - pipx inject pylint pytest-pylint==0.19 pytest-github-actions-annotate-failures - echo "::warning file=.github/workflows/python-code-quality.yml,line=149,col=42,endColumn=48::\ - Temporarily downgraded pytest-pylint and pytest to allow merging other PRs.\ - The errors reported with a newer version seem legitimite and should be fixed \ - (2023-10-18, see https://github.com/OSGeo/grass/pull/3205)\ - (2024-01-28, see https://github.com/OSGeo/grass/issues/3380)" export PYTHONPATH=`grass --config python_path`:$PYTHONPATH export LD_LIBRARY_PATH=$(grass --config path)/lib:$LD_LIBRARY_PATH - pytest --pylint -m pylint --pylint-rcfile=.pylintrc --pylint-jobs=$(nproc) \ - --pylint-ignore-patterns="${{ env.PylintIgnore }}" - env: - PylintIgnore: "python/.*,gui/wxpython/.*,doc/.*,man/.*,utils/.*,locale/.*,raster/.*,\ - imagery/.*,scripts/r.in.wms/wms_drv.py,scripts/g.extension/g.extension.py,\ - temporal/t.rast.accdetect/t.rast.accdetect.py,temporal/t.rast.accumulate/t.rast.accumulate.py,\ - scripts/d.rast.edit/d.rast.edit.py" + pylint --persistent=no --py-version=${{ env.MIN_PYTHON_VERSION }} --jobs=$(nproc) . - name: Test compiling example modules run: | - ( cd doc/raster/r.example/ && make ) - ( cd doc/vector/v.example/ && make ) + ( cd doc/examples/raster/r.example/ && make ) + ( cd doc/examples/vector/v.example/ && make ) - name: Run Sphinx to check API documentation build run: | @@ -201,7 +196,7 @@ jobs: cp -rp dist.$ARCH/docs/html/libpython sphinx-grass - name: Make Sphinx documentation available - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: name: sphinx-grass path: sphinx-grass diff --git a/.github/workflows/super-linter.yml b/.github/workflows/super-linter.yml index b8fe555a343..3138da70dda 100644 --- a/.github/workflows/super-linter.yml +++ b/.github/workflows/super-linter.yml @@ -12,6 +12,8 @@ concurrency: group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} cancel-in-progress: true +permissions: {} + jobs: super-linter: name: GitHub Super Linter @@ -31,7 +33,7 @@ jobs: # list of files that changed across commits fetch-depth: 0 - name: Lint code base - uses: super-linter/super-linter/slim@e1cb86b6e8d119f789513668b4b30bf17fe1efe4 # v7.2.0 + uses: super-linter/super-linter/slim@85f7611e0f7b53c8573cca84aa0ed4344f6f6a4d # v7.2.1 env: DEFAULT_BRANCH: main # To report GitHub Actions status checks diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 9023df7d1b3..5f130a00812 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -10,6 +10,8 @@ on: - releasebranch_* pull_request: +permissions: {} + jobs: ubuntu: concurrency: @@ -149,7 +151,7 @@ jobs: - name: Make HTML test report available if: ${{ always() }} - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: name: testreport-${{ matrix.os }}-${{ matrix.config }}-${{ matrix.extra-include }} path: testreport diff --git a/.github/workflows/verify-success.yml b/.github/workflows/verify-success.yml index 237c239f576..92fff2e1d12 100644 --- a/.github/workflows/verify-success.yml +++ b/.github/workflows/verify-success.yml @@ -45,34 +45,36 @@ on: type: string required: true # Can't escape the handlebars in the description - description: + description: >- In the calling job that defines all the needed jobs, send `toJson(needs)` inside `$` followed by `{{ }}` fail_if_failure: type: boolean default: true - description: + description: >- If true, this workflow will fail if any job from 'needs_context was failed fail_if_cancelled: type: boolean default: true - description: + description: >- If true, this workflow will fail if any job from 'needs_context' was cancelled fail_if_skipped: type: boolean default: false - description: + description: >- If true, this workflow will fail if any job from 'needs_context' was skipped require_success: type: boolean default: true - description: + description: >- If true, this workflow will fail if no job from 'needs_context' was successful +permissions: {} + jobs: verify-success: name: Success diff --git a/.gitignore b/.gitignore index 49317acf642..fc8d6fbafd4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ /emacs.desktop -*~ *.lock !flake.lock *.pyc @@ -12,6 +11,7 @@ config.status* error.log grass.pc autom4te.cache/ +macos/cache/* # ignore specific file generated during make gui/wxpython/menustrings.py @@ -67,3 +67,82 @@ lib/*/latex/ .coverage .coverage.* coverage.xml + +# Created by https://www.toptal.com/developers/gitignore/api/windows,linux,macos +# Edit at https://www.toptal.com/developers/gitignore?templates=windows,linux,macos + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/windows,linux,macos diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c094d2bbd05..147a34e82db 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,24 +37,17 @@ repos: ) - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.8.2 + rev: v0.9.4 hooks: # Run the linter. - id: ruff args: [--fix, --preview] + # Run the formatter. + - id: ruff-format - repo: https://github.com/igorshubovych/markdownlint-cli - rev: v0.43.0 + rev: v0.44.0 hooks: - id: markdownlint-fix - # Using this mirror lets us use mypyc-compiled black, which is about 2x faster - - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.10.0 - hooks: - - id: black-jupyter - exclude: | - (?x)^( - python/libgrass_interface_generator/ - ) - repo: https://github.com/pycqa/flake8 rev: 7.1.1 hooks: diff --git a/.pylintrc b/.pylintrc deleted file mode 100644 index b260c0fd401..00000000000 --- a/.pylintrc +++ /dev/null @@ -1,656 +0,0 @@ -[MASTER] - -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code. -extension-pkg-allow-list= - -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code. (This is an alternative name to extension-pkg-allow-list -# for backward compatibility.) -extension-pkg-whitelist= - -# Return non-zero exit code if any of these messages/categories are detected, -# even if score is above --fail-under value. Syntax same as enable. Messages -# specified are enabled, while categories only check already-enabled messages. -fail-on= - -# Specify a score threshold to be exceeded before program exits with error. -fail-under=10.0 - -# Files or directories to be skipped. They should be base names, not paths. -ignore=CVS - -# Add files or directories matching the regex patterns to the ignore-list. The -# regex matches against paths and can be in Posix or Windows format. -ignore-paths=python/.*, - gui/wxpython/.*, - .*/testsuite/.*, - -# Files or directories matching the regex patterns are skipped. The regex -# matches against base names, not paths. -ignore-patterns= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -#init-hook= - -# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the -# number of processors available to use. -jobs=1 - -# Control the amount of potential inferred values when inferring a single -# object. This can help the performance when dealing with large functions or -# complex, nested conditions. -limit-inference-results=100 - -# List of plugins (as comma separated values of python module names) to load, -# usually to register additional checkers. -load-plugins= - -# Pickle collected data for later comparisons. -persistent=yes - -# Minimum Python version to use for version dependent checks. Will default to -# the version used to run pylint. -py-version=3.8 - -# When enabled, pylint would attempt to guess common misconfiguration and emit -# user-friendly hints instead of false-positive error messages. -suggestion-mode=yes - -# Allow loading of arbitrary C extensions. Extensions are imported into the -# active Python interpreter and may run arbitrary code. -unsafe-load-any-extension=no - - -[MESSAGES CONTROL] - -# Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. -confidence= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once). You can also use "--disable=all" to -# disable everything first and then re-enable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use "--disable=all --enable=classes -# --disable=W". -disable=raw-checker-failed, - bad-inline-option, - locally-disabled, - file-ignored, - suppressed-message, - deprecated-pragma, - fixme, - missing-module-docstring, - missing-function-docstring, - missing-class-docstring, - import-outside-toplevel, - useless-import-alias, - consider-using-from-import, - wrong-import-order, - wrong-import-position, - import-error, - no-name-in-module, - no-member, - unused-import, - unused-variable, - unused-argument, - expression-not-assigned, - self-assigning-variable, - unspecified-encoding, - no-self-use, - inconsistent-return-statements, - invalid-name, - broad-except, - bare-except, - raise-missing-from, - undefined-variable, - undefined-loop-variable, - cell-var-from-loop, - not-callable, - global-variable-undefined, - global-statement, - global-variable-not-assigned, - attribute-defined-outside-init, - line-too-long, - eval-used, - no-value-for-parameter, - deprecated-method, - deprecated-argument, - anomalous-backslash-in-string, - redefined-builtin, - redefined-outer-name, - useless-object-inheritance, - useless-return, - consider-using-f-string, - consider-iterating-dictionary, - consider-using-dict-items, - consider-using-enumerate, - consider-using-in, - consider-using-with, - consider-using-generator, - consider-using-max-builtin, - consider-using-min-builtin, - consider-using-get, - unnecessary-comprehension, - unnecessary-pass, - super-with-arguments, - unidiomatic-typecheck, - use-implicit-booleaness-not-comparison, - no-else-break, - no-else-return, - no-else-raise, - simplifiable-if-statement, - use-maxsplit-arg, - use-list-literal, - use-a-generator, - unneeded-not, - chained-comparison, - use-symbolic-message-instead - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time (only on the command line, not in the configuration file where -# it should appear only once). See also the "--disable" option for examples. -enable=c-extension-no-member - - -[REPORTS] - -# Python expression which should return a score less than or equal to 10. You -# have access to the variables 'error', 'warning', 'refactor', and 'convention' -# which contain the number of messages in each category, as well as 'statement' -# which is the total number of statements analyzed. This score is used by the -# global evaluation report (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details. -#msg-template= - -# Set the output format. Available formats are text, parseable, colorized, json -# and msvs (visual studio). You can also give a reporter class, e.g. -# mypackage.mymodule.MyReporterClass. -output-format=text - -# Tells whether to display a full report or only the messages. -reports=no - -# Activate the evaluation score. -score=yes - - -[REFACTORING] - -# Maximum number of nested blocks for function / method body -max-nested-blocks=7 - -# Complete name of functions that never returns. When checking for -# inconsistent-return-statements if a never returning function is called then -# it will be considered as an explicit return statement and no message will be -# printed. -never-returning-functions=sys.exit,argparse.parse_error - - -[SPELLING] - -# Limits count of emitted suggestions for spelling mistakes. -max-spelling-suggestions=4 - -# Spelling dictionary name. Available dictionaries: fr_MC (myspell), fr_CA -# (myspell), fr_BE (myspell), fr_LU (myspell), fr_CH (myspell), fr_FR -# (myspell), ar (myspell), es_CR (myspell), de_CH_frami (myspell), es_EC -# (myspell), ar_YE (myspell), en_CA (myspell), ar_BH (myspell), ar_IN -# (myspell), ar_TN (myspell), en_ZA (myspell), de_DE_frami (myspell), ar_SY -# (myspell), ar_IQ (myspell), ar_LB (myspell), ar_KW (myspell), ru_RU -# (myspell), es_BO (myspell), en_GB (myspell), ar_SD (myspell), de_DE -# (myspell), es_CU (myspell), es_PA (myspell), ar_EG (myspell), es_HN -# (myspell), de_CH (myspell), es_NI (myspell), es_AR (myspell), es_ES -# (myspell), ar_SA (myspell), es_VE (myspell), de_AT_frami (myspell), it_IT -# (myspell), ar_OM (myspell), ar_DZ (myspell), it_CH (myspell), es_MX -# (myspell), es_PY (myspell), en_AU (myspell), es_DO (myspell), es_SV -# (myspell), es_PR (myspell), es_GT (myspell), ar_LY (myspell), ar_JO -# (myspell), en_US (myspell), de_AT (myspell), es_PE (myspell), ar_QA -# (myspell), es_CL (myspell), pt_BR (myspell), ar_AE (myspell), pt_PT -# (myspell), es_CO (myspell), es_UY (myspell), ar_MA (myspell), fr (myspell), -# es_US (myspell), en (aspell). -spelling-dict= - -# List of comma separated words that should be considered directives if they -# appear and the beginning of a comment and should not be checked. -spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy: - -# List of comma separated words that should not be checked. -spelling-ignore-words= - -# A path to a file that contains the private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to the private dictionary (see the -# --spelling-private-dict-file option) instead of raising a message. -spelling-store-unknown-words=no - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=FIXME, - XXX, - TODO - -# Regular expression of note tags to take in consideration. -#notes-rgx= - - -[STRING] - -# This flag controls whether inconsistent-quotes generates a warning when the -# character used as a quote delimiter is used inconsistently within a module. -check-quote-consistency=no - -# This flag controls whether the implicit-str-concat should generate a warning -# on implicit string concatenation in sequences defined over several lines. -check-str-concat-over-line-jumps=no - - -[TYPECHECK] - -# List of decorators that produce context managers, such as -# contextlib.contextmanager. Add to this list to register other decorators that -# produce valid context managers. -contextmanager-decorators=contextlib.contextmanager - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E1101 when accessed. Python regular -# expressions are accepted. -generated-members= - -# Tells whether missing members accessed in mixin class should be ignored. A -# class is considered mixin if its name matches the mixin-class-rgx option. -ignore-mixin-members=yes - -# Tells whether to warn about missing members when the owner of the attribute -# is inferred to be None. -ignore-none=yes - -# This flag controls whether pylint should warn about no-member and similar -# checks whenever an opaque object is returned when inferring. The inference -# can return multiple potential results while evaluating a Python object, but -# some branches might not be evaluated, which results in partial inference. In -# that case, it might be useful to still emit no-member and other checks for -# the rest of the inferred objects. -ignore-on-opaque-inference=yes - -# List of class names for which member attributes should not be checked (useful -# for classes with dynamically set attributes). This supports the use of -# qualified names. -ignored-classes=optparse.Values,thread._local,_thread._local - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis). It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules= - -# Show a hint with possible names when a member name was not found. The aspect -# of finding the hint is based on edit distance. -missing-member-hint=yes - -# The minimum edit distance a name should have in order to be considered a -# similar match for a missing member name. -missing-member-hint-distance=1 - -# The total number of similar names that should be taken in consideration when -# showing a hint for a missing member. -missing-member-max-choices=1 - -# Regex pattern to define which classes are considered mixins ignore-mixin- -# members is set to 'yes' -mixin-class-rgx=.*[Mm]ixin - -# List of decorators that change the signature of a decorated function. -signature-mutators= - - -[VARIABLES] - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid defining new builtins when possible. -# Translation function is (unfortunately) defined as a buildin. -additional-builtins=_ - -# Tells whether unused global variables should be treated as a violation. -allow-global-unused-variables=yes - -# List of names allowed to shadow builtins -allowed-redefined-builtins= - -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_, - _cb - -# A regular expression matching the name of dummy variables (i.e. expected to -# not be used). -# On top of the defaults, simple unused is also permissible. -dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore. -ignored-argument-names=_.*|^ignored_|^unused_ - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# List of qualified module names which can have objects that can redefine -# builtins. -redefining-builtins-modules=past.builtins,future.builtins,builtins,io - - -[SIMILARITIES] - -# Comments are removed from the similarity computation -ignore-comments=yes - -# Docstrings are removed from the similarity computation -ignore-docstrings=yes - -# Imports are removed from the similarity computation -ignore-imports=no - -# Signatures are removed from the similarity computation -ignore-signatures=no - -# Minimum lines number of a similarity. -# Matching only larger chunks of code, not the default 4 lines. -min-similarity-lines=50 - - -[LOGGING] - -# The type of string formatting that logging methods do. `old` means using % -# formatting, `new` is for `{}` formatting. -logging-format-style=old - -# Logging modules to check that the string format arguments are in logging -# function parameter format. -logging-modules=logging - - -[BASIC] - -# Naming style matching correct argument names. -argument-naming-style=snake_case - -# Regular expression matching correct argument names. Overrides argument- -# naming-style. -#argument-rgx= - -# Naming style matching correct attribute names. -attr-naming-style=snake_case - -# Regular expression matching correct attribute names. Overrides attr-naming- -# style. -#attr-rgx= - -# Bad variable names which should always be refused, separated by a comma. -bad-names=foo, - bar, - baz, - toto, - tutu, - tata - -# Bad variable names regexes, separated by a comma. If names match any regex, -# they will always be refused -bad-names-rgxs= - -# Naming style matching correct class attribute names. -class-attribute-naming-style=any - -# Regular expression matching correct class attribute names. Overrides class- -# attribute-naming-style. -#class-attribute-rgx= - -# Naming style matching correct class constant names. -class-const-naming-style=UPPER_CASE - -# Regular expression matching correct class constant names. Overrides class- -# const-naming-style. -#class-const-rgx= - -# Naming style matching correct class names. -class-naming-style=PascalCase - -# Regular expression matching correct class names. Overrides class-naming- -# style. -#class-rgx= - -# Naming style matching correct constant names. -const-naming-style=UPPER_CASE - -# Regular expression matching correct constant names. Overrides const-naming- -# style. -#const-rgx= - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=-1 - -# Naming style matching correct function names. -function-naming-style=snake_case - -# Regular expression matching correct function names. Overrides function- -# naming-style. -#function-rgx= - -# Good variable names which should always be accepted, separated by a comma. -good-names=i, - j, - k, - x, - y, - z, - ex, - Run, - _ - -# Good variable names regexes, separated by a comma. If names match any regex, -# they will always be accepted -good-names-rgxs= - -# Include a hint for the correct naming format with invalid-name. -include-naming-hint=no - -# Naming style matching correct inline iteration names. -inlinevar-naming-style=any - -# Regular expression matching correct inline iteration names. Overrides -# inlinevar-naming-style. -#inlinevar-rgx= - -# Naming style matching correct method names. -method-naming-style=snake_case - -# Regular expression matching correct method names. Overrides method-naming- -# style. -#method-rgx= - -# Naming style matching correct module names. -module-naming-style=snake_case - -# Regular expression matching correct module names. Overrides module-naming- -# style. -#module-rgx= - -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= - -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=^_ - -# List of decorators that produce properties, such as abc.abstractproperty. Add -# to this list to register other decorators that produce valid properties. -# These decorators are taken in consideration only for invalid-name. -property-classes=abc.abstractproperty - -# Naming style matching correct variable names. -variable-naming-style=snake_case - -# Regular expression matching correct variable names. Overrides variable- -# naming-style. -#variable-rgx= - - -[FORMAT] - -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -expected-line-ending-format= - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=^\s*(# )??$ - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - -# Maximum number of characters on a single line. -max-line-length=100 - -# Maximum number of lines in a module. -max-module-lines=2000 - -# Allow the body of a class to be on the same line as the declaration if body -# contains single statement. -single-line-class-stmt=no - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=no - - -[IMPORTS] - -# List of modules that can be imported at any level, not just the top level -# one. -allow-any-import-level= - -# Allow wildcard imports from modules that define __all__. -allow-wildcard-with-all=no - -# Analyse import fallback blocks. This can be used to support both Python 2 and -# 3 compatible code, which means that the block might have code that exists -# only in one or another interpreter, leading to false positives when analysed. -analyse-fallback-blocks=no - -# Deprecated modules which should not be used, separated by a comma. -deprecated-modules= - -# Output a graph (.gv or any supported image format) of external dependencies -# to the given file (report RP0402 must not be disabled). -ext-import-graph= - -# Output a graph (.gv or any supported image format) of all (i.e. internal and -# external) dependencies to the given file (report RP0402 must not be -# disabled). -import-graph= - -# Output a graph (.gv or any supported image format) of internal dependencies -# to the given file (report RP0402 must not be disabled). -int-import-graph= - -# Force import order to recognize a module as part of the standard -# compatibility libraries. -known-standard-library= - -# Force import order to recognize a module as part of a third party library. -known-third-party=enchant - -# Couples of modules and preferred modules, separated by a comma. -preferred-modules= - - -[CLASSES] - -# Warn about protected attribute access inside special methods -check-protected-access-in-special-methods=no - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__, - __new__, - setUp, - __post_init__ - -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict, - _fields, - _replace, - _source, - _make - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=cls - - -[DESIGN] - -# List of regular expressions of class ancestor names to ignore when counting -# public methods (see R0903) -exclude-too-few-public-methods= - -# List of qualified class names to ignore when counting class parents (see -# R0901) -ignored-parents= - -# Maximum number of arguments for function / method. -max-args=15 - -# Maximum number of attributes for a class (see R0902). -max-attributes=25 - -# Maximum number of boolean expressions in an if statement (see R0916). -max-bool-expr=8 - -# Maximum number of branch for function / method body. -max-branches=44 - -# Maximum number of locals for function / method body. -max-locals=50 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=20 - -# Maximum number of return / yield for function / method body. -max-returns=9 - -# Maximum number of statements in function / method body. -max-statements=220 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=1 - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "BaseException, Exception". -overgeneral-exceptions=BaseException, - Exception diff --git a/Dockerfile b/Dockerfile index 6b18a841ad4..a1be969f46e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -# syntax=docker/dockerfile:1.12@sha256:db1ff77fb637a5955317c7a3a62540196396d565f3dd5742e76dddbb6d75c4c5 +# syntax=docker/dockerfile:1.13@sha256:426b85b823c113372f766a963f68cfd9cd4878e1bcc0fda58779127ee98a28eb # Note: This file must be kept in sync in ./Dockerfile and ./docker/ubuntu/Dockerfile. # Changes to this file must be copied over to the other file. diff --git a/Makefile b/Makefile index 8b32a149a5a..a3f533c7d07 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ # PURPOSE: It provides the commands necessary to compile, install, # clean, and uninstall GRASS # See INSTALL.md file for usage. -# COPYRIGHT: (C) 2002-2024 by the GRASS Development Team +# COPYRIGHT: (C) 2002-2025 by the GRASS Development Team # # This program is free software under the GNU General Public # License (>=v2). Read the file COPYING that comes with GRASS @@ -46,7 +46,6 @@ DIRS = \ visualization \ locale \ man \ - macosx \ mswindows SUBDIRS = $(DIRS) diff --git a/README.md b/README.md index 7dfdb49df82..ef39e640721 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ visualization. Launch this repository in Binder and experiment with GRASS's Python API in Jupyter Notebooks by clicking the button below: -[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/OSGeo/grass/main?labpath=doc%2Fnotebooks%2Fjupyter_example.ipynb) +[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/OSGeo/grass/main?labpath=doc%2Fexamples%2Fnotebooks%2Fjupyter_example.ipynb) ## Contributing diff --git a/REQUIREMENTS.md b/REQUIREMENTS.md index ff13e49331e..4d61c80e323 100644 --- a/REQUIREMENTS.md +++ b/REQUIREMENTS.md @@ -137,7 +137,7 @@ MacOSX users may go here to download precompiled libraries etc.: --- -© _GRASS Development Team 1997-2024_ +© _GRASS Development Team 1997-2025_ Please report bugs here: [https://grass.osgeo.org/contribute/](https://grass.osgeo.org/contribute/) diff --git a/configure.ac b/configure.ac index 0aeef9efc05..47d78b469b1 100644 --- a/configure.ac +++ b/configure.ac @@ -9,7 +9,7 @@ # PURPOSE: This configure runs all the tests to determine what components # are installed on the current system. It also defines certain # configuration variables for compilation and installation. -# COPYRIGHT: (C) 2000-2024 by the GRASS Development Team +# COPYRIGHT: (C) 2000-2025 by the GRASS Development Team # # This program is free software under the GNU General # Public License (>=v2). Read the file COPYING that diff --git a/db/databaseintro.html b/db/databaseintro.html index c400ab6e0ed..f7fb11a3b47 100644 --- a/db/databaseintro.html +++ b/db/databaseintro.html @@ -35,7 +35,7 @@

Attribute data import and export

Further conversion tools: diff --git a/db/db.connect/db.connect.html b/db/db.connect/db.connect.html index 434124b4e65..5b6e2fa1517 100644 --- a/db/db.connect/db.connect.html +++ b/db/db.connect/db.connect.html @@ -9,7 +9,7 @@

DESCRIPTION

NOTES

-Values are stored in the mapset's VAR file; +Values are stored in the mapset's VAR file; the connection is not tested for validity.

The -p flag will display the current connection parameters.

The -c flag will silently check if the connection parameters have diff --git a/db/db.describe/testsuite/test_dbdescribe.py b/db/db.describe/testsuite/test_dbdescribe.py index f48debf0388..dc4d3298a06 100644 --- a/db/db.describe/testsuite/test_dbdescribe.py +++ b/db/db.describe/testsuite/test_dbdescribe.py @@ -5,6 +5,7 @@ @author: lucadelu """ + import json from grass.gunittest.case import TestCase diff --git a/db/db.execute/db.execute.html b/db/db.execute/db.execute.html index b8fcce41d7b..08d5b10ae54 100644 --- a/db/db.execute/db.execute.html +++ b/db/db.execute/db.execute.html @@ -76,7 +76,7 @@

EXAMPLES

Update attribute with multiple SQL instructions in file -(e.g., file.sql, instruction line must end with a semicolon): +(e.g., file.sql, instruction line must end with a semicolon):
 UPDATE roads SET travelcost=5 WHERE cat=1;
 UPDATE roads SET travelcost=2 WHERE cat=2;
diff --git a/db/db.login/db.login.html b/db/db.login/db.login.html
index 7173f2c044c..6f2eb652e4b 100644
--- a/db/db.login/db.login.html
+++ b/db/db.login/db.login.html
@@ -15,8 +15,8 @@ 

NOTE

file in the user account, specifically Only the file owner can access this file. diff --git a/db/drivers/ogr/README b/db/drivers/ogr/README index b5fa80bb25e..fde41d85722 100644 --- a/db/drivers/ogr/README +++ b/db/drivers/ogr/README @@ -5,4 +5,4 @@ SQL DB support via OGR ----------------------------------------------------- General Documentation - http://www.remotesensing.org/gdal/ogr/ogr_sql.html + https://gdal.org/en/stable/user/ogr_sql_dialect.html diff --git a/db/drivers/postgres/README b/db/drivers/postgres/README index a2959739f6e..76d44e6bf6b 100644 --- a/db/drivers/postgres/README +++ b/db/drivers/postgres/README @@ -10,7 +10,7 @@ by G_warning(). FIELD TYPES: Field type code may change. See for example -http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/include/catalog/pg_type.h;hb=refs/heads/master +https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/include/catalog/pg_type.h;hb=refs/heads/master Because of this, type codes must be read from server when database is opened. @@ -19,7 +19,7 @@ Check also for PostgreSQL data types for defining them in GRASS: Supported types in ./globals.h: -(See https://www.postgresql.org/docs/9.4/interactive/datatype.html) +(See https://www.postgresql.org/docs/current/datatype.html) DB_C_TYPE_INT: bit, int2, smallint, int4, int, integer, int8, bigint, serial, oid diff --git a/db/drivers/postgres/grass-pg.html b/db/drivers/postgres/grass-pg.html index 3950c0dd315..721d9a035b0 100644 --- a/db/drivers/postgres/grass-pg.html +++ b/db/drivers/postgres/grass-pg.html @@ -5,7 +5,7 @@

Creating a PostgreSQL database

-A new database is created with createdb, see +A new database is created with createdb, see the PostgreSQL manual for details. @@ -76,7 +76,7 @@

Geometry import from PostgreSQL table into GRASS

PostGIS: PostgreSQL with vector geometry

-PostGIS: +PostGIS: adds geographic object support to PostgreSQL.

Example: Import from PostGIS

@@ -114,15 +114,15 @@

Example: Import from PostGIS

Geometry Converters

@@ -143,6 +143,6 @@

REFERENCES

diff --git a/db/drivers/sqlite/grass-sqlite.html b/db/drivers/sqlite/grass-sqlite.html index ea2f9526506..57808ccb79c 100644 --- a/db/drivers/sqlite/grass-sqlite.html +++ b/db/drivers/sqlite/grass-sqlite.html @@ -34,7 +34,7 @@

Operators available in conditions

Browsing table data in DB

-A convenient SQLite front-end is sqlitebrowser. +A convenient SQLite front-end is sqlitebrowser. To open a DB file stored within the current mapset, the following way is suggested (corresponds to above database connection): @@ -78,5 +78,5 @@

SEE ALSO

SQLite web site, SQLite manual, - sqlite - Management Tools + sqlite - Management Tools diff --git a/display/d.barscale/draw_scale.c b/display/d.barscale/draw_scale.c index c53a48ed576..64612b1cead 100644 --- a/display/d.barscale/draw_scale.c +++ b/display/d.barscale/draw_scale.c @@ -634,8 +634,12 @@ int draw_scale(double east, double north, int length, int seg, int units, } D_stroke(); - if (fontsize < 0) + if (fontsize < 0) { + if (length != 0) { + G_free(label); + } return 0; + } /* draw the distance + units text */ @@ -676,6 +680,9 @@ int draw_scale(double east, double north, int length, int seg, int units, D_pos_abs(x_pos + 5 - (tr - tl), y_pos + ysize / 2 + (tt - tb) / 2); D_text(label); } + if (length != 0) { + G_free(label); + } return 0; } diff --git a/display/d.extract/d.extract.html b/display/d.extract/d.extract.html index 39c37915ade..9e09ddadc03 100644 --- a/display/d.extract/d.extract.html +++ b/display/d.extract/d.extract.html @@ -6,7 +6,7 @@

DESCRIPTION

EXAMPLE

-

Graphically extract roads from a roads map:

+

Graphically extract roads from a roads map

 d.mon x0
 d.vect roads
diff --git a/display/d.graph/d.graph.html b/display/d.graph/d.graph.html
index d70530f5da1..34017d34bc7 100644
--- a/display/d.graph/d.graph.html
+++ b/display/d.graph/d.graph.html
@@ -132,10 +132,10 @@ 

COMMANDS

symbol type size xper yper [line_color [fill_color]]
A symbol is drawn at the given size on the display monitor. The xper and yper options define the center of the icon and -are given as a percentage of the display frame (0,0 is lower left). -The symbol can be any of those stored in $GISBASE/etc/symbol/ +are given as a percentage of the display frame (0,0 is lower left). +The symbol can be any of those stored in $GISBASE/etc/symbol/ (e.g. basic/circle) or stored in the user's mapset directory in the -form $MAPSET/symbol/type/name. +form $MAPSET/symbol/type/name. The colors may be either a standard color name, an R:G:B triplet, or "none". If using an R:G:B triplet, each color value can range from 0-255. If not specified the default line_color is black and the default diff --git a/display/d.grid/d.grid.html b/display/d.grid/d.grid.html index 7d775d71bc7..412a6a413ca 100644 --- a/display/d.grid/d.grid.html +++ b/display/d.grid/d.grid.html @@ -9,7 +9,7 @@

DESCRIPTION

If the user provides a -g flag a geographic (projected) grid will be drawn. With the -g flag the size argument accepts both decimal degrees and colon separated -ddd:mm:ss coordinates (eg. 00:30:00 for half of a degree). +ddd:mm:ss coordinates (eg. 00:30:00 for half of a degree). A geographic grid cannot be drawn for a latitude/longitude or XY projection. diff --git a/display/d.his/d.his.html b/display/d.his/d.his.html index 56f7d99bebc..49909d20d04 100644 --- a/display/d.his/d.his.html +++ b/display/d.his/d.his.html @@ -1,5 +1,9 @@

DESCRIPTION

+d.his displays the result obtained by combining hue, +intensity, and saturation (HIS) values from user-specified input +raster map layers. +

HIS stands for hue, intensity, and saturation. This program produces a raster map layer providing a visually pleasing combination of hue, intensity, and diff --git a/display/d.labels/d.labels.html b/display/d.labels/d.labels.html index 61a9f94052e..0bcf9b07c57 100644 --- a/display/d.labels/d.labels.html +++ b/display/d.labels/d.labels.html @@ -7,7 +7,7 @@

DESCRIPTION

the v.label program or simply created by the user as an ASCII file (using a text editor) and placed in the appropriate directory under the user's current mapset and project -(i.e. $MAPSET/paint/labels/). +(i.e. $MAPSET/paint/labels/).

NOTES

diff --git a/display/d.legend.vect/d.legend.vect.html b/display/d.legend.vect/d.legend.vect.html index 43602b0e936..36a2e4e6533 100644 --- a/display/d.legend.vect/d.legend.vect.html +++ b/display/d.legend.vect/d.legend.vect.html @@ -1,6 +1,6 @@

DESCRIPTION

-Module d.legend.vect draws vector legend of currently displayed vector maps. +d.legend.vect draws vector legend of currently displayed vector maps.

Parameter at defines the screen position of upper-left legend corner. Parameter columns defines the number of legend columns. diff --git a/display/d.linegraph/d.linegraph.html b/display/d.linegraph/d.linegraph.html index 23a11463df9..a9782c98c2c 100644 --- a/display/d.linegraph/d.linegraph.html +++ b/display/d.linegraph/d.linegraph.html @@ -24,7 +24,7 @@

File inputs

should) be only relative paths to these files. While this is not recommended for scripting, it can be advantageous when typing the paths manually. For example when all files are stored in the directory -/home/john/data, the user can provide the following in the command +/home/john/data, the user can provide the following in the command line:
@@ -145,7 +145,7 @@ 

EXAMPLE

-->

-The next command sequence creates a file plot.png in the current +The next command sequence creates a file plot.png in the current directory which is the drawing made by d.linegraph.

diff --git a/display/d.mon/d.mon.html b/display/d.mon/d.mon.html
index ed1e6cb2344..9f76cff9880 100644
--- a/display/d.mon/d.mon.html
+++ b/display/d.mon/d.mon.html
@@ -52,8 +52,8 @@ 

Releasing (unselecting) a monitor

NOTES

d.mon is designed for interactive use. If non-interactive use -is needed (e.g., in a script) set GRASS_RENDER_IMMEDIATE=png -(or =cairo) and use the related environment +is needed (e.g., in a script) set GRASS_RENDER_IMMEDIATE=png +(or =cairo) and use the related environment variables to control output size etc.

EXAMPLES

@@ -73,7 +73,7 @@

wx0 monitor

Figure: The initialization of display monitor wx0
-All subsequently displayed data will be rendered on monitor wx0. +All subsequently displayed data will be rendered on monitor wx0.
 g.region raster=elevation -p
@@ -95,7 +95,7 @@ 

CAIRO file renderer monitor

From this moment on all displayed data will be rendered into -file output.pdf. +file output.pdf.

List running monitors

diff --git a/display/d.mon/render_cmd.py b/display/d.mon/render_cmd.py index 225a82ebe9b..2959440897e 100644 --- a/display/d.mon/render_cmd.py +++ b/display/d.mon/render_cmd.py @@ -39,22 +39,23 @@ def remove_mapfile(mapfile): # read environment variables from file def read_env_file(env_file): width = height = legfile = None - fd = open(env_file) - if fd is None: - grass.fatal("Unable to open file '{0}'".format(env_file)) - lines = fd.readlines() - for line in lines: - if line.startswith("#"): - continue - k, v = line.rstrip("\n").split("#", 1)[0].strip().split("=", 1) - os.environ[k] = v - if width is None and k == "GRASS_RENDER_WIDTH": - width = int(v) - if height is None and k == "GRASS_RENDER_HEIGHT": - height = int(v) - if legfile is None and k == "GRASS_LEGEND_FILE": - legfile = v - fd.close() + try: + with open(env_file) as fd: + for line in fd: + line = line.strip() + if line.startswith("#") or not line: + continue + # Split on comment and parse key=value + k, v = line.split("#", 1)[0].strip().split("=", 1) + os.environ[k] = v + if width is None and k == "GRASS_RENDER_WIDTH": + width = int(v) + if height is None and k == "GRASS_RENDER_HEIGHT": + height = int(v) + if legfile is None and k == "GRASS_LEGEND_FILE": + legfile = v + except OSError: + grass.fatal(f"Unable to open file '{env_file}'") if width is None or height is None: grass.fatal("Unknown monitor size") @@ -86,20 +87,20 @@ def update_cmd_file(cmd_file, cmd, mapfile): mode = "w" if cmd[0] == "d.erase" else "a" # update cmd file - fd = open(cmd_file, mode) - if fd is None: - grass.fatal("Unable to open file '{0}'".format(cmd_file)) - if mode == "a": - frame = os.getenv("GRASS_RENDER_FRAME", None) - if frame: - fd.write("# GRASS_RENDER_FRAME={0}\n".format(frame)) - if mapfile: - fd.write("# GRASS_RENDER_FILE={0}\n".format(mapfile)) - fd.write(" ".join(gtask.cmdtuple_to_list(cmd))) - fd.write("\n") - else: - fd.write("") - fd.close() + try: + with open(cmd_file, mode) as fd: + if mode == "a": + frame = os.getenv("GRASS_RENDER_FRAME", None) + if frame: + fd.write("# GRASS_RENDER_FRAME={0}\n".format(frame)) + if mapfile: + fd.write("# GRASS_RENDER_FILE={0}\n".format(mapfile)) + fd.write(" ".join(gtask.cmdtuple_to_list(cmd))) + fd.write("\n") + else: + fd.write("") + except OSError: + grass.fatal(f"Unable to open file '{cmd_file}'") # adjust region @@ -152,14 +153,15 @@ def read_stdin(cmd): opt = "input" if opt: - tmpfile = tempfile.NamedTemporaryFile(dir=path).name + ".txt" - fd = open(tmpfile, "w") - while 1: - line = sys.stdin.readline() - if not line: - break - fd.write(line) - fd.close() + with tempfile.NamedTemporaryFile( + dir=path, suffix=".txt", mode="w", delete=False + ) as fd: + while True: + line = sys.stdin.readline() + if not line: + break + fd.write(line) + tmpfile = fd.name cmd[1][opt] = tmpfile @@ -172,11 +174,13 @@ def read_stdin(cmd): width, height, legfile = read_env_file(os.path.join(path, "env")) if mon.startswith("wx"): - mapfile = tempfile.NamedTemporaryFile(dir=path).name if cmd[0] in {"d.barscale", "d.legend", "d.northarrow", "d.legend.vect"}: - mapfile += ".png" + suffix = ".png" else: - mapfile += ".ppm" + suffix = ".ppm" + + with tempfile.NamedTemporaryFile(dir=path, suffix=suffix) as tmpfile: + mapfile = tmpfile.name # to force rendering by wx monitors, but don't create a map file for # non-rendering modules if cmd[0] not in non_rendering_modules: diff --git a/display/d.path/d.path.html b/display/d.path/d.path.html index b98e5d90dde..922e0582f75 100644 --- a/display/d.path/d.path.html +++ b/display/d.path/d.path.html @@ -10,7 +10,7 @@

NOTE

The user needs to display a vector map before using d.path. If no graphics monitor -is open, a file map.png is generated in the current directory. +is open, a file map.png is generated in the current directory.

The 'from' and 'to' points are entered by mouse into the map displayed in the GRASS monitor, diff --git a/display/d.profile/d.profile.html b/display/d.profile/d.profile.html index 1f4e789639a..cb17088d4de 100644 --- a/display/d.profile/d.profile.html +++ b/display/d.profile/d.profile.html @@ -1,6 +1,6 @@

DESCRIPTION

-This command displays the profile for a specified transect. +d.profile displays the profile for a specified transect.

SEE ALSO

diff --git a/display/d.rast.arrow/d.rast.arrow.html b/display/d.rast.arrow/d.rast.arrow.html index 5face5ec59c..912daad0edf 100644 --- a/display/d.rast.arrow/d.rast.arrow.html +++ b/display/d.rast.arrow/d.rast.arrow.html @@ -17,7 +17,7 @@

DESCRIPTION

of that category. Cells containing null data will be marked with an "X". You can disable drawing of null data and unknown aspect values by -setting its color to "none". +setting its color to "none".

When specifying the magnitude_map option, arrow lengths denoting magnitude will be extracted from the cell values of the specified @@ -35,7 +35,7 @@

DESCRIPTION

the raster map is relatively close in scale. You can use the skip option to draw arrows every n-th cell in both directions if you are working with relatively high resolutions. It may be useful to disable the grid in -this case, which is accomplished by setting its color to "none". +this case, which is accomplished by setting its color to "none".

For GRASS and Compass type aspect maps, the cell values of the aspect map will determine the corresponding direction in 360 degrees. ANSWERS type aspect maps will be plotted in multiples of 15 degrees counterclockwise from east, and diff --git a/display/d.rast.num/d.rast.num.html b/display/d.rast.num/d.rast.num.html index 28d30b75bfc..f35c1c5abf9 100644 --- a/display/d.rast.num/d.rast.num.html +++ b/display/d.rast.num/d.rast.num.html @@ -24,7 +24,8 @@

NOTES

EXAMPLE

-A) Distance from the streams network (North Carolina sample dataset): +Distance from the streams network (North Carolina sample dataset): +
 g.region raster=streams_derived -p
 r.grow.distance input=streams_derived distance=dist_from_streams
diff --git a/display/d.rgb/d.rgb.html b/display/d.rgb/d.rgb.html
index 9b706f2b3ce..deae068cb19 100644
--- a/display/d.rgb/d.rgb.html
+++ b/display/d.rgb/d.rgb.html
@@ -1,5 +1,8 @@
 

DESCRIPTION

+d.rgb displays three user-specified raster maps as red, green, +and blue overlays in the active graphics frame. +

RGB stands for red, green, and blue. d.rgb visually combines three raster maps to form a color image. For each map, the corresponding component from the diff --git a/display/d.text/test.py b/display/d.text/test.py index 398cfecf428..dd8836938a5 100755 --- a/display/d.text/test.py +++ b/display/d.text/test.py @@ -52,7 +52,7 @@ def text(in_text): for i in range(36): font(fonts[int(i % len(fonts))]) - size((36 - i if ((i >= 9 and i <= 18) or i > 27) else i) % 9) + size((36 - i if ((9 <= i <= 18) or i > 27) else i) % 9) rotate(i * 10) color(colors[i % len(colors)]) xy( diff --git a/display/d.title/d.title.html b/display/d.title/d.title.html index 15a0d8e75cd..be02c674f60 100644 --- a/display/d.title/d.title.html +++ b/display/d.title/d.title.html @@ -25,17 +25,18 @@

EXAMPLES

For example, a user wishing to create a suitable TITLE for the Spearfish, SD soils map layer and to display this TITLE in the active display frame on the graphics monitor might type the following: -
-
d.title map=soils color=red size=5 > TITLE.file -
d.text < TITLE.file -
+ +
+d.title map=soils color=red size=5 > TITLE.file
+d.text < TITLE.file
+
+ Alternately, the user might pipe d.title output directly into d.text: -
-
-d.title map=soils color=red size=5 | -d.text -
+ +
+d.title map=soils color=red size=5 | d.text
+

A file created by d.title can be displayed with d.text. @@ -45,8 +46,10 @@

EXAMPLES

SEE ALSO

-d.font
-d.text
+ +d.font, +d.text +

AUTHOR

diff --git a/display/d.vect.thematic/d.vect.thematic.html b/display/d.vect.thematic/d.vect.thematic.html index 52ce4908d38..a40a31005b4 100644 --- a/display/d.vect.thematic/d.vect.thematic.html +++ b/display/d.vect.thematic/d.vect.thematic.html @@ -40,8 +40,8 @@

Thematic map with classes

Thematic map with calculated class breaks

-The following example uses a calculated attribute (density = -pop/area) and the standard deviation algorithm to calculate class +The following example uses a calculated attribute (density = +pop/area) and the standard deviation algorithm to calculate class breaks for 5 classes:
diff --git a/display/d.vect/d.vect.html b/display/d.vect/d.vect.html
index 4ba8494c41f..38341d0afcb 100644
--- a/display/d.vect/d.vect.html
+++ b/display/d.vect/d.vect.html
@@ -5,14 +5,14 @@ 

DESCRIPTION

NOTES

-d.vect can simply be used typing d.vect -map=vector_map. There are a large variety of optional parameters +d.vect can simply be used typing d.vect +map=vector_map. There are a large variety of optional parameters which allow the user to specify vector type, colors, data fields, SQL queries, label size and justification, etc.

When d.vect is used with where parameter on MS Windows -Command Prompt, it is important to use ˆ -carret symbol for escaping special characters < > ( ) & | , ; ". +Command Prompt, it is important to use ˆ +carret symbol for escaping special characters < > ( ) & | , ; ".

 d.vect map=vector_map where="cat ˆ> 10 AND cat ˆ< 20"
 
@@ -32,7 +32,7 @@

NOTES

Feature colors may be specified by v.colors in a form of color -table or in an attribute table column containing RRR:GGG:BBB +table or in an attribute table column containing RRR:GGG:BBB values.

A table for a vector map might look like this: diff --git a/display/displaydrivers.html b/display/displaydrivers.html index 2a8f4f71213..2caab56be77 100644 --- a/display/displaydrivers.html +++ b/display/displaydrivers.html @@ -3,7 +3,7 @@ The current command line rendering mechanism is direct rendering into a file. The driver is selected by setting -the GRASS_RENDER_IMMEDIATE variable or by +the GRASS_RENDER_IMMEDIATE variable or by running d.mon module.

diff --git a/doc/README b/doc/README index d127f9064ca..b9bc876cfd2 100644 --- a/doc/README +++ b/doc/README @@ -40,7 +40,7 @@ Database streams attributes screenshot -DIR="../trunk-commands/doc/" +DIR="../doc/" for FILE in *.png; do convert $FILE -resize 200x200 $DIR/`basename -s .png $FILE`.jpg; done; diff --git a/doc/development/rfc/legal_aspects_of_code_contributions.md b/doc/development/rfc/legal_aspects_of_code_contributions.md index 596cf301183..9607569c45e 100644 --- a/doc/development/rfc/legal_aspects_of_code_contributions.md +++ b/doc/development/rfc/legal_aspects_of_code_contributions.md @@ -66,4 +66,4 @@ source repository: Questions regarding GRASS GIS should be directed to the GRASS Development Team at the following address: -Internet: +Internet: diff --git a/doc/gui/wxpython/example/Makefile b/doc/examples/gui/wxpython/Makefile similarity index 100% rename from doc/gui/wxpython/example/Makefile rename to doc/examples/gui/wxpython/Makefile diff --git a/doc/gui/wxpython/example/README b/doc/examples/gui/wxpython/README similarity index 96% rename from doc/gui/wxpython/example/README rename to doc/examples/gui/wxpython/README index 1e619486943..c615ed57452 100644 --- a/doc/gui/wxpython/example/README +++ b/doc/examples/gui/wxpython/README @@ -20,7 +20,7 @@ or it can be launched from the console. 1. Go to GRASS root directory -2. Copy directory ./doc/gui/wxpython/example to ./gui/wxpython/example +2. Copy directory ./doc/examples/gui/wxpython to ./gui/wxpython 3. Edit ./gui/wxpython/Makefile: diff --git a/doc/gui/wxpython/example/dialogs.py b/doc/examples/gui/wxpython/dialogs.py similarity index 100% rename from doc/gui/wxpython/example/dialogs.py rename to doc/examples/gui/wxpython/dialogs.py diff --git a/doc/gui/wxpython/example/frame.py b/doc/examples/gui/wxpython/frame.py similarity index 99% rename from doc/gui/wxpython/example/frame.py rename to doc/examples/gui/wxpython/frame.py index 7f68642de9f..adba49b842d 100644 --- a/doc/gui/wxpython/example/frame.py +++ b/doc/examples/gui/wxpython/frame.py @@ -41,8 +41,8 @@ from example.dialogs import ExampleMapDialog # It is possible to call grass library functions (in C) directly via ctypes -# however this is less stable. Example is available in trunk/doc/python/, ctypes -# are used in nviz, vdigit, iclass gui modules. +# however this is less stable. Example is available in doc/examples/python/, +# ctypes are used in nviz, vdigit, iclass gui modules. # from ctypes import * # try: diff --git a/doc/examples/gui/wxpython/g.gui.example.html b/doc/examples/gui/wxpython/g.gui.example.html new file mode 100644 index 00000000000..c11ec5b2b8b --- /dev/null +++ b/doc/examples/gui/wxpython/g.gui.example.html @@ -0,0 +1,66 @@ + + +

DESCRIPTION

+ +

+The purpose of the Example Tool is to make life easier +for new wxGUI developers. It can serve as a basic template when +creating standalone GRASS GUI-based application. Example tool +can display one raster map a show information about it. +

+ +

+Following topics are covered: +

+ +
    +
  • creating standalone window
  • +
  • adding toolbars, statusbar
  • +
  • displaying raster map
  • +
  • running GRASS modules from application
  • +
  • creating dialog for element (raster, vector, ...) selection
  • +
  • using temporary region
  • +
  • access from main menu
  • +
  • writing programmer documentation
  • +
  • writing user documentation
  • +
+ +

NOTE

+ +See README to learn how to get Example Tool to work. + + + +

EXAMPLE TOOL TOOLBAR

+ +
+
  + Select raster layer
+
Select raster layer and compute statistics related to this layer.
+
+ +

SEE ALSO

+ + + wxGUI, + wxGUI components + + + + +

AUTHOR

+ +Anna Kratochvilova, +Czech Technical University in Prague, Czech Republic diff --git a/doc/gui/wxpython/example/g.gui.example.py b/doc/examples/gui/wxpython/g.gui.example.py similarity index 100% rename from doc/gui/wxpython/example/g.gui.example.py rename to doc/examples/gui/wxpython/g.gui.example.py diff --git a/doc/gui/wxpython/example/toolbars.py b/doc/examples/gui/wxpython/toolbars.py similarity index 98% rename from doc/gui/wxpython/example/toolbars.py rename to doc/examples/gui/wxpython/toolbars.py index 382672fbe64..bb7f9fb5f68 100644 --- a/doc/gui/wxpython/example/toolbars.py +++ b/doc/examples/gui/wxpython/toolbars.py @@ -43,7 +43,7 @@ def __init__(self, parent, toolSwitcher): def _toolbarData(self): """!Returns toolbar data (name, icon, handler)""" # BaseIcons are a set of often used icons. It is possible - # to reuse icons in ./trunk/gui/icons/grass or add new ones there. + # to reuse icons in gui/icons/grass or add new ones there. icons = BaseIcons return self._getToolbarData( ( diff --git a/doc/examples/notebooks/README.md b/doc/examples/notebooks/README.md new file mode 100644 index 00000000000..9aaf8e5d226 --- /dev/null +++ b/doc/examples/notebooks/README.md @@ -0,0 +1,32 @@ +# Notebooks + +## Introduction + +### Using the notebooks locally + +Clone this repository with `git clone` first. Then, locally start the Jupyter +notebook server from the command line in the `doc/examples/notebooks/` +directory containing the `*.ipynb` files with: `jupyter notebook` + +This will open a new browser tab or window with a list of the contents of the +current working directory. Clicking on one of the `*.ipynb` files will start +the notebook. + +See also the official documentation for +[The Jupyter Notebook](https://jupyter-notebook.readthedocs.io/en/latest/). + +### Introductory notebooks to GRASS GIS and Jupyter + +* GRASS GIS in Jupyter Notebook with Python and grass.jupyter: + [jupyter_example.ipynb](jupyter_example.ipynb) +* The grass.jupyter Package: [jupyter_tutorial.ipynb](jupyter_tutorial.ipynb) + +## Thematic Jupyter notebooks + +* Viewshed Analysis: [viewshed_analysis.ipynb](viewshed_analysis.ipynb) +* Spatio-Temporal Analysis with grass.jupyter: [temporal.ipynb](temporal.ipynb) +* Solar Energy Potential Analysis: + [solar_potential.ipynb](solar_potential.ipynb) +* GRASS GIS Scripting with Python: + [scripting_example.ipynb](scripting_example.ipynb) +* Hydrology with GRASS GIS: [hydrology.ipynb](hydrology.ipynb) diff --git a/doc/notebooks/hydrology.ipynb b/doc/examples/notebooks/hydrology.ipynb similarity index 100% rename from doc/notebooks/hydrology.ipynb rename to doc/examples/notebooks/hydrology.ipynb diff --git a/doc/notebooks/jupyter_example.ipynb b/doc/examples/notebooks/jupyter_example.ipynb similarity index 100% rename from doc/notebooks/jupyter_example.ipynb rename to doc/examples/notebooks/jupyter_example.ipynb diff --git a/doc/notebooks/jupyter_tutorial.ipynb b/doc/examples/notebooks/jupyter_tutorial.ipynb similarity index 100% rename from doc/notebooks/jupyter_tutorial.ipynb rename to doc/examples/notebooks/jupyter_tutorial.ipynb diff --git a/doc/notebooks/parallelization_tutorial.ipynb b/doc/examples/notebooks/parallelization_tutorial.ipynb similarity index 99% rename from doc/notebooks/parallelization_tutorial.ipynb rename to doc/examples/notebooks/parallelization_tutorial.ipynb index 4611f5d1c24..32f30d78a74 100644 --- a/doc/notebooks/parallelization_tutorial.ipynb +++ b/doc/examples/notebooks/parallelization_tutorial.ipynb @@ -331,7 +331,7 @@ " * write output maps/files with identical names (common mistake, but easy to fix)\n", " * modify computational region\n", " * modify vector attribute database\n", - " * modify MASK\n", + " * modify raster mask\n", " * use [r.reclass](https://grass.osgeo.org/grass-stable/manuals/r.reclass.html) to reclassify from the same base map\n", "\n", "The following sections provide solutions, starting with the option to execute tools in separate mapsets, which addresses all of the issues above, except r.reclass." diff --git a/doc/notebooks/scripting_example.ipynb b/doc/examples/notebooks/scripting_example.ipynb similarity index 100% rename from doc/notebooks/scripting_example.ipynb rename to doc/examples/notebooks/scripting_example.ipynb diff --git a/doc/notebooks/solar_potential.ipynb b/doc/examples/notebooks/solar_potential.ipynb similarity index 100% rename from doc/notebooks/solar_potential.ipynb rename to doc/examples/notebooks/solar_potential.ipynb diff --git a/doc/notebooks/temporal.ipynb b/doc/examples/notebooks/temporal.ipynb similarity index 100% rename from doc/notebooks/temporal.ipynb rename to doc/examples/notebooks/temporal.ipynb diff --git a/doc/notebooks/viewshed_analysis.ipynb b/doc/examples/notebooks/viewshed_analysis.ipynb similarity index 100% rename from doc/notebooks/viewshed_analysis.ipynb rename to doc/examples/notebooks/viewshed_analysis.ipynb diff --git a/doc/python/README b/doc/examples/python/README similarity index 88% rename from doc/python/README rename to doc/examples/python/README index e5f48fad99e..51c5c1ce9dc 100644 --- a/doc/python/README +++ b/doc/examples/python/README @@ -7,7 +7,7 @@ There are two ways of using Python to run GRASS commands: - Module creation using hooks into the C library functions using ctypes. - The scripts in the doc/python/examples/ directory will describe this. + The scripts in the doc/examples/python/ directory will describe this. (Ctypes is standard in Python 2.5 and newer; replaces the SWIG implementation in GRASS) diff --git a/doc/python/m.distance.py b/doc/examples/python/m.distance.py similarity index 98% rename from doc/python/m.distance.py rename to doc/examples/python/m.distance.py index 2383cbfb669..e6e26bc2842 100755 --- a/doc/python/m.distance.py +++ b/doc/examples/python/m.distance.py @@ -20,7 +20,7 @@ ############################################################################ # # Requires GRASS Python Ctypes interface -# Requires Numeric module (NumPy) from http://numpy.scipy.org/ +# Requires Numeric module (NumPy) from https://numpy.org # # %module diff --git a/doc/python/raster_example_ctypes.py b/doc/examples/python/raster_example_ctypes.py similarity index 100% rename from doc/python/raster_example_ctypes.py rename to doc/examples/python/raster_example_ctypes.py diff --git a/doc/python/script/Makefile b/doc/examples/python/script/Makefile similarity index 100% rename from doc/python/script/Makefile rename to doc/examples/python/script/Makefile diff --git a/doc/python/script/r.example.html b/doc/examples/python/script/r.example.html similarity index 100% rename from doc/python/script/r.example.html rename to doc/examples/python/script/r.example.html diff --git a/doc/python/script/r.example.py b/doc/examples/python/script/r.example.py similarity index 100% rename from doc/python/script/r.example.py rename to doc/examples/python/script/r.example.py diff --git a/doc/python/vector_example_ctypes.py b/doc/examples/python/vector_example_ctypes.py similarity index 100% rename from doc/python/vector_example_ctypes.py rename to doc/examples/python/vector_example_ctypes.py diff --git a/doc/raster/r.example/COMMENTS b/doc/examples/raster/r.example/COMMENTS similarity index 100% rename from doc/raster/r.example/COMMENTS rename to doc/examples/raster/r.example/COMMENTS diff --git a/doc/raster/r.example/Makefile b/doc/examples/raster/r.example/Makefile similarity index 89% rename from doc/raster/r.example/Makefile rename to doc/examples/raster/r.example/Makefile index 8636720f1df..f6061377f78 100644 --- a/doc/raster/r.example/Makefile +++ b/doc/examples/raster/r.example/Makefile @@ -1,6 +1,6 @@ # fix this relative to include/ # or use absolute path to the GRASS source code -MODULE_TOPDIR = ../../.. +MODULE_TOPDIR = ../../../.. PGM = r.example diff --git a/doc/raster/r.example/main.c b/doc/examples/raster/r.example/main.c similarity index 100% rename from doc/raster/r.example/main.c rename to doc/examples/raster/r.example/main.c diff --git a/doc/raster/r.example/r.example.html b/doc/examples/raster/r.example/r.example.html similarity index 100% rename from doc/raster/r.example/r.example.html rename to doc/examples/raster/r.example/r.example.html diff --git a/doc/examples/vector/TODO b/doc/examples/vector/TODO new file mode 100644 index 00000000000..d43bffca15d --- /dev/null +++ b/doc/examples/vector/TODO @@ -0,0 +1,258 @@ + GRASS 6 vector TODO + --------------------- + (Radim Blazek, May 2006) + +This document is summary of my ideas on how vector part of GRASS GIS +could be improved. + +It can be that you come to conclusion that vectors in GRASS are bad +and it is necessary to start from scratch. In that case I would +recommend to leave current library and modules intact and start the +new work in parallel (the new modules could start with w.* or v2.*). I +was thinking for example about completely new vector library based on +OGC standard, using either OGR directly or an abstraction layer and +OGR as an option (driver). That does not mean that I prefer simple +feature specification over current GRASS implementation, I am not sure +which one is better. In any case it would be pity to drop current +topological format with all its flexibility. Each approach has +advantages and disadvantages. I think that it the best to have in OS +GIS all alternatives file/database and topology/simple feature. + + + +Historical notes +---------------- +The current implementation of vectors is based on previous work which +was present in GRASS5 (the vector library and modules and DBMI +library). We started this work together with David D. Gray in autumn +2000 (IIRC) but David had to leave GRASS project soon so that I almost +all responsibility for vector development in GRASS6 and its results is +mine. + +The current design of GRASS vectors is result of these factor: ++ very limited resources for development (necessity to use existing + free code/libraries/applications whenever possible) ++ relatively little experience with development of GIS application ++ respect for certain features of GRASS5 vector model and for existing + community which is using it ++ bad experience with quality of data produced in simple feature based + applications (ArcView) + + +1. Library +---------- + +1.1 Geometry +------------ + +Keep topology and spatial index in file instead of in memory +------------------------------------------------------------ +Scalability seems to be currently the biggest problem of GRASS +vectors. The geometry of GRASS vectors (coor file) is never loaded +whole to the memory. OTOH the support structures (topology and spatial +index) are loaded to memory on runtime. It should be possible to use +files for topology and spatial index also on runtime and that way +decrease the memory occupied by running module (practically to +zero). The speed will decrease a bit but not significantly because +files are usually cached by system. + +* Update: implemented in r38385 (2009/07) by Markus Metz + +Temporary vector +---------------- +Analytical modules process data in the output vector (for example +v.overlay and v.buffer). Because many lines can be deleted (broken +lines for example) and new lines are written at the end of coor file +the output file can contain many 'dead' lines (not used space). It +would be better to do processing in a temporary vector and copy only +alive lines to the output when processing finished. That means +implement Vect_open_temporary() which will work like Vect_open_new() +but the files will be opened in temporary directory (it should not +write to $MAPSET/vector). + + +Recycle deleted lines +--------------------- +The space which was occupied by a line in coor file is lost after call +to Vect_delete_line(). A list of the free positions be kept and +Vect_write_line() should write in that free space if possible instead +of to append a new line to the end of the file. There is already empty +structure 'recycle' in 'dig_head' where the list could be implemented +(without changing 'dig_head' size, to keep binary compatibility). + +* Note: currently wxGUI vector digitizer 'undo' depends on this 'feature' + +Vect_rewrite_line +----------------- +Implement properly Vect_rewrite_line(). Currently it simply calls +Vect_delete_line() and Vect_write_line(). It should be implemented so +that if the new size of the line is the same as the old size it will +be written in the same place in the coor file where the original line +existed. + +* Note: see above + +Remove bounding box from support structures (?) +----------------------------------------------- +The vector structures (P_line, P_area, P_isle) store bounding box in +N,S,E,W,T,B (doubles). Especially in case of element type GV_POINT the +bounding box occupies a lot of space (2-3 times more than the point +itself). I am not sure if this is really good idea, it is necesssary to +valutate also how often Vect_line_box() is called and the impact of +the necessity to calculate always the box on the fly (when it is not +stored in the structure) which can be time consuming for example for +areas or long lines. + +* See also https://trac.osgeo.org/grass/ticket/542 +* Update: implemented in r46898 (2011/07) by Markus Metz + +Switching to update mode +------------------------ +It would be useful to have a possibility to switch to 'update' mode a +map which was opened by Vect_open_old/new() and similarly to switch +back to 'normal' mode. Currently it is necessary to call Vect_close() +and Vect_open_update(). + +Layer names +----------- +The layers are currently identified only by numbers but it is possible +to assign to each layer number a name. The library can read these +names but it is not possible to use the name as parameter for +modules. It is necessary to write int Vect_get_layer_by_name ( struct +Map_info *map, char *name) which will accept both names and numbers +and use this function in vector modules. This is also important for +OGR interface improvements (see below). + +* Update: implemented in r38548 (2009/07) by Martin Landa + +OGR interface +------------- +It is important to enable direct access to OGR data sources without +v.external and without necessity to store anything in files. The +problem of v.external is that topology is stored in file that means it +can be wrong when the source is opened next time. It should be +relatively easy to call Vect_build_ogr() whenever an OGR vector is +opened with level2 (topology) requested and topology will be built on +the fly. OGR vectors would be specified by virtual mapset name +'OGR'. Each OGR datasource will be equivalent to GRASS vector and each +OGR layer will be equivalent to GRASS layer (it is necessary to +implement layer names, see above). It would be for example possible to +display a shapefile or PostGIS layers directly: + + d.vect map=./shapefiles/@OGR layer=roads # display shapefile ./shapefiles/roads.shp + d.vect map=PG:dbname=test@OGR layer=roads # display table roads from database test + +* Update: in progress, + see https://trac.osgeo.org/grass/wiki/Grass7/VectorLib/OGRInterface#DirectOGRreadaccess + +Simple feature API and sequential reading +----------------------------------------- +Most GRASS modules are currently using random access to the data which +reflects GRASS format. This works well with GRASS data but it can +become very slow or even impossible with OGR data sources because some +OGR drivers don't support random access or random access is very +slow. Because conversion from topological format to simple feature is +very simple and sequential reading of GRASS vectors is not problem it +would be desirable to implement in GRASS vector library 'simple +feature' API to GRASS vectors and map it directly to OGR API in case +of OGR data sources. Then many GRASS modules can be modified to use +sequentil reading and simple feature API and that will make more +efficient processing of data directly read from OGR data sources. + + +1.2 Attributes +-------------- + +In general I found the use of true RDBMS for attributes as a +problem. The data are stored in two distinct places (vector files + +database) and it makes it difficult to keep them consistent and manage +(move, backup). Another problem is random access to the data in RDBMS +from an application which is terribly slow (due to communication with +server). RDBMS is not bad, bad is the combination of files and +RDBMS. I think that either everything must be stored in RDBMS +(PostGIS) or nothing. Eric G. Miller (IIRC) was right when he said +that data are 'too distant' when RDBMS is used with geometry in file. + +I think that more work should be done on the drivers which are using +embedded databases stored in files (SQLite,MySQL,DBF) with scope to +reach similar functionality (functions, queries) which are in true +RDBMS without penalty of communication with server. It should be also +considered the possibility to change the default location of database +files to vector directory ($MAPSET/vector/test). That means to keep +all the data of one vector in a single directory. It is already +possible but it is not the default settings, for example: + + db.connect driver=dbf database='$GISDBASE/$LOCATION_NAME/$MAPSET/vector/$MAP/' + db.connect driver=sqlite database='$GISDBASE/$LOCATION_NAME/$MAPSET/vector/$MAP/db.sqlite' + db.connect driver=mesql database='$GISDBASE/$LOCATION_NAME/$MAPSET/vector/$MAP/' + +Implement insert/update cursors +------------------------------- +GRASS modules are currently sending all data to database drivers as +individual SQL insert/update statements. This makes the update process +slow (cunstructing and parsing queries) and number precision can be +lost. The solution is to implement db_open_insert/update_cursor() and +db_insert/update() in database drivers and use these functions in +modules. The drivers should then use precompiled statements +(e.g. SQLite) or they could update the database directly (DBF). + +Note that it is not necessary to implement these functions in all +drivers at the same time. You can implement lib/db/stubs functions +which will create SQL statement and send it to db_execute() which is +implemented in all drivers until the functions are properly +implemented in all drivers. + +SQLite driver +------------- +Current implementation is very slow with large updates/inserts. I +think that it is because all statements are parsed and it should be +possible to improve by insert/update cursors (see above). + +DBF driver +---------- +Add on the fly index for select/update. + +Implement db_copy_table() in drivers +---------------------------------- +db_copy_table() is implement in client library and it always reads and +writes all the data which is slow. It would be better to send this +request to the driver (if possible, i.e. input and output driver are +the same) which can copy tables much faster. For example true RDBMS +can use 'create table new as select * from old' and DBF driver can +simply copy files. + +Load drivers as dynamic libraries +--------------------------------- +Database drivers are implemented as executables which communicate with +modules via pipes. This implementation creates some problems with +portability (especially on Windows) and it makes communication slow (I +am not sure how much). It would be probably desirable to implement +drivers as loadable modules (dlopen() and equivalents). + + +2. Modules +---------- + +v.overlay +--------- +Select only relevant features which will be written to the output if +'and,not,nor' operators are used. An inspiration is available in +v.select. + +v.pack/v.unpack +--------------- +Write it. New modules to pack/unpack a vector to/from single file +(probably tar). I am not sure about format. Originally I was thinking +about ASCII+DBF as it can be read also without GRASS but ASCII and DBF +can lose precision and DBF has other limitations. It would be +probably better to use copy of 'coor' file and attributes written to +SQLite database. + +Update: see + https://trac.osgeo.org/grass/browser/grass-addons/grass7/vector/v.pack + and + https://trac.osgeo.org/grass/browser/grass-addons/grass7/vector/v.unpack + by Luca Delucchi + +1/2009: Other suggestions moved to + https://trac.osgeo.org/grass/ diff --git a/doc/vector/grass51atts.fig b/doc/examples/vector/grass51atts.fig similarity index 100% rename from doc/vector/grass51atts.fig rename to doc/examples/vector/grass51atts.fig diff --git a/doc/vector/grass51atts.png b/doc/examples/vector/grass51atts.png similarity index 100% rename from doc/vector/grass51atts.png rename to doc/examples/vector/grass51atts.png diff --git a/doc/vector/grass51concept.fig b/doc/examples/vector/grass51concept.fig similarity index 100% rename from doc/vector/grass51concept.fig rename to doc/examples/vector/grass51concept.fig diff --git a/doc/vector/grass51concept.png b/doc/examples/vector/grass51concept.png similarity index 100% rename from doc/vector/grass51concept.png rename to doc/examples/vector/grass51concept.png diff --git a/doc/vector/v.example/Makefile b/doc/examples/vector/v.example/Makefile similarity index 91% rename from doc/vector/v.example/Makefile rename to doc/examples/vector/v.example/Makefile index 9516d79b855..0197d49271d 100644 --- a/doc/vector/v.example/Makefile +++ b/doc/examples/vector/v.example/Makefile @@ -1,6 +1,6 @@ # fix this relative to include/ # or use absolute path to the GRASS source code -MODULE_TOPDIR = ../../.. +MODULE_TOPDIR = ../../../.. PGM = v.example diff --git a/doc/vector/v.example/main.c b/doc/examples/vector/v.example/main.c similarity index 100% rename from doc/vector/v.example/main.c rename to doc/examples/vector/v.example/main.c diff --git a/doc/vector/v.example/v.example.html b/doc/examples/vector/v.example/v.example.html similarity index 100% rename from doc/vector/v.example/v.example.html rename to doc/examples/vector/v.example/v.example.html diff --git a/doc/notebooks/README.md b/doc/notebooks/README.md deleted file mode 100644 index 4a200b0f4f6..00000000000 --- a/doc/notebooks/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# Notebooks - -## Introduction - -### Using the notebooks locally - -Clone this repository with `git clone` first. Then, locally start the Jupyter -notebook server from the command line in the `doc/notebooks/` directory containing -the `*.ipynb` files with: -`jupyter notebook` - -This will open a new browser tab or window with a list of the contents of the current -working directory. Clicking on one of the `*.ipynb` files will start the notebook. - -See also the official documentation for [The Jupyter Notebook](https://jupyter-notebook.readthedocs.io/en/latest/). - -### Introductory notebooks to GRASS GIS and Jupyter - -* GRASS GIS in Jupyter Notebook with Python and grass.jupyter: [jupyter_example.ipynb](jupyter_example.ipynb) -* The grass.jupyter Package: [jupyter_tutorial.ipynb](jupyter_tutorial.ipynb) - -## Thematic Jupyter notebooks - -* Viewshed Analysis: [viewshed_analysis.ipynb](viewshed_analysis.ipynb) -* Spatio-Temporal Analysis with grass.jupyter: [temporal.ipynb](temporal.ipynb) -* Solar Energy Potential Analysis: [solar_potential.ipynb](solar_potential.ipynb) -* GRASS GIS Scripting with Python: [scripting_example.ipynb](scripting_example.ipynb) -* Hydrology with GRASS GIS: [hydrology.ipynb](hydrology.ipynb) diff --git a/doc/projectionintro.html b/doc/projectionintro.html index 33bc964e3d7..c51c9efc761 100644 --- a/doc/projectionintro.html +++ b/doc/projectionintro.html @@ -63,8 +63,8 @@

References

  • ASPRS Grids and Datum
  • Projections Transform List (PROJ)
  • Coordinate operations by PROJ (projections, conversions, transformations, pipeline operator)
  • -
  • MapRef - The Collection of Map Projections and Reference Systems for Europe
  • -
  • Information and Service System for European Coordinate Reference Systems - CRS
  • +
  • MapRef - The Collection of Map Projections and Reference Systems for Europe
  • +
  • Information and Service System for European Coordinate Reference Systems - CRS
  • See also

    diff --git a/docker/alpine/Dockerfile b/docker/alpine/Dockerfile index f8b2dace916..a552a744a8a 100644 --- a/docker/alpine/Dockerfile +++ b/docker/alpine/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.21@sha256:21dc6063fd678b478f57c0e13f47560d0ea4eeba26dfc947b2a4f81f686b9f45 as common +FROM alpine:3.21@sha256:56fa17d2a7e7f168a043a2712e63aed1f8543aeafdcee47c58dcffe38ed51099 as common # Based on: # https://github.com/mundialis/docker-grass-gis/blob/master/Dockerfile diff --git a/docker/ubuntu/Dockerfile b/docker/ubuntu/Dockerfile index 6b18a841ad4..a1be969f46e 100644 --- a/docker/ubuntu/Dockerfile +++ b/docker/ubuntu/Dockerfile @@ -1,4 +1,4 @@ -# syntax=docker/dockerfile:1.12@sha256:db1ff77fb637a5955317c7a3a62540196396d565f3dd5742e76dddbb6d75c4c5 +# syntax=docker/dockerfile:1.13@sha256:426b85b823c113372f766a963f68cfd9cd4878e1bcc0fda58779127ee98a28eb # Note: This file must be kept in sync in ./Dockerfile and ./docker/ubuntu/Dockerfile. # Changes to this file must be copied over to the other file. diff --git a/flake.lock b/flake.lock index ac3afa5fcf9..94f4784f626 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1733312601, - "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=", + "lastModified": 1736143030, + "narHash": "sha256-+hu54pAoLDEZT9pjHlqL9DNzWz0NbUn8NEAHP7PQPzU=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9", + "rev": "b905f6fc23a9051a6e1b741e1438dbfc0634c6de", "type": "github" }, "original": { @@ -19,11 +19,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1733376361, - "narHash": "sha256-aLJxoTDDSqB+/3orsulE6/qdlX6MzDLIITLZqdgMpqo=", + "lastModified": 1736693123, + "narHash": "sha256-9lIfXCaBPwUA7FnfDnoH4gxxdOvXG78k6UlUw0+ZDxc=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "929116e316068c7318c54eb4d827f7d9756d5e9c", + "rev": "2fdec2c2e68b7b7845d1ea4e0894c63143e3261b", "type": "github" }, "original": { @@ -35,14 +35,14 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1733096140, - "narHash": "sha256-1qRH7uAUsyQI7R1Uwl4T+XvdNv778H0Nb5njNrqvylY=", + "lastModified": 1735774519, + "narHash": "sha256-CewEm1o2eVAnoqb6Ml+Qi9Gg/EfNAxbRx1lANGVyoLI=", "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/5487e69da40cbd611ab2cadee0b4637225f7cfae.tar.gz" + "url": "https://github.com/NixOS/nixpkgs/archive/e9b51731911566bbf7e4895475a87fe06961de0b.tar.gz" }, "original": { "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/5487e69da40cbd611ab2cadee0b4637225f7cfae.tar.gz" + "url": "https://github.com/NixOS/nixpkgs/archive/e9b51731911566bbf7e4895475a87fe06961de0b.tar.gz" } }, "root": { diff --git a/general/g.filename/g.filename.html b/general/g.filename/g.filename.html index 0f6c89357e3..5462f5cb48e 100644 --- a/general/g.filename/g.filename.html +++ b/general/g.filename/g.filename.html @@ -6,7 +6,7 @@

    DESCRIPTION

    The list of element names to search for is not fixed; any subdirectory of the mapset directory is a valid element name.

    However, the user can find the list of standard GRASS GIS element names in -the file $GISBASE/etc/element_list. This is the file which +the file $GISBASE/etc/element_list. This is the file which g.remove/g.rename/g.copy use to determine which files need to be deleted/renamed/copied for a given entity type. diff --git a/general/g.findfile/g.findfile.html b/general/g.findfile/g.findfile.html index 140495691d9..5746759fbd9 100644 --- a/general/g.findfile/g.findfile.html +++ b/general/g.findfile/g.findfile.html @@ -7,7 +7,7 @@

    DESCRIPTION

    The list of element names to search for is not fixed; any subdirectory of the mapset directory is a valid element name.

    However, the user can find the list of standard GRASS element -names in the file $GISBASE/etc/element_list. This is the file +names in the file $GISBASE/etc/element_list. This is the file which g.remove, g.rename and g.copy use to determine which files need to be deleted/renamed/copied for a given entity type. diff --git a/general/g.gisenv/g.gisenv.html b/general/g.gisenv/g.gisenv.html index 4eb2a88ed13..bf95770390c 100644 --- a/general/g.gisenv/g.gisenv.html +++ b/general/g.gisenv/g.gisenv.html @@ -1,5 +1,8 @@

    DESCRIPTION

    +g.gisenv outputs and modifies the user's current GRASS GIS +variable settings. + When a user runs GRASS, certain variables are set specifying the GRASS data base, project, mapset, peripheral device drivers, etc., being used in the current GRASS session. These variable name settings are @@ -46,7 +49,7 @@

    OPTIONS

    "project" directory itself contains subdirectories called "mapsets"; each "mapset" stores "data base elements" - the directories (e.g., -the cell, cellhd, vector, etc., directories) +the cell, cellhd, vector, etc., directories) in which GRASS data files are actually stored.
    LOCATION_NAME @@ -118,7 +121,7 @@

    OPTIONS

    The full path to the current mapset is determined from GISDBASE, LOCATION_NAME, MAPSET variables, in the example -above: /opt/grassdata/spearfish/PERMANENT. The full path can +above: /opt/grassdata/spearfish/PERMANENT. The full path can be printed using g.gisenv by providing multiple variables:
    @@ -146,7 +149,7 @@ 

    NOTES

    By default the GRASS variables are stored in gisrc file (defined by environmental variable GISRC). If store=mapset is given then the -variables are stored in <gisdbase>/<project>/<mapset>/VAR +variables are stored in <gisdbase>/<project>/<mapset>/VAR after the current GRASS session is closed.

    EXAMPLES

    diff --git a/general/g.gisenv/main.c b/general/g.gisenv/main.c index 73dd9be6719..e30b2772983 100644 --- a/general/g.gisenv/main.c +++ b/general/g.gisenv/main.c @@ -198,6 +198,7 @@ char *parse_variable(const char *v_name, char **value) { char *u_name; /* uppercase variable name */ char *name, *ptr; + name = NULL; name = G_store(v_name); if (value) @@ -207,12 +208,14 @@ char *parse_variable(const char *v_name, char **value) if (ptr != NULL) { *ptr = '\0'; if (value) - *value = ptr + 1; + *value = G_store(ptr + 1); } /* Allow unset without '=' sign */ if (value) { - if (*value != NULL && **value == '\0') + if (*value != NULL && **value == '\0') { + G_free(*value); *value = NULL; + } } if (strlen(name) < 1) G_fatal_error(_("GRASS variable not defined")); @@ -224,6 +227,7 @@ char *parse_variable(const char *v_name, char **value) G_verbose_message(_("GRASS variable must be uppercase. Using '%s'."), u_name); } + G_free(name); return u_name; } diff --git a/general/g.gui/g.gui.html b/general/g.gui/g.gui.html index 4c8b162b6b1..3988edb68e3 100644 --- a/general/g.gui/g.gui.html +++ b/general/g.gui/g.gui.html @@ -11,18 +11,18 @@

    DESCRIPTION

    NOTES

    -If the -d update flag is given or the GRASS_GUI +If the -d update flag is given or the GRASS_GUI environmental variable is unset, then -the GRASS internal variable GUI is permanently changed and +the GRASS internal variable GUI is permanently changed and the selected ui will be used as the default UI from then on.

    All GRASS internal variables (see g.gisenv) are stored in the user's home directory in a hidden file called -$HOME/.grass8/rc on Unix-based operating systems -and %APPDATA%\GRASS8\rc on MS Windows. Note that these GRASS +$HOME/.grass8/rc on Unix-based operating systems +and %APPDATA%\GRASS8\rc on MS Windows. Note that these GRASS internal variables are not the shell environment variables and the -rc file is not a classic UNIX run command file, it just +rc file is not a classic UNIX run command file, it just contains persistent GRASS variables.

    EXAMPLES

    diff --git a/general/g.list/g.list.html b/general/g.list/g.list.html index b99b46cde61..cc646c6ad7b 100644 --- a/general/g.list/g.list.html +++ b/general/g.list/g.list.html @@ -42,7 +42,7 @@

    Mapset search path

    If mapset is not specified, then g.list searches for data files in the mapsets that are included in the search path (defined by g.mapsets). -See g.mapsets -p. +See g.mapsets -p.
     g.list rast -p
    @@ -66,7 +66,7 @@ 

    Mapset search path

    Similarly, mapset=* (one asterisk) prints data files from all available mapsets also including those that are not listed in the -current search path (see g.mapsets -l). +current search path (see g.mapsets -l).
     g.list rast mapset=* -p
    @@ -177,7 +177,7 @@ 

    Maps whose region overlaps with a saved region

    g.list type=rast,vect region=*
    -Note that, without region=*, g.list type=rast,vect simply +Note that, without region=*, g.list type=rast,vect simply lists all available raster and vector maps from the current search path regardless of their region. diff --git a/general/g.mapset/g.mapset.html b/general/g.mapset/g.mapset.html index d8838b59a26..dd32c1d00d8 100644 --- a/general/g.mapset/g.mapset.html +++ b/general/g.mapset/g.mapset.html @@ -4,7 +4,7 @@

    DESCRIPTION

    as location), or GISDBASE (directory with one or more projects).

    -With g.mapset, the shell history (i.e. .bash_history file +With g.mapset, the shell history (i.e. .bash_history file of the initial project will be used to record the command history.

    NOTES

    diff --git a/general/g.mapsets/g.mapsets.html b/general/g.mapsets/g.mapsets.html index 3fe1a52df73..8fa75baaf2f 100644 --- a/general/g.mapsets/g.mapsets.html +++ b/general/g.mapsets/g.mapsets.html @@ -1,5 +1,8 @@

    DESCRIPTION

    +g.mapsets modifies/prints the user's current mapset search +path. + For basic information about GRASS mapset, project and data base refer to GRASS Quickstart. @@ -23,7 +26,7 @@

    DESCRIPTION

    their current mapset. Although the user can also access (i.e., use) data that are stored under other mapsets in the same GRASS project using the -mapname@mapsetname notation or mapset search path, the user +mapname@mapsetname notation or mapset search path, the user can only make permanent changes (create or modify data) located in the current mapset. The user's mapset search path lists the order in which other mapsets in @@ -33,7 +36,7 @@

    DESCRIPTION

    using the g.mapsets command. This program allows the user to use other's relevant map data without altering the original data layer, and without taking up disk space with a copy of the original -map. The mapname@mapsetname notation may be used irrespective +map. The mapname@mapsetname notation may be used irrespective of the mapset search path, i.e., any map found in another mapset with sufficient g.access privileges may be called in such a manner. @@ -60,8 +63,8 @@

    DESCRIPTION

    g.copy raster=soils@PERMANENT,my_soils
    -ensures that a new file named my_soils is to be a copy of -the file soils from the mapset PERMANENT. +ensures that a new file named my_soils is to be a copy of +the file soils from the mapset PERMANENT.

    In each project there is the special mapset PERMANENT included diff --git a/general/g.message/g.message.html b/general/g.message/g.message.html index 07bf643c97b..67587eaf002 100644 --- a/general/g.message/g.message.html +++ b/general/g.message/g.message.html @@ -1,10 +1,13 @@

    DESCRIPTION

    -

    This program is to be used in Shell/Perl/Python scripts, so the author does not -need to use the echo program. The advantage of g.message is +g.message prints a message, warning, progress info, or fatal error +in the GRASS GIS way. + +This program is to be used in Shell/Perl/Python scripts, so the author does not +need to use the echo program. The advantage of g.message is that it formats messages just like other GRASS modules do and that its -functionality is influenced by the GRASS_VERBOSE and -GRASS_MESSAGE_FORMAT environment variables. +functionality is influenced by the GRASS_VERBOSE and +GRASS_MESSAGE_FORMAT environment variables.

    The program can be used for standard informative messages as well as warnings @@ -14,7 +17,7 @@

    DESCRIPTION

    NOTES

    -Messages containing "=" must use the full message= syntax so +Messages containing "=" must use the full message= syntax so the parser doesn't get confused.

    If you want a long message (multi-line) to be dealt with as a single @@ -31,18 +34,18 @@

    NOTES

    It's advisable to single quote the messages that are to be printed literally. It prevents a number of characters (most notably, space and the dollar sign -'$') from being treated specifically by the shell. +'$') from being treated specifically by the shell.

    When it is necessary to include, for example, a variable's value as part of the message, the double quotes may be used, which do not deprive the dollar sign of its special variable-expansion powers.

    While it is known that the interactive Bash instances may treat the -exclamation mark '!' character specifically (making single quoting +exclamation mark '!' character specifically (making single quoting of it necessary), it shouldn't be the case for the non-interactive instances of Bash. Nonetheless, to avoid context-based confusion later on you are encouraged to single-quote messages that do not require -$VARIABLE expansion. +$VARIABLE expansion.

    Usage in Python scripts

    @@ -51,13 +54,13 @@

    Usage in Python scripts

    for g.message.
      -
    • debug() for g.message -d
    • -
    • error() for g.message -e
    • -
    • fatal() for g.message -e + exit()
    • -
    • info() for g.message -i
    • -
    • message() for g.message
    • -
    • verbose() for g.message -v
    • -
    • warning() for g.message -w
    • +
    • debug() for g.message -d
    • +
    • error() for g.message -e
    • +
    • fatal() for g.message -e + exit()
    • +
    • info() for g.message -i
    • +
    • message() for g.message
    • +
    • verbose() for g.message -v
    • +
    • warning() for g.message -w

    @@ -78,7 +81,7 @@

    Usage in Python scripts

    VERBOSITY LEVELS

    -Controlled by the "GRASS_VERBOSE" environment variable. Typically this +Controlled by the "GRASS_VERBOSE" environment variable. Typically this is set using the --quiet or --verbose command line options.
    • 0 - only errors and warnings are printed
    • @@ -88,7 +91,7 @@

      VERBOSITY LEVELS

    DEBUG LEVELS

    -Controlled by the "DEBUG" GRASS gisenv variable (set with +Controlled by the "DEBUG" GRASS gisenv variable (set with g.gisenv).
    Recommended levels: diff --git a/general/g.mkfontcap/g.mkfontcap.html b/general/g.mkfontcap/g.mkfontcap.html index b1ddeb0663b..2854d808a3b 100644 --- a/general/g.mkfontcap/g.mkfontcap.html +++ b/general/g.mkfontcap/g.mkfontcap.html @@ -2,7 +2,7 @@

    DESCRIPTION

    g.mkfontcap is a utility to generate a GRASS font configuration file ("fontcap") containing details of the fonts available on the current system. -If Freetype is not installed, +If Freetype is not installed, the font list will be limited to the set of Hershey stroke fonts supplied with GRASS. With Freetype enabled however, the module will recursively scan all files within a predefined hierarchy to find Freetype-compatible scalable @@ -24,10 +24,10 @@

    DESCRIPTION

    of the string, if enclosed in ${xxx} syntax (see examples above).

    The module will normally write to the standard fontcap file location, -$GISBASE/etc/fontcap. If the environment variable -GRASS_FONT_CAP is set, the output will instead be written +$GISBASE/etc/fontcap. If the environment variable +GRASS_FONT_CAP is set, the output will instead be written to the file specified by that variable. This is useful if you don't have -permission to modify $GISBASE/etc/fontcap: in this case you can +permission to modify $GISBASE/etc/fontcap: in this case you can use e.g.

    diff --git a/general/g.parser/g.parser.html b/general/g.parser/g.parser.html
    index 224049b46bb..c438a675454 100644
    --- a/general/g.parser/g.parser.html
    +++ b/general/g.parser/g.parser.html
    @@ -75,8 +75,8 @@ 

    OPTIONS

    # %end
    -With {NULL} it is possible to suppress a predefined description -or label. +With {NULL} it is possible to suppress a predefined description +or label.

    The parsers allows using predefined standardized options and @@ -150,9 +150,9 @@

    NOTES

    -A "guisection" field may be added to each option and flag to +A "guisection" field may be added to each option and flag to specify that the options should appear in multiple tabs in the -auto-generated GUI. Any options without a guisection field +auto-generated GUI. Any options without a guisection field go into the "Required" or "Options" tab. For example:

     # % guisection: tabname
    @@ -160,23 +160,23 @@ 

    NOTES

    would put that option in a tab named tabname.

    -A "key_desc" field may be added to each option to specify the text that +A "key_desc" field may be added to each option to specify the text that appears in the module's usage help section. For example:

     # % key_desc: filename
     
    added to an input option would create the usage summary -[input=filename]. +[input=filename].

    If a script is run with --o, the parser will -set GRASS_OVERWRITE=1, which has the same effect as passing +set GRASS_OVERWRITE=1, which has the same effect as passing --o to every module which is run from the script. Similarly, passing ---q or --v will set GRASS_VERBOSE to 0 or 3 respectively, +--q or --v will set GRASS_VERBOSE to 0 or 3 respectively, which has the same effect as passing --q or --v to every module which is run from the script. Rather than checking whether --o, --q or --v -were used, you should be checking GRASS_OVERWRITE and/or -GRASS_VERBOSE instead. If those variables are set, the script +were used, you should be checking GRASS_OVERWRITE and/or +GRASS_VERBOSE instead. If those variables are set, the script should behave the same way regardless of whether they were set by --o, --q or --v being passed to the script or set by other means. @@ -191,13 +191,13 @@

    Conditional parameters

    Marking an option as "required" will result in the parser raising a fatal error if the option is not given, with one exception: if a flag -has the suppress_required option, and that flag is given, all +has the suppress_required option, and that flag is given, all requirements are ignored. This feature is intended for flags which abandon "normal operation" for the module; e.g. r.in.gdal's -f flag (list supported formats) uses it.
    But in general, an option cannot be marked as required if it is -optional except for the special case of a suppress_required flag. +optional except for the special case of a suppress_required flag. The parser has the ability to specify option relationships.

    @@ -219,15 +219,15 @@

    Conditional parameters

    The available rule types are:
      -
    • exclusive: at most one of the options may be given
    • -
    • required: at least one of the options must be given
    • -
    • requires: if the first option is given, at least one of the +
    • exclusive: at most one of the options may be given
    • +
    • required: at least one of the options must be given
    • +
    • requires: if the first option is given, at least one of the subsequent options must also be given
    • -
    • requires_all: if the first option is given, all of the +
    • requires_all: if the first option is given, all of the subsequent options must also be given
    • -
    • excludes: if the first option is given, none of the +
    • excludes: if the first option is given, none of the subsequent options may be given
    • -
    • collective: all or nothing; if any option is given, all +
    • collective: all or nothing; if any option is given, all must be given
    @@ -305,7 +305,7 @@

    reStructuredText

    reStructuredText is sometimes abbreviated as reST, ReST, or RST. -The commonly used file extension is .rst. +The commonly used file extension is .rst. Don't be confused with Representational State Transfer (REST) technology.

    TRANSLATION

    @@ -334,7 +334,7 @@

    EXAMPLES

    To run properly, the script needs to be copied into a directory listed -in $GRASS_ADDON_PATH environmental variable with the +in $GRASS_ADDON_PATH environmental variable with the executable flag being set.

    diff --git a/general/g.proj/g.proj.html b/general/g.proj/g.proj.html index b7f4f185af3..89a44c93622 100644 --- a/general/g.proj/g.proj.html +++ b/general/g.proj/g.proj.html @@ -11,9 +11,9 @@

    DESCRIPTION

    If compiled without OGR present, the functionality is limited to:
      -
    • Reporting the CRS information for the current project -(previously called location), -either in conventional GRASS (-p flag) or PROJ (-j flag) format
    • +
    • Reporting the CRS information for the current project (previously called +location), either in conventional GRASS (-p flag) or PROJ (-j +flag) format
    • Changing the datum, or reporting and modifying the datum transformation parameters, for the current project
    @@ -32,25 +32,25 @@

    DESCRIPTION

    read from this file. If the file is not georeferenced or cannot be read, XY (unprojected) will be used.
    -
    wkt=filename or -
    +
    wkt=filename or -
    The file filename should contain a CRS description in WKT -format with or without line-breaks (e.g. a '.prj' file). If - is given +format with or without line-breaks (e.g. a '.prj' file). If - is given for the filename, the WKT description will be read from stdin rather than a file.
    -
    proj4=description or -
    +
    proj4=description or -
    description should be a CRS description in PROJ format, enclosed in -quotation marks if there are any spaces. If - is given for +quotation marks if there are any spaces. If - is given for description, the PROJ description will be read from stdin rather than as a directly-supplied command-line parameter.
    epsg=number
    number should correspond to the index number of a valid -co-ordinate system in the EPSG +co-ordinate system in the EPSG database. EPSG code support is based upon a local copy of the GDAL CSV co-ordinate system and datum information files, stored in the directory -$GISBASE/etc/proj/ogr_csv. These can be updated if necessary +$GISBASE/etc/proj/ogr_csv. These can be updated if necessary to support future revisions of the EPSG database.
    @@ -65,22 +65,22 @@

    DESCRIPTION

    datumtrans parameter should in general always be used in conjunction with datum. -

    The -p, -j, -w, etc. flags are all functional when importing CRS -information from an external source, meaning that g.proj can be -used to convert between representations of the information. It is +

    The -p, -j, -w, etc. flags are all functional when +importing CRS information from an external source, meaning that g.proj +can be used to convert between representations of the information. It is not required that either the input or output be in GRASS format. -

    In addition however, if the -c flag is specified, g.proj will +

    In addition however, if the -c flag is specified, g.proj will create new GRASS CRS files (PROJ_INFO, PROJ_UNITS, WIND and DEFAULT_WIND) based on the imported information. If the project -parameter is specified in addition to -c, then a new project will be created. +parameter is specified in addition to -c, then a new project will be created. Otherwise the CRS information files in the current project will be overwritten. The program will not warn before doing this. -

    The final mode of operation of g.proj is to report on the datum +

    The final mode of operation of g.proj is to report on the datum information and datum transformation parameters associated with the -co-ordinate system. The -d flag will report a human-readable summary of +co-ordinate system. The -d flag will report a human-readable summary of this.

    NOTES

    @@ -88,17 +88,21 @@

    NOTES

    If the input co-ordinate system contains a datum name but no transformation parameters, and there is more than one suitable parameter set available (according to the files datum.table and datumtransform.table in -$GISBASE/etc/proj), g.proj will check the value of -the datumtrans option and act according to the following:
    --1: List available parameter sets in a GUI-parsable (but also -human-readable) format and exit.
    -0 (default): Continue without specifying parameters - if +$GISBASE/etc/proj), g.proj will check the value of +the datumtrans option and act according to the following: + +

      +
    • -1: List available parameter sets in a GUI-parsable (but also +human-readable) format and exit.
    • +
    • 0 (default): Continue without specifying parameters - if used when creating a project, other GRASS modules will use the "default" -(likely non-optimum) parameters for this datum if necessary in the future.
      -Any other number less than or equal to the number of parameter sets +(likely non-optimum) parameters for this datum if necessary in the future.
    • +
    • Any other number less than or equal to the number of parameter sets available for this datum: Choose this parameter set and add it to the -co-ordinate system description.
      -If the -t flag is specified, the module will attempt to change the +co-ordinate system description.
    • +
    + +If the -t flag is specified, the module will attempt to change the datum transformation parameters using one of the above two methods even if a valid parameter set is already specified in the input co-ordinate system. This can be useful to change the datum information @@ -167,10 +171,10 @@

    Create new project

    Create a new project with the coordinate system referred to by EPSG code -900913 (Google Mercator Projection)
    +3857 (Pseudo-Mercator Projection)

    -g.proj -c epsg=900913 project=google
    +g.proj -c epsg=3857 project=google
     

    Create a new project with the coordinate system referred to by EPSG code @@ -229,7 +233,7 @@

    REFERENCES

    Further reading

    diff --git a/general/g.region/g.region.html b/general/g.region/g.region.html index 85d75a058df..2eb4e58aa08 100644 --- a/general/g.region/g.region.html +++ b/general/g.region/g.region.html @@ -4,7 +4,7 @@

    DESCRIPTION

    settings of the current geographic region. These regional boundaries can be set by the user directly and/or set from a region definition file (stored under the -windows directory in the user's current +windows directory in the user's current mapset). The user can create, modify, and store as many geographic region definitions as desired for any given mapset. However, only one of these geographic region @@ -50,8 +50,8 @@

    DEFINITIONS

    Each GRASS project (previously called location) has a fixed geographic region, called the default geographic region -(stored in the region file DEFAULT_WIND under -the special mapset PERMANENT), that defines the +(stored in the region file DEFAULT_WIND under +the special mapset PERMANENT), that defines the extent of the data base. While this provides a starting point for defining new geographic regions, user-defined geographic regions need not fall within this geographic @@ -73,14 +73,14 @@

    DEFINITIONS

    Each GRASS MAPSET may contain any number of pre-defined, and named, geographic regions. These region definitions are stored in the user's current mapset -location under the windows directory (also +location under the windows directory (also referred to as the user's saved region definitions). Any of these pre-defined geographic regions may be selected, by name, to become the current geographic region. Users may also access saved region definitions stored under other mapsets in the current project, if these mapsets are included in the user's mapset search -path or the '@' operator is used (region_name@mapset). +path or the '@' operator is used (region_name@mapset).

    NOTES

    @@ -118,7 +118,7 @@

    NOTES

    The -g flag prints the current region settings in shell script style. This format can be given back to g.region on its command line. This may also be used to save region settings as shell environment variables -with the UNIX eval command, "eval `g.region -g`". +with the UNIX eval command, "eval `g.region -g`".

    With -u flag current region is not updated even if one or more options for changing region is used (res=, raster=, etc). @@ -163,9 +163,9 @@

    EXAMPLES

    Printing extent and raster resolution in 2D and 3D

    -
    +
    g.region -p - +
    This will print the current region in the format: @@ -185,9 +185,9 @@

    Printing extent and raster resolution in 2D and 3D

    -

    +
    g.region -p3 - +
    This will print the current region and the 3D region (used for voxels) in the format: @@ -216,9 +216,9 @@

    Printing extent and raster resolution in 2D and 3D

    -

    +
    g.region -g - +
    The -g option prints the region in the following script style (key=value) format: @@ -235,9 +235,9 @@

    Printing extent and raster resolution in 2D and 3D

    -

    +
    g.region -bg - +
    The -bg option prints the region in the following script style (key=value) format plus the @@ -259,9 +259,9 @@

    Printing extent and raster resolution in 2D and 3D

    -

    +
    g.region -l - +
    The -l option prints the region in the following format: @@ -278,9 +278,9 @@

    Printing extent and raster resolution in 2D and 3D

    -

    +
    g.region -pm - +
    This will print the current region in the format (latitude-longitude project): @@ -303,36 +303,36 @@

    Printing extent and raster resolution in 2D and 3D

    Changing extent and raster resolution using values

    -
    +
    g.region n=7360100 e=699000 - +
    will reset the northing and easting for the current region, but leave the south edge, west edge, and the region cell resolutions unchanged.

    -

    +
    g.region n=51:36:05N e=10:10:05E s=51:29:55N w=9:59:55E res=0:00:01 - +
    will reset the northing, easting, southing, westing and resolution for the current region, here in DMS latitude-longitude style (decimal degrees and degrees with decimal minutes can also be used).

    -

    +
    g.region -dp s=698000 - +
    will set the current region from the default region for the GRASS project, reset the south edge to 698000, and then print the result.

    -

    +
    g.region n=n+1000 w=w-500 - +
    The n=value may also be specified as a function of its current value: n=n+value @@ -344,9 +344,9 @@

    Changing extent and raster resolution using values

    decreased by 500 units.

    -

    +
    g.region n=s+1000 e=w+1000 - +
    This form allows the user to set the region boundary values relative to one another. Here, the northern @@ -364,19 +364,18 @@

    Changing extent and raster resolution using values

    Changing extent and raster resolution using maps

    -
    +
    g.region raster=soils - +
    This form will make the current region settings exactly the same as those given in the cell header file for the raster map layer soils.

    - -

    +
    g.region raster=soils zoom=soils - +
    This form will first look up the cell header file for the raster map layer soils, use this as the @@ -385,14 +384,13 @@

    Changing extent and raster resolution using maps

    data in the map layer soils. Note that if the parameter raster=soils were not specified, the zoom would shrink to encompass all non-NULL data values in -the soils map that were located within the current region +the soils map that were located within the current region settings.

    - -

    +
    g.region -up raster=soils - +
    The -u option suppresses the re-setting of the current region definition. This can be useful when it is @@ -401,24 +399,24 @@

    Changing extent and raster resolution using maps

    without changing the current region settings.

    -

    +
    g.region -up zoom=soils save=soils - +
    This will zoom into the smallest region which encompasses all non-NULL soils data values, and save the new region settings in a file to be called soils -and stored under the windows directory in the +and stored under the windows directory in the user's current mapset. The current region settings are not changed.

    Changing extent and raster resolution in 3D

    -
    +
    g.region b=0 t=3000 tbres=200 res3=100 g.region -p3 - +
    This will define the 3D region for voxel computations. In this example a volume with bottom (0m) to top (3000m) @@ -430,7 +428,7 @@

    Using g.region in a shell in combination with OGR

    Extracting a spatial subset of the external vector map -soils.shp into new external vector map soils_cut.shp +soils.shp into new external vector map soils_cut.shp using the OGR ogr2ogr tool:
    @@ -443,8 +441,8 @@ 

    Using g.region in a shell in combination with OGR

    Using g.region in a shell in combination with GDAL

    Extracting a spatial subset of the external raster map -p016r035_7t20020524_z17_nn30.tif into new external raster -map p016r035_7t20020524_nc_spm_wake_nn30.tif using the GDAL +p016r035_7t20020524_z17_nn30.tif into new external raster +map p016r035_7t20020524_nc_spm_wake_nn30.tif using the GDAL gdalwarp tool:
    diff --git a/general/g.remove/g.remove.html b/general/g.remove/g.remove.html
    index b6554a398db..974a5d7c40e 100644
    --- a/general/g.remove/g.remove.html
    +++ b/general/g.remove/g.remove.html
    @@ -3,16 +3,16 @@ 

    DESCRIPTION

    g.remove removes data files matching a pattern given by wildcards or POSIX Extended Regular Expressions. If the -f force flag is not given then nothing is removed, instead the list of selected file names is printed to -stdout as a preview of the files to be deleted. +stdout as a preview of the files to be deleted.

    EXAMPLES

    -Delete map1 and map2 raster maps in the current mapset: +Delete map1 and map2 raster maps in the current mapset:
     g.remove -f type=raster name=tmp1,tmp2
     
    -Delete all raster and vector maps starting with "tmp_" in the current +Delete all raster and vector maps starting with "tmp_" in the current mapset:
     # show matching raster and vector maps but do not delete yet (as verification)
    @@ -22,8 +22,8 @@ 

    EXAMPLES

    g.remove -f type=raster,vector pattern="tmp_*"
    -Delete all vector maps starting with "stream_" in the current mapset, -but exclude those ending with "_final": +Delete all vector maps starting with "stream_" in the current mapset, +but exclude those ending with "_final":
     g.remove -f type=vector pattern="stream_*" exclude="*_final"
     
    diff --git a/general/g.setproj/g.setproj.html b/general/g.setproj/g.setproj.html index f59d8331ced..eb39ccb4010 100644 --- a/general/g.setproj/g.setproj.html +++ b/general/g.setproj/g.setproj.html @@ -52,7 +52,7 @@

    NOTES

    If the datum or ellipsoid required are not listed within this program, the user/administrator may add the definition to the files datum.table, datumtransform.table and ellipse.table in the -$GISBASE/etc/proj directory. +$GISBASE/etc/proj directory.

    Depending on the projection selected, the user will then be prompted for the various other parameters required to define it. @@ -74,9 +74,9 @@

    SEE ALSO

    Further reading

    diff --git a/general/g.tempfile/g.tempfile.html b/general/g.tempfile/g.tempfile.html index 8952e531b78..668252e8a7b 100644 --- a/general/g.tempfile/g.tempfile.html +++ b/general/g.tempfile/g.tempfile.html @@ -3,7 +3,7 @@

    DESCRIPTION

    g.tempfile is designed for shell scripts that need to use large temporary files. GRASS provides a mechanism for temporary files that does not depend on -/tmp. GRASS temporary files are created in the data base with the assumption +/tmp/. GRASS temporary files are created in the data base with the assumption that there will be enough space under the data base for large files. GRASS periodically removes temporary files that have been left behind by programs that failed to remove them before terminating. @@ -13,18 +13,21 @@

    DESCRIPTION

    creates an unique file and prints the name. The user is required to provide a process-id which will be used as part of the name of the file. Most Unix shells provide a way to get the process id of the current shell. -For /bin/sh and /bin/csh this is $$. -It is recommended that $$ be specified as the process-id for +For /bin/sh and /bin/csh this is $$. +It is recommended that $$ be specified as the process-id for g.tempfile.

    EXAMPLE

    -For /bin/sh scripts the following syntax should be used: +For /bin/sh scripts the following syntax should be used: +
     temp1=`g.tempfile pid=$$`
     temp2=`g.tempfile pid=$$`
     
    -For /bin/csh scripts, the following can be used: + +For /bin/csh scripts, the following can be used: +
     set temp1=`g.tempfile pid=$$`
     set temp2=`g.tempfile pid=$$`
    @@ -38,10 +41,10 @@ 

    NOTES

    Although GRASS does eventually get around to removing tempfiles that have been left behind, the programmer should make every effort to remove these files. They often get -large and take up disk space. If you write /bin/sh scripts, -learn to use the /bin/sh trap command. If you -write /bin/csh scripts, learn to use the /bin/csh -onintr command. +large and take up disk space. If you write /bin/sh scripts, +learn to use the /bin/sh related trap command. If you +write /bin/csh scripts, learn to use the /bin/csh +related onintr command.

    AUTHOR

    diff --git a/general/g.version/g.version.html b/general/g.version/g.version.html index f5d2f63043c..abb3867c9d5 100644 --- a/general/g.version/g.version.html +++ b/general/g.version/g.version.html @@ -22,7 +22,7 @@

    NOTES

    by -e flag.

    -See also function version() +See also function version() from Python Scripting Library. @@ -69,10 +69,10 @@

    Full info in shell script style

    sqlite=3.36.0
    -Note: if revision=exported is reported instead of the git hash then the -git program was not available during compilation of GRASS GIS and the -source code did not contain the .git/ subdirectory (requires e.g. to -git clone the GRASS GIS software repository.) +Note: if revision=exported is reported instead of the git hash then the +git program was not available during compilation of GRASS GIS and the +source code did not contain the .git/ subdirectory (requires e.g. to +git clone the GRASS GIS software repository.)

    Citing GRASS GIS

    diff --git a/general/g.version/tests/g_version_test.py b/general/g.version/tests/g_version_test.py index 805518382a2..ac9efed5491 100644 --- a/general/g.version/tests/g_version_test.py +++ b/general/g.version/tests/g_version_test.py @@ -4,18 +4,18 @@ def test_g_version_no_flag(session): """Test that g.version output contains the word 'GRASS'.""" output = gs.read_command("g.version", env=session.env).strip() - assert ( - "GRASS" in output - ), "Expected 'GRASS' in g.version output, but it was not found." + assert "GRASS" in output, ( + "Expected 'GRASS' in g.version output, but it was not found." + ) def test_c_flag(session): """Test the output of g.version -c for Copyright and License Statement.""" expected_text = "Copyright and License Statement" output = gs.read_command("g.version", flags="c", env=session.env).strip() - assert ( - expected_text in output - ), f"Expected '{expected_text}' in g.version -c output, but got: '{output}'" + assert expected_text in output, ( + f"Expected '{expected_text}' in g.version -c output, but got: '{output}'" + ) def test_e_flag(session): @@ -23,17 +23,17 @@ def test_e_flag(session): expected_keys = ["PROJ:", "GDAL/OGR:", "SQLite:"] output = gs.read_command("g.version", flags="e", env=session.env).strip() for key in expected_keys: - assert ( - key in output - ), f"Expected key '{key}' in g.version -e output, but it was not found." + assert key in output, ( + f"Expected key '{key}' in g.version -e output, but it was not found." + ) def test_b_flag(session): """Test that g.version -b output contains the word 'GRASS'.""" output = gs.read_command("g.version", flags="b", env=session.env).strip() - assert ( - "GRASS" in output - ), "Expected 'GRASS' in g.version -b output, but it was not found." + assert "GRASS" in output, ( + "Expected 'GRASS' in g.version -b output, but it was not found." + ) def test_g_flag(session): @@ -48,9 +48,9 @@ def test_g_flag(session): ] output = gs.parse_command("g.version", flags="g", env=session.env) for key in expected_keys: - assert ( - key in output - ), f"Expected key '{key}' in g.version -g output, but it was not found." + assert key in output, ( + f"Expected key '{key}' in g.version -g output, but it was not found." + ) def test_r_flag(session): @@ -58,9 +58,9 @@ def test_r_flag(session): expected_texts = ["libgis revision:", "libgis date:"] output = gs.read_command("g.version", flags="r", env=session.env).strip() for text in expected_texts: - assert ( - text in output - ), f"Expected key '{text}' in g.version -r output, but it was not found." + assert text in output, ( + f"Expected key '{text}' in g.version -r output, but it was not found." + ) def test_x_flag(session): @@ -78,6 +78,6 @@ def curly_brackets_paired(text): return False return counter == 0 - assert curly_brackets_paired( - output - ), "Curly brackets are not properly paired in the g.version -x output." + assert curly_brackets_paired(output), ( + "Curly brackets are not properly paired in the g.version -x output." + ) diff --git a/grasslib.dox b/grasslib.dox index dd5de826cfe..1dd0bce6a00 100644 --- a/grasslib.dox +++ b/grasslib.dox @@ -1,7 +1,7 @@ /*! \mainpage GRASS GIS 8 Programmer's Manual GRASS GIS (Geographic @@ -23,7 +23,7 @@ Team, an international team of programmers, GRASS module authors are cited within their module's source code and the contributed manual pages. -© 2000-2024 by the GRASS Development Team +© 2000-2025 by the GRASS Development Team This manual is published under GNU Free Documentation diff --git a/gui/wxpython/.pylintrc b/gui/wxpython/.pylintrc deleted file mode 100644 index 7bfe0db218d..00000000000 --- a/gui/wxpython/.pylintrc +++ /dev/null @@ -1,688 +0,0 @@ -[MASTER] - -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code. -extension-pkg-allow-list= - -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code. (This is an alternative name to extension-pkg-allow-list -# for backward compatibility.) -extension-pkg-whitelist= - -# Return non-zero exit code if any of these messages/categories are detected, -# even if score is above --fail-under value. Syntax same as enable. Messages -# specified are enabled, while categories only check already-enabled messages. -fail-on= - -# Specify a score threshold to be exceeded before program exits with error. -fail-under=10.0 - -# Files or directories to be skipped. They should be base names, not paths. -ignore=CVS - -# Add files or directories matching the regex patterns to the ignore-list. The -# regex matches against paths and can be in Posix or Windows format. -ignore-paths=core/.*, - dbmgr/.*, - docs/.*, # Sphinx config files. - gcp/.*, - gmodeler/.*, - gui_core/.*, - iclass/.*, - image2target/.*, - iscatt/.*, - lmgr/.*, # Close to being compliant. - location_wizard/.*, # Close to being compliant. - mapdisp/.*, # Close to being compliant. - mapswipe/.*, # Close to being compliant. - mapwin/.*, # Close to being compliant. - modules/.*, # Close to being compliant. - photo2image/.*, - nviz/.*, - psmap/.*, - tplot/.*, # Close to being compliant. - vdigit/.*, - vnet/.*, # Close to being compliant. - web_services/.*, # Close to being compliant. - wxplot/.*, # Close to being compliant. - xml/, # XML files only. - menustrings.py, - wxgui.py, - states.txt, - .*Makefile, - .*README.*, - - -# Files or directories matching the regex patterns are skipped. The regex -# matches against base names, not paths. -ignore-patterns= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -#init-hook= - -# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the -# number of processors available to use. -jobs=1 - -# Control the amount of potential inferred values when inferring a single -# object. This can help the performance when dealing with large functions or -# complex, nested conditions. -limit-inference-results=100 - -# List of plugins (as comma separated values of python module names) to load, -# usually to register additional checkers. -load-plugins= - -# Pickle collected data for later comparisons. -persistent=yes - -# Minimum Python version to use for version dependent checks. Will default to -# the version used to run pylint. -py-version=3.8 - -# When enabled, pylint would attempt to guess common misconfiguration and emit -# user-friendly hints instead of false-positive error messages. -suggestion-mode=yes - -# Allow loading of arbitrary C extensions. Extensions are imported into the -# active Python interpreter and may run arbitrary code. -unsafe-load-any-extension=no - - -[MESSAGES CONTROL] - -# Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. -confidence= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once). You can also use "--disable=all" to -# disable everything first and then reenable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use "--disable=all --enable=classes -# --disable=W". -disable=raw-checker-failed, - bad-inline-option, - locally-disabled, - file-ignored, - suppressed-message, - deprecated-pragma, - fixme, - unnecessary-lambda, - # Import issues - import-error, - wrong-import-position, - ungrouped-imports, - wrong-import-order, - unused-import, - import-outside-toplevel, - consider-using-from-import, - no-name-in-module, - # End of import issues - missing-function-docstring, - missing-module-docstring, - missing-class-docstring, - useless-object-inheritance, - attribute-defined-outside-init, - no-self-use, - unused-variable, - possibly-unused-variable, - unused-argument, - expression-not-assigned, - unnecessary-pass, - pointless-string-statement, - unreachable, - self-assigning-variable, - redefined-builtin, - redefined-outer-name, - cell-var-from-loop, - undefined-loop-variable, - unspecified-encoding, - arguments-differ, - arguments-renamed, - no-value-for-parameter, - redundant-keyword-arg, - protected-access, - inconsistent-return-statements, - too-many-function-args, - global-statement, - global-variable-not-assigned, - global-variable-undefined, - dangerous-default-value, - broad-except, - bare-except, - invalid-envvar-default, - anomalous-backslash-in-string, - # Here we start consider... warnings - consider-using-enumerate, - consider-using-set-comprehension, - consider-using-in, - consider-using-dict-items, - consider-iterating-dictionary, - consider-using-f-string, - unnecessary-comprehension, - simplifiable-if-expression, - simplifiable-if-statement, - use-list-literal, - use-dict-literal, - use-a-generator, - use-implicit-booleaness-not-len, - no-else-return, - no-else-raise, - no-else-continue, - raise-missing-from, - super-with-arguments, - useless-return, - # Here we end consider... warnings - consider-using-with, # Resource-related warning - use-symbolic-message-instead - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time (only on the command line, not in the configuration file where -# it should appear only once). See also the "--disable" option for examples. -enable=c-extension-no-member - - -[REPORTS] - -# Python expression which should return a score less than or equal to 10. You -# have access to the variables 'error', 'warning', 'refactor', and 'convention' -# which contain the number of messages in each category, as well as 'statement' -# which is the total number of statements analyzed. This score is used by the -# global evaluation report (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details. -#msg-template= - -# Set the output format. Available formats are text, parseable, colorized, json -# and msvs (visual studio). You can also give a reporter class, e.g. -# mypackage.mymodule.MyReporterClass. -output-format=text - -# Tells whether to display a full report or only the messages. -reports=no - -# Activate the evaluation score. -score=yes - - -[REFACTORING] - -# Maximum number of nested blocks for function / method body -max-nested-blocks=10 - -# Complete name of functions that never returns. When checking for -# inconsistent-return-statements if a never returning function is called then -# it will be considered as an explicit return statement and no message will be -# printed. -never-returning-functions=sys.exit,argparse.parse_error - - -[SPELLING] - -# Limits count of emitted suggestions for spelling mistakes. -max-spelling-suggestions=4 - -# Spelling dictionary name. Available dictionaries: fr_MC (myspell), fr_CA -# (myspell), fr_BE (myspell), fr_LU (myspell), fr_CH (myspell), fr_FR -# (myspell), ar (myspell), es_CR (myspell), de_CH_frami (myspell), es_EC -# (myspell), ar_YE (myspell), en_CA (myspell), ar_BH (myspell), ar_IN -# (myspell), ar_TN (myspell), en_ZA (myspell), de_DE_frami (myspell), ar_SY -# (myspell), ar_IQ (myspell), ar_LB (myspell), ar_KW (myspell), ru_RU -# (myspell), es_BO (myspell), en_GB (myspell), ar_SD (myspell), de_DE -# (myspell), es_CU (myspell), es_PA (myspell), ar_EG (myspell), es_HN -# (myspell), de_CH (myspell), es_NI (myspell), es_AR (myspell), es_ES -# (myspell), ar_SA (myspell), es_VE (myspell), de_AT_frami (myspell), it_IT -# (myspell), ar_OM (myspell), ar_DZ (myspell), it_CH (myspell), es_MX -# (myspell), es_PY (myspell), en_AU (myspell), es_DO (myspell), es_SV -# (myspell), es_PR (myspell), es_GT (myspell), ar_LY (myspell), ar_JO -# (myspell), en_US (myspell), de_AT (myspell), es_PE (myspell), ar_QA -# (myspell), es_CL (myspell), pt_BR (myspell), ar_AE (myspell), pt_PT -# (myspell), es_CO (myspell), es_UY (myspell), ar_MA (myspell), fr (myspell), -# es_US (myspell), en (aspell). -spelling-dict= - -# List of comma separated words that should be considered directives if they -# appear and the beginning of a comment and should not be checked. -spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy: - -# List of comma separated words that should not be checked. -spelling-ignore-words= - -# A path to a file that contains the private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to the private dictionary (see the -# --spelling-private-dict-file option) instead of raising a message. -spelling-store-unknown-words=no - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=FIXME, - XXX, - TODO - -# Regular expression of note tags to take in consideration. -#notes-rgx= - - -[STRING] - -# This flag controls whether inconsistent-quotes generates a warning when the -# character used as a quote delimiter is used inconsistently within a module. -check-quote-consistency=no - -# This flag controls whether the implicit-str-concat should generate a warning -# on implicit string concatenation in sequences defined over several lines. -check-str-concat-over-line-jumps=no - - -[TYPECHECK] - -# List of decorators that produce context managers, such as -# contextlib.contextmanager. Add to this list to register other decorators that -# produce valid context managers. -contextmanager-decorators=contextlib.contextmanager - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E1101 when accessed. Python regular -# expressions are accepted. -generated-members= - -# Tells whether missing members accessed in mixin class should be ignored. A -# class is considered mixin if its name matches the mixin-class-rgx option. -ignore-mixin-members=yes - -# Tells whether to warn about missing members when the owner of the attribute -# is inferred to be None. -ignore-none=yes - -# This flag controls whether pylint should warn about no-member and similar -# checks whenever an opaque object is returned when inferring. The inference -# can return multiple potential results while evaluating a Python object, but -# some branches might not be evaluated, which results in partial inference. In -# that case, it might be useful to still emit no-member and other checks for -# the rest of the inferred objects. -ignore-on-opaque-inference=yes - -# List of class names for which member attributes should not be checked (useful -# for classes with dynamically set attributes). This supports the use of -# qualified names. -ignored-classes=optparse.Values,thread._local,_thread._local,main_window.page.MainPageBase - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis). It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules=wx - -# Show a hint with possible names when a member name was not found. The aspect -# of finding the hint is based on edit distance. -missing-member-hint=yes - -# The minimum edit distance a name should have in order to be considered a -# similar match for a missing member name. -missing-member-hint-distance=1 - -# The total number of similar names that should be taken in consideration when -# showing a hint for a missing member. -missing-member-max-choices=1 - -# Regex pattern to define which classes are considered mixins ignore-mixin- -# members is set to 'yes' -mixin-class-rgx=.*[Mm]ixin - -# List of decorators that change the signature of a decorated function. -signature-mutators= - - -[VARIABLES] - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid defining new builtins when possible. -# Translation function is (unfortunately) defined as a buildin. -additional-builtins=_ - -# Tells whether unused global variables should be treated as a violation. -allow-global-unused-variables=yes - -# List of names allowed to shadow builtins -allowed-redefined-builtins= - -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_, - _cb - -# A regular expression matching the name of dummy variables (i.e. expected to -# not be used). -# On top of the defaults, simple unused is also permissible. -dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore. -ignored-argument-names=_.*|^ignored_|^unused_|^event$ - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# List of qualified module names which can have objects that can redefine -# builtins. -redefining-builtins-modules=past.builtins,future.builtins,builtins,io - - -[SIMILARITIES] - -# Comments are removed from the similarity computation -ignore-comments=yes - -# Docstrings are removed from the similarity computation -ignore-docstrings=yes - -# Imports are removed from the similarity computation -ignore-imports=no - -# Signatures are removed from the similarity computation -ignore-signatures=no - -# Minimum lines number of a similarity. -# Matching only larger chunks of code, not the default 4 lines. -min-similarity-lines=10 - - -[LOGGING] - -# The type of string formatting that logging methods do. `old` means using % -# formatting, `new` is for `{}` formatting. -logging-format-style=old - -# Logging modules to check that the string format arguments are in logging -# function parameter format. -logging-modules=logging - - -[BASIC] - -# Naming style matching correct argument names. -argument-naming-style=any - -# Regular expression matching correct argument names. Overrides argument- -# naming-style. -#argument-rgx= - -# Naming style matching correct attribute names. -attr-naming-style=any - -# Regular expression matching correct attribute names. Overrides attr-naming- -# style. -#attr-rgx= - -# Bad variable names which should always be refused, separated by a comma. -bad-names=foo, - bar, - baz, - toto, - tutu, - tata - -# Bad variable names regexes, separated by a comma. If names match any regex, -# they will always be refused -bad-names-rgxs= - -# Naming style matching correct class attribute names. -class-attribute-naming-style=any - -# Regular expression matching correct class attribute names. Overrides class- -# attribute-naming-style. -#class-attribute-rgx= - -# Naming style matching correct class constant names. -class-const-naming-style=UPPER_CASE - -# Regular expression matching correct class constant names. Overrides class- -# const-naming-style. -#class-const-rgx= - -# Naming style matching correct class names. -class-naming-style=PascalCase - -# Regular expression matching correct class names. Overrides class-naming- -# style. -#class-rgx= - -# Naming style matching correct constant names. -const-naming-style=any - -# Regular expression matching correct constant names. Overrides const-naming- -# style. -#const-rgx= - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=-1 - -# Naming style matching correct function names. -function-naming-style=any - -# Regular expression matching correct function names. Overrides function- -# naming-style. -#function-rgx= - -# Good variable names which should always be accepted, separated by a comma. -good-names=i, - j, - k, - x, - y, - z, - ex, - Run, - _ - -# Good variable names regexes, separated by a comma. If names match any regex, -# they will always be accepted -good-names-rgxs= - -# Include a hint for the correct naming format with invalid-name. -include-naming-hint=no - -# Naming style matching correct inline iteration names. -inlinevar-naming-style=any - -# Regular expression matching correct inline iteration names. Overrides -# inlinevar-naming-style. -#inlinevar-rgx= - -# Naming style matching correct method names. -method-naming-style=any - -# Regular expression matching correct method names. Overrides method-naming- -# style. -#method-rgx= - -# Naming style matching correct module names. -module-naming-style=snake_case - -# Regular expression matching correct module names. Overrides module-naming- -# style. -#module-rgx= - -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= - -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=^_ - -# List of decorators that produce properties, such as abc.abstractproperty. Add -# to this list to register other decorators that produce valid properties. -# These decorators are taken in consideration only for invalid-name. -property-classes=abc.abstractproperty - -# Naming style matching correct variable names. -variable-naming-style=any - -# Regular expression matching correct variable names. Overrides variable- -# naming-style. -#variable-rgx= - - -[FORMAT] - -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -expected-line-ending-format= - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=^\s*(# )??$ - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - -# Maximum number of characters on a single line. -max-line-length=120 - -# Maximum number of lines in a module. -max-module-lines=3000 - -# Allow the body of a class to be on the same line as the declaration if body -# contains single statement. -single-line-class-stmt=no - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=no - - -[IMPORTS] - -# List of modules that can be imported at any level, not just the top level -# one. -allow-any-import-level= - -# Allow wildcard imports from modules that define __all__. -allow-wildcard-with-all=no - -# Analyse import fallback blocks. This can be used to support both Python 2 and -# 3 compatible code, which means that the block might have code that exists -# only in one or another interpreter, leading to false positives when analysed. -analyse-fallback-blocks=no - -# Deprecated modules which should not be used, separated by a comma. -deprecated-modules= - -# Output a graph (.gv or any supported image format) of external dependencies -# to the given file (report RP0402 must not be disabled). -ext-import-graph= - -# Output a graph (.gv or any supported image format) of all (i.e. internal and -# external) dependencies to the given file (report RP0402 must not be -# disabled). -import-graph= - -# Output a graph (.gv or any supported image format) of internal dependencies -# to the given file (report RP0402 must not be disabled). -int-import-graph= - -# Force import order to recognize a module as part of the standard -# compatibility libraries. -known-standard-library= - -# Force import order to recognize a module as part of a third party library. -known-third-party=enchant - -# Couples of modules and preferred modules, separated by a comma. -preferred-modules= - - -[CLASSES] - -# Warn about protected attribute access inside special methods -check-protected-access-in-special-methods=no - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__, - __new__, - setUp, - __post_init__ - -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict, - _fields, - _replace, - _source, - _make - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=cls - - -[DESIGN] - -# List of regular expressions of class ancestor names to ignore when counting -# public methods (see R0903) -exclude-too-few-public-methods= - -# List of qualified class names to ignore when counting class parents (see -# R0901) -ignored-parents= - -# Maximum number of arguments for function / method. -# We tend to have function with more arguments than the default 5 -# and that doesn't seem to be the problem of our code. -max-args=25 - -# Maximum number of attributes for a class (see R0902). -max-attributes=40 - -# Maximum number of boolean expressions in an if statement (see R0916). -max-bool-expr=5 - -# Maximum number of branch for function / method body. -max-branches=30 - -# Maximum number of locals for function / method body. -max-locals=40 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=120 - -# Maximum number of return / yield for function / method body. -max-returns=10 - -# Maximum number of statements in function / method body. -max-statements=100 - -# Minimum number of public methods for a class (see R0903). -# It is okay to inherit and have only __init__. -min-public-methods=0 - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "BaseException, Exception". -overgeneral-exceptions=BaseException, - Exception diff --git a/gui/wxpython/Makefile b/gui/wxpython/Makefile index b1785f75dc0..a661e593bfc 100644 --- a/gui/wxpython/Makefile +++ b/gui/wxpython/Makefile @@ -49,7 +49,7 @@ xml/module_tree_menudata.xml: core/toolboxes.py $(call run_grass,$(PYTHON) $< "validate" $@) menustrings.py: core/menutree.py $(DSTDIR)/xml/menudata.xml $(DSTDIR)/xml/module_tree_menudata.xml $(DSTDIR)/xml/menudata_modeler.xml $(DSTDIR)/xml/menudata_psmap.xml - @echo "# This is a generated file.\n" > $@ + @echo "# This is a generated file.\n# pylint: skip-file\n" > $@ $(call run_grass,$(PYTHON) $< "manager" >> $@) $(call run_grass,$(PYTHON) $< "module_tree" >> $@) $(call run_grass,$(PYTHON) $< "modeler" >> $@) diff --git a/gui/wxpython/animation/anim.py b/gui/wxpython/animation/anim.py index 591bbff151c..6496f3b4ca4 100644 --- a/gui/wxpython/animation/anim.py +++ b/gui/wxpython/animation/anim.py @@ -93,11 +93,11 @@ def SetCallbackOrientationChanged(self, callback): def Start(self): if not self.IsActive(): - return + pass def Pause(self, paused): if not self.IsActive(): - return + pass def Stop(self): if not self.IsActive(): diff --git a/gui/wxpython/animation/controller.py b/gui/wxpython/animation/controller.py index 1a8cb1fe9cc..6cf3269eeb1 100644 --- a/gui/wxpython/animation/controller.py +++ b/gui/wxpython/animation/controller.py @@ -36,7 +36,6 @@ HashCmds, ) from animation.data import AnimationData -from itertools import starmap class AnimationController(wx.EvtHandler): @@ -369,7 +368,7 @@ def _updateAnimations(self, activeIndices, mapNamesDict=None): if anim.viewMode == "3d": regions = [None] * len(regions) self.animations[i].SetFrames( - list(starmap(HashCmds, zip(anim.cmdMatrix, regions))) + list(map(HashCmds, anim.cmdMatrix, regions)) ) self.animations[i].SetActive(True) else: @@ -627,7 +626,6 @@ def export_avi_callback(event): del self.busy if error: GError(parent=self.frame, message=error) - return if exportInfo["method"] == "sequence": filename = os.path.join( diff --git a/gui/wxpython/animation/data.py b/gui/wxpython/animation/data.py index 3799cf43ef1..ec33ce082d2 100644 --- a/gui/wxpython/animation/data.py +++ b/gui/wxpython/animation/data.py @@ -240,9 +240,7 @@ def _computeRegions(self, count, startRegion, endRegion=None, zoomValue=None): del region["projection"] if "zone" in region: del region["zone"] - regions = [] - for i in range(self._mapCount): - regions.append(copy.copy(region)) + regions = [copy.copy(region) for i in range(self._mapCount)] self._regions = regions if not (endRegion or zoomValue): return @@ -295,9 +293,8 @@ def __init__(self): def SetName(self, name): if not self.hidden: if self._mapType is None: - raise ValueError( - "To set layer name, the type of layer must be specified." - ) + msg = "To set layer name, the type of layer must be specified." + raise ValueError(msg) if self._mapType in {"strds", "stvds", "str3ds"}: try: name = validateTimeseriesName(name, self._mapType) diff --git a/gui/wxpython/animation/nviztask.py b/gui/wxpython/animation/nviztask.py index 26605737ea8..5dbb1d3e982 100644 --- a/gui/wxpython/animation/nviztask.py +++ b/gui/wxpython/animation/nviztask.py @@ -136,9 +136,7 @@ def _processSurface(self, surface, mapName): self._setMultiTaskParam(mode2, value) # position - pos = [] - for coor in ("x", "y", "z"): - pos.append(str(surface["position"][coor])) + pos = [str(surface["position"][coor]) for coor in ("x", "y", "z")] value = ",".join(pos) self._setMultiTaskParam("surface_position", value) diff --git a/gui/wxpython/animation/provider.py b/gui/wxpython/animation/provider.py index 1b07f63841f..08d1c083cb8 100644 --- a/gui/wxpython/animation/provider.py +++ b/gui/wxpython/animation/provider.py @@ -217,8 +217,9 @@ def Load(self, force=False, bgcolor=(255, 255, 255), nprocs=4): """ Debug.msg( 2, - "BitmapProvider.Load: " - "force={f}, bgcolor={b}, nprocs={n}".format(f=force, b=bgcolor, n=nprocs), + "BitmapProvider.Load: force={f}, bgcolor={b}, nprocs={n}".format( + f=force, b=bgcolor, n=nprocs + ), ) cmds = [] regions = [] diff --git a/gui/wxpython/animation/temporal_manager.py b/gui/wxpython/animation/temporal_manager.py index 0f00cc6e329..d121d59e13a 100644 --- a/gui/wxpython/animation/temporal_manager.py +++ b/gui/wxpython/animation/temporal_manager.py @@ -19,6 +19,7 @@ import datetime from operator import itemgetter +from pathlib import Path import grass.script as gs import grass.temporal as tgis @@ -116,8 +117,7 @@ def _setTemporalState(self): # check for units for relative type if relative: - units = set() - units.update(infoDict["unit"] for infoDict in self.timeseriesInfo.values()) + units = {infoDict["unit"] for infoDict in self.timeseriesInfo.values()} if len(units) > 1: message = _( "It is not allowed to display data with different units (%s)." @@ -162,10 +162,11 @@ def GetGranularity(self): return self._getCommonGranularity() def _getCommonGranularity(self): - allMaps = [] - for dataset in self.timeseriesList: - maps = self.timeseriesInfo[dataset]["maps"] - allMaps.extend(maps) + allMaps = [ + a + for dataset in self.timeseriesList + for a in self.timeseriesInfo[dataset]["maps"] + ] if self.temporalType == TemporalType.ABSOLUTE: gran = tgis.compute_absolute_time_granularity(allMaps) @@ -210,9 +211,9 @@ def GetLabelsAndMaps(self): newMapList[i : i + len(mapList)] = mapList newMapLists.append(newMapList) - mapDict = {} - for i, dataset in enumerate(self.timeseriesList): - mapDict[dataset] = newMapLists[i] + mapDict = { + dataset: newMapLists[i] for i, dataset in enumerate(self.timeseriesList) + } if self.temporalType == TemporalType.ABSOLUTE: # ('1996-01-01 00:00:00', '1997-01-01 00:00:00', 'year'), @@ -411,8 +412,7 @@ def createAbsoluteInterval(): gs.mapcalc(exp="temp_6 = rand(0, 650)", overwrite=True) n1 = gs.read_command("g.tempfile", pid=1, flags="d").strip() - fd = open(n1, "w") - fd.write( + Path(n1).write_text( "prec_1|2001-01-01|2001-02-01\n" "prec_2|2001-04-01|2001-05-01\n" "prec_3|2001-05-01|2001-09-01\n" @@ -420,11 +420,9 @@ def createAbsoluteInterval(): "prec_5|2002-01-01|2002-05-01\n" "prec_6|2002-05-01|2002-07-01\n" ) - fd.close() n2 = gs.read_command("g.tempfile", pid=2, flags="d").strip() - fd = open(n2, "w") - fd.write( + Path(n2).write_text( "temp_1|2000-10-01|2001-01-01\n" "temp_2|2001-04-01|2001-05-01\n" "temp_3|2001-05-01|2001-09-01\n" @@ -432,7 +430,6 @@ def createAbsoluteInterval(): "temp_5|2002-01-01|2002-05-01\n" "temp_6|2002-05-01|2002-07-01\n" ) - fd.close() name1 = "absinterval1" name2 = "absinterval2" gs.run_command( @@ -486,8 +483,7 @@ def createRelativeInterval(): gs.mapcalc(exp="temp_6 = rand(0, 650)", overwrite=True) n1 = gs.read_command("g.tempfile", pid=1, flags="d").strip() - fd = open(n1, "w") - fd.write( + Path(n1).write_text( "prec_1|1|4\n" "prec_2|6|7\n" "prec_3|7|10\n" @@ -495,11 +491,9 @@ def createRelativeInterval(): "prec_5|11|14\n" "prec_6|14|17\n" ) - fd.close() n2 = gs.read_command("g.tempfile", pid=2, flags="d").strip() - fd = open(n2, "w") - fd.write( + Path(n2).write_text( "temp_1|5|6\n" "temp_2|6|7\n" "temp_3|7|10\n" @@ -507,7 +501,6 @@ def createRelativeInterval(): "temp_5|11|18\n" "temp_6|19|22\n" ) - fd.close() name1 = "relinterval1" name2 = "relinterval2" gs.run_command( @@ -560,8 +553,7 @@ def createAbsolutePoint(): gs.mapcalc(exp="prec_6 = rand(0, 650)", overwrite=True) n1 = gs.read_command("g.tempfile", pid=1, flags="d").strip() - fd = open(n1, "w") - fd.write( + Path(n1).write_text( "prec_1|2001-01-01\n" "prec_2|2001-03-01\n" "prec_3|2001-04-01\n" @@ -569,7 +561,6 @@ def createAbsolutePoint(): "prec_5|2001-08-01\n" "prec_6|2001-09-01\n" ) - fd.close() name = "abspoint" gs.run_command( "t.create", @@ -608,9 +599,9 @@ def createRelativePoint(): gs.mapcalc(exp="prec_6 = rand(0, 650)", overwrite=True) n1 = gs.read_command("g.tempfile", pid=1, flags="d").strip() - fd = open(n1, "w") - fd.write("prec_1|1\nprec_2|3\nprec_3|5\nprec_4|7\nprec_5|11\nprec_6|13\n") - fd.close() + Path(n1).write_text( + "prec_1|1\nprec_2|3\nprec_3|5\nprec_4|7\nprec_5|11\nprec_6|13\n" + ) name = "relpoint" gs.run_command( "t.create", diff --git a/gui/wxpython/animation/toolbars.py b/gui/wxpython/animation/toolbars.py index 1ac3c2efedb..199bcf15aee 100644 --- a/gui/wxpython/animation/toolbars.py +++ b/gui/wxpython/animation/toolbars.py @@ -74,7 +74,7 @@ def __init__(self, parent): def _toolbarData(self): """Returns toolbar data (name, icon, handler)""" # BaseIcons are a set of often used icons. It is possible - # to reuse icons in ./trunk/gui/icons/grass or add new ones there. + # to reuse icons in gui/icons/grass or add new ones there. icons = ganimIcons return self._getToolbarData( ( @@ -120,7 +120,7 @@ def __init__(self, parent): def _toolbarData(self): """Returns toolbar data (name, icon, handler)""" # BaseIcons are a set of often used icons. It is possible - # to reuse icons in ./trunk/gui/icons/grass or add new ones there. + # to reuse icons in gui/icons/grass or add new ones there. icons = ganimIcons return self._getToolbarData( ( diff --git a/gui/wxpython/animation/utils.py b/gui/wxpython/animation/utils.py index 72e817ae962..54aafa3532f 100644 --- a/gui/wxpython/animation/utils.py +++ b/gui/wxpython/animation/utils.py @@ -212,8 +212,7 @@ def checkSeriesCompatibility(mapSeriesList=None, timeseriesList=None): ) if mapSeriesList: - count = set() - count.update(len(mapSeries) for mapSeries in mapSeriesList) + count = {len(mapSeries) for mapSeries in mapSeriesList} if len(count) > 1: raise GException( _( @@ -240,11 +239,9 @@ def ComputeScaledRect(sourceSize, destSize): """Fits source rectangle into destination rectangle by scaling and centering. - >>> ComputeScaledRect(sourceSize = (10, 40), destSize = (100, 50)) {'height': 50, 'scale': 1.25, 'width': 13, 'x': 44, 'y': 0} - :param sourceSize: size of source rectangle :param destSize: size of destination rectangle """ diff --git a/gui/wxpython/core/debug.py b/gui/wxpython/core/debug.py index b6c12a60b62..bcb33f4d1da 100644 --- a/gui/wxpython/core/debug.py +++ b/gui/wxpython/core/debug.py @@ -61,7 +61,7 @@ def msg(self, level, message, *args): :param args: formatting params """ # self.SetLevel() - if self.debuglevel > 0 and level > 0 and level <= self.debuglevel: + if 0 < level <= self.debuglevel: if args: sys.stderr.write( "GUI D%d/%d: " % (level, self.debuglevel) diff --git a/gui/wxpython/core/gcmd.py b/gui/wxpython/core/gcmd.py index 0c64f0d0973..0b8d57fe2fe 100644 --- a/gui/wxpython/core/gcmd.py +++ b/gui/wxpython/core/gcmd.py @@ -25,17 +25,20 @@ @author Martin Landa """ +from __future__ import annotations + +import errno +import locale import os +import signal +import subprocess import sys import time -import errno -import signal import traceback -import locale -import subprocess from threading import Thread -import wx +from typing import TYPE_CHECKING, AnyStr, TextIO, overload +import wx from core.debug import Debug from core.globalvar import SCT_EXT @@ -44,15 +47,29 @@ is_mswindows = sys.platform == "win32" if is_mswindows: + import msvcrt + from win32file import ReadFile, WriteFile from win32pipe import PeekNamedPipe - import msvcrt else: - import select import fcntl + import select +if TYPE_CHECKING: + from io import TextIOWrapper -def DecodeString(string): + +@overload +def DecodeString(string: AnyStr) -> AnyStr | str: + pass + + +@overload +def DecodeString(string: None) -> None: + pass + + +def DecodeString(string: AnyStr | None) -> AnyStr | str | None: """Decode string using system encoding :param string: string to be decoded @@ -68,7 +85,17 @@ def DecodeString(string): return string -def EncodeString(string): +@overload +def EncodeString(string: str) -> bytes | str: + pass + + +@overload +def EncodeString(string: None) -> None: + pass + + +def EncodeString(string: str | None) -> bytes | str | None: """Return encoded string using system locales :param string: string to be encoded @@ -298,7 +325,7 @@ def _recv(self, which, maxsize): message = "Other end disconnected!" -def recv_some(p, t=0.1, e=1, tr=5, stderr=0): +def recv_some(p, t=0.1, e=1, tr=5, stderr=0) -> str: # TODO: use LiteralString on 3.11+ tr = max(tr, 1) x = time.time() + t y = [] @@ -342,7 +369,7 @@ def __init__( stdin=None, verbose=None, wait=True, - rerr=False, + rerr: bool | None = False, stdout=None, stderr=None, ): @@ -482,7 +509,7 @@ def __ProcessStdErr(self): type = "WARNING" elif "GRASS_INFO_ERROR" in line: # error type = "ERROR" - elif "GRASS_INFO_END": # end of message + elif "GRASS_INFO_END" in line: # end of message msg.append((type, content)) type = None content = "" @@ -510,7 +537,14 @@ class CommandThread(Thread): """Create separate thread for command. Used for commands launched on the background.""" - def __init__(self, cmd, env=None, stdin=None, stdout=sys.stdout, stderr=sys.stderr): + def __init__( + self, + cmd, + env=None, + stdin: TextIOWrapper | None = None, + stdout: TextIO = sys.stdout, + stderr: TextIO = sys.stderr, + ) -> None: """ :param cmd: command (given as list) :param env: environmental variables @@ -522,11 +556,11 @@ def __init__(self, cmd, env=None, stdin=None, stdout=sys.stdout, stderr=sys.stde self.cmd = cmd self.stdin = stdin - self.stdout = stdout - self.stderr = stderr + self.stdout: TextIO = stdout + self.stderr: TextIO = stderr self.env = env - self.module = None + self.module: Popen | None = None self.error = "" self._want_abort = False @@ -584,7 +618,7 @@ def run(self): print(e, file=sys.stderr) return 1 - if self.stdin: # read stdin if requested ... + if self.stdin and self.module.stdin is not None: # read stdin if requested... self.module.stdin.write(self.stdin) self.module.stdin.close() @@ -593,14 +627,14 @@ def run(self): def _redirect_stream(self): """Redirect stream""" - if self.stdout: + if self.stdout and self.module is not None and self.module.stdout is not None: # make module stdout/stderr non-blocking out_fileno = self.module.stdout.fileno() if not is_mswindows: flags = fcntl.fcntl(out_fileno, fcntl.F_GETFL) fcntl.fcntl(out_fileno, fcntl.F_SETFL, flags | os.O_NONBLOCK) - if self.stderr: + if self.stderr and self.module is not None and self.module.stderr is not None: # make module stdout/stderr non-blocking out_fileno = self.module.stderr.fileno() if not is_mswindows: @@ -608,19 +642,20 @@ def _redirect_stream(self): fcntl.fcntl(out_fileno, fcntl.F_SETFL, flags | os.O_NONBLOCK) # wait for the process to end, sucking in stuff until it does end - while self.module.poll() is None: - if self._want_abort: # abort running process - self.module.terminate() - self.aborted = True - return - if self.stdout: - line = recv_some(self.module, e=0, stderr=0) - self.stdout.write(line) - if self.stderr: - line = recv_some(self.module, e=0, stderr=1) - self.stderr.write(line) - if len(line) > 0: - self.error = line + if self.module is not None: + while self.module.poll() is None: + if self._want_abort: # abort running process + self.module.terminate() + self.aborted = True + return + if self.stdout: + line = recv_some(self.module, e=0, stderr=0) + self.stdout.write(line) + if self.stderr: + line = recv_some(self.module, e=0, stderr=1) + self.stderr.write(line) + if len(line) > 0: + self.error = line # get the last output if self.stdout: @@ -632,12 +667,12 @@ def _redirect_stream(self): if len(line) > 0: self.error = line - def abort(self): + def abort(self) -> None: """Abort running process, used by main thread to signal an abort""" self._want_abort = True -def _formatMsg(text): +def _formatMsg(text: str) -> str: """Format error messages for dialogs""" message = "" for line in text.splitlines(): @@ -660,14 +695,14 @@ def _formatMsg(text): def RunCommand( prog, flags="", - overwrite=False, - quiet=False, - verbose=False, + overwrite: bool = False, + quiet: bool = False, + verbose: bool = False, parent=None, - read=False, + read: bool = False, parse=None, - stdin=None, - getErrorMsg=False, + stdin: TextIO | None = None, + getErrorMsg: bool = False, env=None, **kwargs, ): @@ -717,7 +752,7 @@ def RunCommand( ps = grass.start_command(prog, flags, overwrite, quiet, verbose, env=env, **kwargs) - if stdin: + if stdin and ps.stdin: ps.stdin.write(encode(stdin)) ps.stdin.close() ps.stdin = None @@ -764,7 +799,7 @@ def RunCommand( return stdout, _formatMsg(stderr) -def GetDefaultEncoding(forceUTF8=False): +def GetDefaultEncoding(forceUTF8: bool = False) -> str: """Get default system encoding :param bool forceUTF8: force 'UTF-8' if encoding is not defined @@ -786,4 +821,4 @@ def GetDefaultEncoding(forceUTF8=False): return enc -_enc = GetDefaultEncoding() # define as global variable +_enc: str = GetDefaultEncoding() # define as global variable diff --git a/gui/wxpython/core/gconsole.py b/gui/wxpython/core/gconsole.py index cde41710cd9..b46d19254db 100644 --- a/gui/wxpython/core/gconsole.py +++ b/gui/wxpython/core/gconsole.py @@ -316,7 +316,7 @@ def write(self, s): if "GRASS_INFO_PERCENT" in line: value = int(line.rsplit(":", 1)[1].strip()) - progressValue = value if value >= 0 and value < 100 else 0 + progressValue = value if 0 <= value < 100 else 0 elif "GRASS_INFO_MESSAGE" in line: self.type = "message" self.message += line.split(":", 1)[1].strip() + "\n" @@ -856,13 +856,15 @@ def OnCmdDone(self, event): for p in task.get_options()["flags"]: if p.get("name") == "r" and p.get("value"): action = "delete" + mask_full_name = gs.parse_command("r.mask.status", format="json")["name"] + mask_name, mask_mapset = mask_full_name.split("@", maxsplit=1) gisenv = gs.gisenv() self._giface.grassdbChanged.emit( grassdb=gisenv["GISDBASE"], location=gisenv["LOCATION_NAME"], - mapset=gisenv["MAPSET"], + mapset=mask_mapset, action=action, - map="MASK", + map=mask_name, element="raster", ) diff --git a/gui/wxpython/core/giface.py b/gui/wxpython/core/giface.py index 1b17a3e5932..af7accf19b3 100644 --- a/gui/wxpython/core/giface.py +++ b/gui/wxpython/core/giface.py @@ -22,9 +22,6 @@ from grass.pydispatch.signal import Signal -# to disable Abstract class not referenced -# pylint: disable=R0921 - class Notification: """Enum class for notifications suggestions. diff --git a/gui/wxpython/core/globalvar.py b/gui/wxpython/core/globalvar.py index e0397a2b121..a5ce362f8c8 100644 --- a/gui/wxpython/core/globalvar.py +++ b/gui/wxpython/core/globalvar.py @@ -95,7 +95,8 @@ def CheckForWx(): version = parse_version_string(wx.__version__) if version < WXPY3_MIN_VERSION: - raise ValueError("Your wxPython version is {}".format(wx.__version__)) + msg = "Your wxPython version is {}".format(wx.__version__) + raise ValueError(msg) return except ImportError as e: print("ERROR: wxGUI requires wxPython. {}".format(e), file=sys.stderr) @@ -123,14 +124,12 @@ def CheckForWx(): import wx.lib.flatnotebook as FN -""" -Query layer (generated for example by selecting item in the Attribute Table Manager) -Deleted automatically on re-render action -""" +# Query layer (generated for example by selecting item in the Attribute Table Manager) +# Deleted automatically on re-render action # temporal query layer (removed on re-render action) QUERYLAYER = "qlayer" -"""Style definition for FlatNotebook pages""" +# Style definition for FlatNotebook pages FNPageStyle = ( FN.FNB_NODRAG | FN.FNB_TABS_BORDER_SIMPLE @@ -142,7 +141,7 @@ def CheckForWx(): FN.FNB_BOTTOM | FN.FNB_NODRAG | FN.FNB_NO_NAV_BUTTONS | FN.FNB_NO_X_BUTTON ) -"""Dialog widget dimension""" +# Dialog widget dimension DIALOG_SPIN_SIZE = (150, -1) DIALOG_COMBOBOX_SIZE = (300, -1) DIALOG_GSELECT_SIZE = (400, -1) @@ -233,21 +232,21 @@ def UpdateGRASSAddOnCommands(eList=None): Debug.msg(1, "Number of GRASS AddOn commands: %d", nCmd) -"""@brief Collected GRASS-related binaries/scripts""" +# Collected GRASS-related binaries/scripts grassCmd, grassScripts = get_commands() Debug.msg(1, "Number of core GRASS commands: %d", len(grassCmd)) UpdateGRASSAddOnCommands() -"""@Toolbar icon size""" +# Toolbar icon size toolbarSize = (24, 24) -"""@Check version of wxPython, use agwStyle for 2.8.11+""" +# Check version of wxPython, use agwStyle for 2.8.11+ hasAgw = CheckWxVersion([2, 8, 11, 0]) -wxPythonPhoenix = CheckWxPhoenix() +wxPythonPhoenix: bool = CheckWxPhoenix() gtk3 = "gtk3" in wx.PlatformInfo -"""@Add GUIDIR/scripts into path""" +# Add GUIDIR/scripts into path os.environ["PATH"] = os.path.join(GUIDIR, "scripts") + os.pathsep + os.environ["PATH"] ignoredCmdPattern = ( diff --git a/gui/wxpython/core/layerlist.py b/gui/wxpython/core/layerlist.py index 5b14a4039fd..43b1a4d118d 100644 --- a/gui/wxpython/core/layerlist.py +++ b/gui/wxpython/core/layerlist.py @@ -38,8 +38,9 @@ def GetSelectedLayers(self, activeOnly=True): layers = [] for layer in self._list: if layer.IsSelected(): - if activeOnly and layer.IsActive(): - layers.append(layer) + if activeOnly: + if layer.IsActive(): + layers.append(layer) else: layers.append(layer) return layers @@ -63,11 +64,7 @@ def GetLayersByTypes(self, mapTypes): :param mapTypes: list of types """ - layers = [] - for layer in self._list: - if layer.mapType in mapTypes: - layers.append(layer) - return layers + return [layer for layer in self._list if layer.mapType in mapTypes] def AddNewLayer( self, @@ -222,15 +219,15 @@ def SetName(self, name): len(fullName) == 1 and self._mapType != "rgb" ): # skip checking rgb maps for now if self._mapType is None: - raise ValueError( - "To set layer name, the type of layer must be specified." - ) + msg = "To set layer name, the type of layer must be specified." + raise ValueError(msg) res = gcore.find_file( name=fullName, element=self._internalTypes[self._mapType] ) if not res["mapset"]: - raise ValueError("Map <{name}> not found.".format(name=name)) + msg = "Map <{name}> not found.".format(name=name) + raise ValueError(msg) self._name = name + "@" + res["mapset"] else: self._name = name @@ -263,7 +260,8 @@ def SetMapType(self, mapType): :param mapType: can be 'raster', 'vector', 'raster_3d' """ if mapType not in self._mapTypes: - raise ValueError("Wrong map type used: {mtype}".format(mtype=mapType)) + msg = "Wrong map type used: {mtype}".format(mtype=mapType) + raise ValueError(msg) self._mapType = mapType @@ -282,9 +280,8 @@ def SetOpacity(self, opacity): :param float opacity: value between 0 and 1 """ if not (0 <= opacity <= 1): - raise ValueError( - "Opacity value must be between 0 and 1, not {op}.".format(op=opacity) - ) + msg = "Opacity value must be between 0 and 1, not {op}.".format(op=opacity) + raise ValueError(msg) self._opacity = opacity opacity = property(fget=GetOpacity, fset=SetOpacity) diff --git a/gui/wxpython/core/menutree.py b/gui/wxpython/core/menutree.py index ef3ebd7b414..a418ffcd98a 100644 --- a/gui/wxpython/core/menutree.py +++ b/gui/wxpython/core/menutree.py @@ -256,7 +256,7 @@ def collectParents(node, parents): else: import grass.script.core as gscore - gscore.fatal("Unknown value for parameter menu: " % menu) + gscore.fatal("Unknown value for parameter menu: %s" % menu) if action == "strings": menudata.PrintStrings(sys.stdout) @@ -269,6 +269,6 @@ def collectParents(node, parents): else: import grass.script.core as gscore - gscore.fatal("Unknown value for parameter action: " % action) + gscore.fatal("Unknown value for parameter action: %s" % action) sys.exit(0) diff --git a/gui/wxpython/core/render.py b/gui/wxpython/core/render.py index 941d21879cd..67e7561c4e7 100644 --- a/gui/wxpython/core/render.py +++ b/gui/wxpython/core/render.py @@ -51,10 +51,10 @@ def get_tempfile_name(suffix, create=False): # which may mitigate problems (like not cleaning files) in case we # go little beyond what is in the documentation in terms of opening # closing and removing the tmp file - tmp = tempfile.NamedTemporaryFile(suffix=suffix, delete=False) - # we don't want it open, we just need the name - name = tmp.name - tmp.close() + with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as tmp: + # we don't want it open, we just need the name + name = tmp.name + if not create: # remove empty file to have a clean state later os.remove(name) @@ -116,9 +116,7 @@ def __init__( self.name = name if self.type == "command": - self.cmd = [] - for c in cmd: - self.cmd.append(cmdlist_to_tuple(c)) + self.cmd = [] + [cmdlist_to_tuple(c) for c in cmd] else: self.cmd = cmdlist_to_tuple(cmd) @@ -326,9 +324,7 @@ def SetOpacity(self, value): def SetCmd(self, cmd): """Set new command for layer""" if self.type == "command": - self.cmd = [] - for c in cmd: - self.cmd.append(cmdlist_to_tuple(c)) + self.cmd = [] + [cmdlist_to_tuple(c) for c in cmd] else: self.cmd = cmdlist_to_tuple(cmd) Debug.msg(3, "Layer.SetCmd(): cmd='%s'" % self.GetCmd(string=True)) @@ -1055,8 +1051,7 @@ def GetRegion( env["GISRC"] = self.gisrc # do not update & shell style output - cmd = {} - cmd["flags"] = "ugpc" + cmd = {"flags": "ugpc"} if default: cmd["flags"] += "d" @@ -1656,10 +1651,7 @@ def GetOverlay(self, id, list=False): :return: overlay (list=False) :return: None (list=False) if no overlay or more overlays found """ - ovl = [] - for overlay in self.overlays: - if overlay.id == id: - ovl.append(overlay) + ovl = [overlay for overlay in self.overlays if overlay.id == id] if not list: if len(ovl) != 1: diff --git a/gui/wxpython/core/settings.py b/gui/wxpython/core/settings.py index 05c6579e636..f65664a5e9b 100644 --- a/gui/wxpython/core/settings.py +++ b/gui/wxpython/core/settings.py @@ -816,9 +816,9 @@ def _internalSettings(self): ) self.internalSettings["display"]["driver"]["choices"] = ["cairo", "png"] - self.internalSettings["display"]["statusbarMode"][ - "choices" - ] = None # set during MapFrame init + self.internalSettings["display"]["statusbarMode"]["choices"] = ( + None # set during MapFrame init + ) self.internalSettings["display"]["mouseWheelZoom"]["choices"] = ( _("Zoom and recenter"), _("Zoom to mouse cursor"), @@ -998,10 +998,7 @@ def SaveToFile(self, settings=None): raise GException(e) except Exception as e: raise GException( - _( - "Writing settings to file <%(file)s> failed." - "\n\nDetails: %(detail)s" - ) + _("Writing settings to file <%(file)s> failed.\n\nDetails: %(detail)s") % {"file": self.filePath, "detail": e} ) return self.filePath @@ -1230,12 +1227,13 @@ def GetDisplayVectSettings(): % UserSettings.Get(group="vectorLayer", key="point", subkey="size"), ) ) - types = [] - for ftype in ["point", "line", "boundary", "centroid", "area", "face"]: + types = [ + ftype + for ftype in ["point", "line", "boundary", "centroid", "area", "face"] if UserSettings.Get( group="vectorLayer", key="showType", subkey=[ftype, "enabled"] - ): - types.append(ftype) + ) + ] settings.append("type=%s" % ",".join(types)) if UserSettings.Get(group="vectorLayer", key="randomColors", subkey="enabled"): diff --git a/gui/wxpython/core/testsuite/test_gcmd.py b/gui/wxpython/core/testsuite/test_gcmd.py index 75d3bf5932b..9653eb5e5c7 100644 --- a/gui/wxpython/core/testsuite/test_gcmd.py +++ b/gui/wxpython/core/testsuite/test_gcmd.py @@ -17,7 +17,6 @@ def recv(self): class Recv_SomeTest(TestCase): - @xfail_windows def test_decode(self): """ diff --git a/gui/wxpython/core/toolboxes.py b/gui/wxpython/core/toolboxes.py index d3b50cb1ea5..da4a516e83c 100644 --- a/gui/wxpython/core/toolboxes.py +++ b/gui/wxpython/core/toolboxes.py @@ -149,10 +149,7 @@ def getMenudataFile(userRootFile, newFile, fallback): generateNew = True _debug( 2, - ( - "toolboxes.getMenudataFile: only one of the user " - "defined files" - ), + ("toolboxes.getMenudataFile: only one of the user defined files"), ) else: # if newer files -> generate new @@ -205,9 +202,7 @@ def getMenudataFile(userRootFile, newFile, fallback): try: xml = _getXMLString(tree.getroot()) - fh = open(menudataFile, "w") - fh.write(xml) - fh.close() + Path(menudataFile).write_text(xml) return menudataFile except Exception: _debug( diff --git a/gui/wxpython/core/utils.py b/gui/wxpython/core/utils.py index b04d2e73c12..a1722fec64b 100644 --- a/gui/wxpython/core/utils.py +++ b/gui/wxpython/core/utils.py @@ -12,6 +12,8 @@ @author Jachym Cepicky """ +from __future__ import annotations + import os import sys import platform @@ -21,6 +23,8 @@ import inspect import operator from string import digits +from typing import TYPE_CHECKING + from grass.script import core as grass from grass.script import task as gtask @@ -31,6 +35,11 @@ from core.globalvar import wxPythonPhoenix +if TYPE_CHECKING: + import wx + import PIL.Image + + def cmp(a, b): """cmp function""" return (a > b) - (a < b) @@ -217,11 +226,7 @@ def GetValidLayerName(name): cIdx = 0 retNameList = list(retName) for c in retNameList: - if ( - not (c >= "A" and c <= "Z") - and not (c >= "a" and c <= "z") - and not (c >= "0" and c <= "9") - ): + if not ("A" <= c <= "Z") and not ("a" <= c <= "z") and not ("0" <= c <= "9"): retNameList[cIdx] = "_" cIdx += 1 retName = "".join(retNameList) @@ -311,7 +316,8 @@ def ListOfMapsets(get="ordered"): mapsets_ordered.append(mapset) return mapsets_ordered - raise ValueError("Invalid value for 'get' parameter of ListOfMapsets()") + msg = "Invalid value for 'get' parameter of ListOfMapsets()" + raise ValueError(msg) def ListSortLower(list): @@ -849,8 +855,7 @@ def StoreEnvVariable(key, value=None, envFile=None): # update environmental variables if value is None: - if key in environ: - del environ[key] + environ.pop(key, None) else: environ[key] = value @@ -866,12 +871,10 @@ def StoreEnvVariable(key, value=None, envFile=None): return expCmd = "set" if windows else "export" - for key, value in environ.items(): - fd.write("%s %s=%s\n" % (expCmd, key, value)) + fd.writelines("%s %s=%s\n" % (expCmd, key, value) for key, value in environ.items()) # write also skipped lines - for line in lineSkipped: - fd.write(line + os.linesep) + fd.writelines(line + os.linesep for line in lineSkipped) fd.close() @@ -922,9 +925,8 @@ def SetAddOnPath(addonPath=None, key="PATH"): "white": (255, 255, 255), "yellow": (255, 255, 0), } -rgb2str = {} -for s, r in str2rgb.items(): - rgb2str[r] = s + +rgb2str = {r: s for s, r in str2rgb.items()} # ensure that gray value has 'gray' string and not 'grey' rgb2str[str2rgb["gray"]] = "gray" # purple is defined as nickname for violet in lib/gis @@ -978,9 +980,7 @@ def color_resolve(color): "d.polar": "polar", "d.legend.vect": "vectleg", } -ltype2command = {} -for cmd, ltype in command2ltype.items(): - ltype2command[ltype] = cmd +ltype2command = {ltype: cmd for cmd, ltype in command2ltype.items()} def GetGEventAttribsForHandler(method, event): @@ -1018,7 +1018,7 @@ def GetGEventAttribsForHandler(method, event): return kwargs, missing_args -def PilImageToWxImage(pilImage, copyAlpha=True): +def PilImageToWxImage(pilImage: PIL.Image.Image, copyAlpha: bool = True) -> wx.Image: """Convert PIL image to wx.Image Based on http://wiki.wxpython.org/WorkingWithImages @@ -1047,7 +1047,7 @@ def PilImageToWxImage(pilImage, copyAlpha=True): return wxImage -def autoCropImageFromFile(filename): +def autoCropImageFromFile(filename) -> wx.Image: """Loads image from file and crops it automatically. If PIL is not installed, it does not crop it. diff --git a/gui/wxpython/core/workspace.py b/gui/wxpython/core/workspace.py index dd460a68195..bd09a9354a6 100644 --- a/gui/wxpython/core/workspace.py +++ b/gui/wxpython/core/workspace.py @@ -1461,7 +1461,7 @@ def __writeNvizVector(self, data): self.indent -= 4 def __writeNvizState(self, view, iview, light, constants): - """ "Save Nviz properties (view, light) to workspace + """Save Nviz properties (view, light) to workspace :param view: Nviz view properties :param iview: Nviz internal view properties diff --git a/gui/wxpython/datacatalog/dialogs.py b/gui/wxpython/datacatalog/dialogs.py index aef9aead7bd..8aec78b4f5e 100644 --- a/gui/wxpython/datacatalog/dialogs.py +++ b/gui/wxpython/datacatalog/dialogs.py @@ -143,8 +143,7 @@ def _doLayout(self): dialogSizer.Add(optionsSizer, proportion=1, flag=wx.ALL | wx.EXPAND, border=10) helptext = StaticText( self.panel, - label="For more reprojection options," - " please see {module}".format( + label="For more reprojection options, please see {module}".format( module="r.proj" if self.etype == "raster" else "v.proj" ), ) diff --git a/gui/wxpython/datacatalog/toolbars.py b/gui/wxpython/datacatalog/toolbars.py index 03de4f350b3..c9c8e2281a9 100644 --- a/gui/wxpython/datacatalog/toolbars.py +++ b/gui/wxpython/datacatalog/toolbars.py @@ -112,7 +112,7 @@ def __init__(self, parent): def _toolbarData(self): """Returns toolbar data (name, icon, handler)""" # BaseIcons are a set of often used icons. It is possible - # to reuse icons in ./trunk/gui/icons/grass or add new ones there. + # to reuse icons in gui/icons/grass or add new ones there. return self._getToolbarData( ( ( diff --git a/gui/wxpython/datacatalog/tree.py b/gui/wxpython/datacatalog/tree.py index bf06fd11ab8..76aee7e5eff 100644 --- a/gui/wxpython/datacatalog/tree.py +++ b/gui/wxpython/datacatalog/tree.py @@ -1892,6 +1892,7 @@ def done(event): gs.try_remove(event.userData) for i in range(len(self.selected_layer)): + cmd: list[str] = [] if self.selected_layer[i].data["type"] == "raster": cmd = ["r.info"] elif self.selected_layer[i].data["type"] == "vector": diff --git a/gui/wxpython/dbmgr/base.py b/gui/wxpython/dbmgr/base.py index 20ddbc47f25..b0c76dd92cc 100644 --- a/gui/wxpython/dbmgr/base.py +++ b/gui/wxpython/dbmgr/base.py @@ -198,8 +198,7 @@ def LoadData(self, layer, columns=None, where=None, sql=None): GError( parent=self, message=_( - "Column <%(column)s> not found in " - "in the table <%(table)s>." + "Column <%(column)s> not found in in the table <%(table)s>." ) % {"column": col, "table": tableName}, ) @@ -1642,10 +1641,7 @@ def OnDataItemAdd(self, event): try: if cat in tlist.itemCatsMap.values(): raise ValueError( - _( - "Record with category number %d " - "already exists in the table." - ) + _("Record with category number %d already exists in the table.") % cat ) @@ -2227,9 +2223,11 @@ def ValidateSelectStatement(self, statement): tablelen = len(self.dbMgrData["mapDBInfo"].layers[self.selLayer]["table"]) - if statement[index + 1 : index + 6].lower() != "from " or statement[ - index + 6 : index + 6 + tablelen - ] != "%s" % (self.dbMgrData["mapDBInfo"].layers[self.selLayer]["table"]): + if ( + statement[index + 1 : index + 6].lower() != "from " + or statement[index + 6 : index + 6 + tablelen] + != "%s" % (self.dbMgrData["mapDBInfo"].layers[self.selLayer]["table"]) + ): return None if len(statement[index + 7 + tablelen :]) > 0: @@ -4008,17 +4006,18 @@ def Update(self, driver, database, table, column): return fd, sqlFilePath = tempfile.mkstemp(text=True) - sqlFile = open(sqlFilePath, "w") stats = ["count", "min", "max", "avg", "sum", "null"] - for fn in stats: - if fn == "null": - sqlFile.write( - "select count(*) from %s where %s is null;%s" - % (table, column, "\n") - ) - else: - sqlFile.write("select %s(%s) from %s;%s" % (fn, column, table, "\n")) - sqlFile.close() + with open(sqlFilePath, "w") as sqlFile: + for fn in stats: + if fn == "null": + sqlFile.write( + "select count(*) from %s where %s is null;%s" + % (table, column, "\n") + ) + else: + sqlFile.write( + "select %s(%s) from %s;%s" % (fn, column, table, "\n") + ) dataStr = RunCommand( "db.select", diff --git a/gui/wxpython/dbmgr/g.gui.dbmgr.html b/gui/wxpython/dbmgr/g.gui.dbmgr.html index 0416843d43b..114e36fd46a 100644 --- a/gui/wxpython/dbmgr/g.gui.dbmgr.html +++ b/gui/wxpython/dbmgr/g.gui.dbmgr.html @@ -74,7 +74,7 @@

    SEE ALSO

    AUTHORS

    -Martin Landa,
    FBK-irst (2007-2008), +Martin Landa, FBK-irst (2007-2008), Trento, Italy, and OSGeoREL at the Czech Technical University in Prague, Czech Republic
    Michael Barton, Arizona State University, USA
    diff --git a/gui/wxpython/dbmgr/vinfo.py b/gui/wxpython/dbmgr/vinfo.py index c32ac5572b4..94dda140406 100644 --- a/gui/wxpython/dbmgr/vinfo.py +++ b/gui/wxpython/dbmgr/vinfo.py @@ -120,9 +120,7 @@ def SelectByPoint(self, queryCoords, qdist): return None # process attributes - ret = {} - for key in ["Category", "Layer", "Table", "Id"]: - ret[key] = [] + ret = {key: [] for key in ["Category", "Layer", "Table", "Id"]} for record in data: if "Table" not in record: diff --git a/gui/wxpython/docs/wxGUI.components.html b/gui/wxpython/docs/wxGUI.components.html index 5dd0cb3b657..b1be3d092f5 100644 --- a/gui/wxpython/docs/wxGUI.components.html +++ b/gui/wxpython/docs/wxGUI.components.html @@ -1,6 +1,13 @@ +

    KEYWORDS

    + + +general, GUI + +

    DESCRIPTION

    + List of available wxGUI components: + +

    SEE ALSO

    + + + wxGUI + diff --git a/gui/wxpython/docs/wxGUI.html b/gui/wxpython/docs/wxGUI.html index fec5ced8c22..70643d6b24f 100644 --- a/gui/wxpython/docs/wxGUI.html +++ b/gui/wxpython/docs/wxGUI.html @@ -441,7 +441,7 @@

    Map Display Toolbar

  • Zoom to saved region. Zooms to previously saved named region.
  • Set computational region extent from display. - The computational region (the mapset's WIND file) + The computational region (the mapset's WIND file) is set to match the current display extent (does not change the resolution), see g.region.
  • Set computational region extent interactively. @@ -657,9 +657,9 @@

    Starting the GUI from command line

  • Alternatively, it may be defined in the main configuration file -($HOME/.grass8/rc on GNU/Linux and macOS, %APPDATA%\Roaming\GRASS8\rc -on MS Windows) using the GUI variable set to wxpython -(GUI: wxpython) or by the environmental variable GRASS_GUI. +($HOME/.grass8/rc on GNU/Linux and macOS, %APPDATA%\Roaming\GRASS8\rc +on MS Windows) using the GUI variable set to wxpython +(GUI: wxpython) or by the environmental variable GRASS_GUI. To start with a previously saved workspace file: @@ -670,7 +670,7 @@

    Starting the GUI from command line

    The user can also start GRASS from the shell command line with the wxGUI -specifying the --gui switch: +specifying the --gui switch:

     grass --gui
    @@ -680,14 +680,14 @@ 

    Starting the GUI from command line

    The GUI can be quit by selecting the 'File > Quit GRASS GIS' menu item which gives options to close only GUI or to quit GRASS GIS entirely if GRASS GIS is running with a command line (a shell in a terminal application). -Exiting the shell (typically by the exit command) ends the GRASS session +Exiting the shell (typically by the exit command) ends the GRASS session including any running GUIs.

    Background information

    wxGUI is a native Graphical User Interface (GUI) for GRASS GIS written in Python -using wxPython library. +using wxPython library.

    SEE ALSO

    diff --git a/gui/wxpython/docs/wxGUI.iscatt.html b/gui/wxpython/docs/wxGUI.iscatt.html index d1601eb1c0d..6e263d2d1fb 100644 --- a/gui/wxpython/docs/wxGUI.iscatt.html +++ b/gui/wxpython/docs/wxGUI.iscatt.html @@ -10,8 +10,8 @@

    DESCRIPTION

    Interactive Scatter Plot Tool allows analyzing group of raster maps. The tool is integrated into Supervised Classification Tool (see the screen shot below). -Also it is possible to launch it from Map Display Window (Analyze map -→ Interactive Scatter Plot Tool). +Also it is possible to launch it from Map Display Window (Analyze map +→ Interactive Scatter Plot Tool). The main idea of the tool is that everything is linked together (scatter plots together and mapwindow with the scatter plots). @@ -39,7 +39,7 @@

    TOOL CONTROLS LAYOUT

    If editing mode is activated (the green polygon tool in toolbar), the areas which were selected -in the scatter plots are highlighted. In the image you can see this area for scatter plot of bands B_6, B_7 inside the ellipse. +in the scatter plots are highlighted. In the image you can see this area for scatter plot of bands B_6, B_7 inside the ellipse. Opacity and color of the selected area can be set in settings. The area corresponds to the active class (in this case clouds). Selected areas are subset of areas, which belongs to the category.

    @@ -71,7 +71,7 @@

    SEE ALSO

    wxGUI, wxGUI components, - r.rescale
    + r.rescale

    diff --git a/gui/wxpython/docs/wxGUI.modules.html b/gui/wxpython/docs/wxGUI.modules.html index 44aeedace61..7e8125f7443 100644 --- a/gui/wxpython/docs/wxGUI.modules.html +++ b/gui/wxpython/docs/wxGUI.modules.html @@ -1,5 +1,10 @@ + +

    KEYWORDS

    + +general, GUI +

    DESCRIPTION

    GRASS GIS functionality is organized into modules, which are standalone programs @@ -76,7 +81,7 @@

    Current working directory

    It can be changed in wxGUI menu Settings - GRASS working environment - Change working directory, -or by typing cd and pressing Enter in the wxGUI Command console. +or by typing cd and pressing Enter in the wxGUI Command console. If the working directory is changed to a directory where the input files are, then it is enough to specify just the name of the file instead of the full path. diff --git a/gui/wxpython/docs/wxGUI.nviz.html b/gui/wxpython/docs/wxGUI.nviz.html index 3c0767f9598..df62a7a8d23 100644 --- a/gui/wxpython/docs/wxGUI.nviz.html +++ b/gui/wxpython/docs/wxGUI.nviz.html @@ -395,10 +395,8 @@

    SEE ALSO

    (especially various video tutorials). -

    - +

    Command-line module m.nviz.image. -

    AUTHORS

    diff --git a/gui/wxpython/docs/wxGUI.toolboxes.html b/gui/wxpython/docs/wxGUI.toolboxes.html index ccbf4f6415c..8679a8eab31 100644 --- a/gui/wxpython/docs/wxGUI.toolboxes.html +++ b/gui/wxpython/docs/wxGUI.toolboxes.html @@ -1,5 +1,10 @@ + +

    KEYWORDS

    + +general, GUI +

    DESCRIPTION

    The Toolboxes is a way to customize items in wxGUI @@ -15,22 +20,22 @@

    DESCRIPTION

    -Toolboxes are configured through two XML files (main_menu.xml and -toolboxes.xml) located in your user home -GRASS directory, subdirectory toolboxes - ($HOME/.grass8/toolboxes/ on UNIX). +Toolboxes are configured through two XML files (main_menu.xml and +toolboxes.xml) located in your user home +GRASS directory, subdirectory toolboxes + ($HOME/.grass8/toolboxes/ on UNIX). Currently, there is no GUI front-end for toolboxes, however only simple editing of text files is needed.

    -

    Brief description of file main_menu.xml

    +

    Brief description of file main_menu.xml

    This file represents the main menu (File, Settings, Raster, ...). By modifying this file you show and hide menu items which are -represented by subtoolbox tag.

    +represented by subtoolbox tag.

    -

    Tag user-toolboxes-list is interpreted as a menu containing a list of all user-defined toolboxes. If not needed it can be removed.

    +

    Tag user-toolboxes-list is interpreted as a menu containing a list of all user-defined toolboxes. If not needed it can be removed.

    -

    Following lines can be copied to .grass8/toolboxes/main_menu.xml +

    Following lines can be copied to .grass8/toolboxes/main_menu.xml and by removing, adding or reordering lines users can change the main menu items. See further examples.

    @@ -52,16 +57,16 @@

    Brief description of file main_menu.xml

    </toolbox>
    -

    Brief description of file toolboxes.xml

    +

    Brief description of file toolboxes.xml

    This file contains structure and description of individual toolboxes. Note that both Raster and e.g. Query raster maps are individual toolboxes although one contains the other. -Tag toolbox contains subtoolbox tags +Tag toolbox contains subtoolbox tags which are defined later in the file. These nested toolboxes are linked -through name attribute.

    +through name attribute.

    -

    Apart from subtoolbox tag, tag toolbox can contain individual items (modules) +

    Apart from subtoolbox tag, tag toolbox can contain individual items (modules) and separators (for visual separation in the menu tree).

    @@ -94,9 +99,9 @@ 

    Brief description of file toolboxes.xml

    To redefine a toolbox (or use it as a template), -copy specific part of file grass7/gui/wxpython/xml/toolboxes.xml +copy specific part of file grass7/gui/wxpython/xml/toolboxes.xml from GRASS installation to a new file in user home -(.grass8/toolboxes/toolboxes.xml) and edit it. +(.grass8/toolboxes/toolboxes.xml) and edit it. Rename this new toolbox.

    EXAMPLES

    @@ -104,7 +109,7 @@

    EXAMPLES

    Hiding menu items

    If we are for example working only with raster data, we can hide menu items Vector and Database. -The file main_menu.xml then contains the following lines +The file main_menu.xml then contains the following lines where we omitted the two toolboxes:

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

    Creating custom toolbox

    In this example we create a new toolbox Favorites containing existing GRASS module and toolbox, custom module created by the user and addon module. -The toolboxes.xml file contains following lines:

    +The toolboxes.xml file contains following lines:

     <?xml version="1.0" encoding="UTF-8"?>
    @@ -157,7 +162,7 @@ 

    Creating custom toolbox

    Optionally, we can add this toolbox to the main menu items. -The main_menu.xml file contains following lines:

    +The main_menu.xml file contains following lines:

     <?xml version="1.0" encoding="UTF-8"?>
    @@ -178,7 +183,7 @@ 

    Creating custom toolbox

    </toolbox>
    -

    If we have user-toolboxes-list tag in the main_menu.xml file, +

    If we have user-toolboxes-list tag in the main_menu.xml file, our custom toolbox will be listed in the automatically added Toolboxes main menu item. The screenshot shows the resulting menu:

    @@ -188,8 +193,8 @@

    Creating custom toolbox

    NOTES

    After the first start of wxGUI with custom toolboxes, -.grass/toolboxes directory will contain file -menudata.xml which is auto-generated and should not be edited.

    +.grass/toolboxes directory will contain file +menudata.xml which is auto-generated and should not be edited.

    SEE ALSO

    diff --git a/gui/wxpython/docs/wxGUI.vnet.html b/gui/wxpython/docs/wxGUI.vnet.html index c1b8a51831b..f6be4446e3f 100644 --- a/gui/wxpython/docs/wxGUI.vnet.html +++ b/gui/wxpython/docs/wxGUI.vnet.html @@ -8,7 +8,7 @@

    KEYWORDS

    DESCRIPTION

    Vector Network Analysis Tool is graphical front-end -for v.net* modules. It allows perform network analysis +for v.net* modules. It allows perform network analysis directly in wxGUI without need to use command line. The tool can be launched from Layer Manager menu Vector → Network analysis → Vector network analysis diff --git a/gui/wxpython/gcp/manager.py b/gui/wxpython/gcp/manager.py index fc8fa869781..6761cead483 100644 --- a/gui/wxpython/gcp/manager.py +++ b/gui/wxpython/gcp/manager.py @@ -27,49 +27,54 @@ @author Support for GraphicsSet added by Stepan Turek (2012) """ +from __future__ import annotations + import os -import sys import shutil +import sys from copy import copy +from typing import TYPE_CHECKING import wx -from wx.lib.mixins.listctrl import ColumnSorterMixin, ListCtrlAutoWidthMixin import wx.lib.colourselect as csel - from core import globalvar +from wx.lib.mixins.listctrl import ColumnSorterMixin, ListCtrlAutoWidthMixin -if globalvar.wxPythonPhoenix: +if globalvar.wxPythonPhoenix or TYPE_CHECKING: from wx import adv as wiz else: from wx import wizard as wiz import grass.script as gs +# isort: split from core import utils +from core.gcmd import GError, GMessage, GWarning, RunCommand +from core.giface import Notification from core.render import Map -from gui_core.gselect import Select, LocationSelect, MapsetSelect -from gui_core.dialogs import GroupDialog -from gui_core.mapdisp import FrameMixin -from core.gcmd import RunCommand, GMessage, GError, GWarning from core.settings import UserSettings from gcp.mapdisplay import MapPanel -from core.giface import Notification +from gui_core.dialogs import GroupDialog +from gui_core.gselect import LocationSelect, MapsetSelect, Select +from gui_core.mapdisp import FrameMixin from gui_core.wrap import ( - SpinCtrl, + BitmapFromImage, Button, - StaticText, - StaticBox, CheckListBox, - TextCtrl, - Menu, - ListCtrl, - BitmapFromImage, CheckListCtrlMixin, + ListCtrl, + Menu, + SpinCtrl, + StaticBox, + StaticText, + TextCtrl, ) - from location_wizard.wizard import GridBagSizerTitledPage as TitledPage +if TYPE_CHECKING: + from wx.adv import WizardEvent + # # global variables # @@ -81,22 +86,13 @@ def getSmallUpArrowImage(): - stream = open(os.path.join(globalvar.IMGDIR, "small_up_arrow.png"), "rb") - try: - img = wx.Image(stream) - finally: - stream.close() - return img + with open(os.path.join(globalvar.IMGDIR, "small_up_arrow.png"), "rb") as stream: + return wx.Image(stream) def getSmallDnArrowImage(): - stream = open(os.path.join(globalvar.IMGDIR, "small_down_arrow.png"), "rb") - try: - img = wx.Image(stream) - finally: - stream.close() - stream.close() - return img + with open(os.path.join(globalvar.IMGDIR, "small_down_arrow.png"), "rb") as stream: + return wx.Image(stream) class GCPWizard: @@ -118,16 +114,13 @@ def __init__(self, parent, giface): # self.target_gisrc = os.environ["GISRC"] self.gisrc_dict = {} - try: - f = open(self.target_gisrc) + with open(self.target_gisrc) as f: for line in f: line = line.replace("\n", "").strip() if len(line) < 1: continue key, value = line.split(":", 1) self.gisrc_dict[key.strip()] = value.strip() - finally: - f.close() self.currentlocation = self.gisrc_dict["LOCATION_NAME"] self.currentmapset = self.gisrc_dict["MAPSET"] @@ -340,13 +333,10 @@ def SetSrcEnv(self, location, mapset): self.source_gisrc = utils.GetTempfile() - try: - f = open(self.source_gisrc, mode="w") - for line in self.gisrc_dict.items(): - f.write(line[0] + ": " + line[1] + "\n") - finally: - f.close() - + with open(self.source_gisrc, mode="w") as f: + f.writelines( + line[0] + ": " + line[1] + "\n" for line in self.gisrc_dict.items() + ) return True def SwitchEnv(self, grc): @@ -510,13 +500,10 @@ def OnMapset(self, event): if not wx.FindWindowById(wx.ID_FORWARD).IsEnabled(): wx.FindWindowById(wx.ID_FORWARD).Enable(True) - def OnPageChanging(self, event=None): + def OnPageChanging(self, event: WizardEvent | None = None) -> None: if event.GetDirection() and (self.xylocation == "" or self.xymapset == ""): GMessage( - _( - "You must select a valid location " - "and mapset in order to continue" - ), + _("You must select a valid location and mapset in order to continue"), parent=self, ) event.Veto() @@ -524,7 +511,7 @@ def OnPageChanging(self, event=None): self.parent.SetSrcEnv(self.xylocation, self.xymapset) - def OnEnterPage(self, event=None): + def OnEnterPage(self, event: WizardEvent | None = None) -> None: if self.xylocation == "" or self.xymapset == "": wx.FindWindowById(wx.ID_FORWARD).Enable(False) else: @@ -684,7 +671,7 @@ def OnVGroup(self, event): def OnExtension(self, event): self.extension = self.ext_txt.GetValue() - def OnPageChanging(self, event=None): + def OnPageChanging(self, event: WizardEvent | None = None) -> None: if event.GetDirection() and self.xygroup == "": GMessage( _("You must select a valid image/map group in order to continue"), @@ -701,7 +688,7 @@ def OnPageChanging(self, event=None): event.Veto() return - def OnEnterPage(self, event=None): + def OnEnterPage(self, event: WizardEvent | None = None) -> None: global maptype self.groupList = [] @@ -888,7 +875,7 @@ def OnTgtVectSelection(self, event): tgt_map["vector"] = self.tgtvectselection.GetValue() - def OnPageChanging(self, event=None): + def OnPageChanging(self, event: WizardEvent | None = None) -> None: global src_map, tgt_map if event.GetDirection() and (src_map == ""): @@ -900,7 +887,7 @@ def OnPageChanging(self, event=None): self.parent.SwitchEnv("target") - def OnEnterPage(self, event=None): + def OnEnterPage(self, event: WizardEvent | None = None) -> None: global maptype, src_map, tgt_map self.srcselection.SetElementList(maptype) @@ -995,10 +982,11 @@ def GetWebServiceLayers(self, ltype=("wms"), name=None): } :return: None when web service map layer name doesn't exist """ - layers = {} - for layer in self.parent._giface.GetLayerList(): - if layer.type in ltype: - layers[str(layer)] = {"type": layer.type, "cmd": layer.cmd} + layers = { + str(layer): {"type": layer.type, "cmd": layer.cmd} + for layer in self.parent._giface.GetLayerList() + if layer.type in ltype + } if name: return layers.get(name) return layers @@ -1026,6 +1014,7 @@ def __init__( Map=None, lmgr=None, ): + # pylint: disable=super-init-not-called; See InitMapDisplay() self.grwiz = grwiz # GR Wizard self._giface = giface @@ -1401,9 +1390,8 @@ def SetSettings(self): font = self.GetFont() font.SetPointSize(int(spx) + 2) - textProp = {} - textProp["active"] = True - textProp["font"] = font + textProp = {"active": True, "font": font} + self.pointsToDrawSrc.SetPropertyVal("text", textProp) self.pointsToDrawTgt.SetPropertyVal("text", copy(textProp)) @@ -1509,41 +1497,41 @@ def SaveGCPs(self, event): """ self.GCPcount = 0 try: - f = open(self.file["points"], mode="w") - # use os.linesep or '\n' here ??? - f.write("# Ground Control Points File\n") - f.write("# \n") - f.write("# target location: " + self.currentlocation + "\n") - f.write("# target mapset: " + self.currentmapset + "\n") - f.write("#\tsource\t\ttarget\t\tstatus\n") - f.write("#\teast\tnorth\teast\tnorth\t(1=ok, 0=ignore)\n") - f.write( - "#----------------------- ----------------------- ---------------\n" # noqa: E501 - ) - - for index in range(self.list.GetItemCount()): - if self.list.IsItemChecked(index): - check = "1" - self.GCPcount += 1 - else: - check = "0" - coord0 = self.list.GetItem(index, 1).GetText() - coord1 = self.list.GetItem(index, 2).GetText() - coord2 = self.list.GetItem(index, 3).GetText() - coord3 = self.list.GetItem(index, 4).GetText() + with open(self.file["points"], mode="w") as f: + # use os.linesep or '\n' here ??? + f.write("# Ground Control Points File\n") + f.write("# \n") + f.write("# target location: " + self.currentlocation + "\n") + f.write("# target mapset: " + self.currentmapset + "\n") + f.write("#\tsource\t\ttarget\t\tstatus\n") + f.write("#\teast\tnorth\teast\tnorth\t(1=ok, 0=ignore)\n") f.write( - coord0 - + " " - + coord1 - + " " - + coord2 - + " " - + coord3 - + " " - + check - + "\n" + "#----------------------- ----------------------- ---------------\n" # noqa: E501 ) + for index in range(self.list.GetItemCount()): + if self.list.IsItemChecked(index): + check = "1" + self.GCPcount += 1 + else: + check = "0" + coord0 = self.list.GetItem(index, 1).GetText() + coord1 = self.list.GetItem(index, 2).GetText() + coord2 = self.list.GetItem(index, 3).GetText() + coord3 = self.list.GetItem(index, 4).GetText() + f.write( + coord0 + + " " + + coord1 + + " " + + coord2 + + " " + + coord3 + + " " + + check + + "\n" + ) + except OSError as err: GError( parent=self, @@ -1557,8 +1545,6 @@ def SaveGCPs(self, event): ) return - f.close() - # if event != None save also to backup file if event: shutil.copy(self.file["points"], self.file["points_bak"]) @@ -1582,27 +1568,26 @@ def ReadGCPs(self): GError(parent=self, message=_("target mapwin not defined")) try: - f = open(self.file["points"]) GCPcnt = 0 + with open(self.file["points"]) as f: + for line in f: + if line[0] == "#" or line == "": + continue + line = line.replace("\n", "").strip() + coords = list(map(float, line.split())) + if coords[4] == 1: + check = True + self.GCPcount += 1 + else: + check = False - for line in f: - if line[0] == "#" or line == "": - continue - line = line.replace("\n", "").strip() - coords = list(map(float, line.split())) - if coords[4] == 1: - check = True - self.GCPcount += 1 - else: - check = False - - self.AddGCP(event=None) - self.SetGCPData("source", (coords[0], coords[1]), sourceMapWin) - self.SetGCPData("target", (coords[2], coords[3]), targetMapWin) - index = self.list.GetSelected() - if index != wx.NOT_FOUND: - self.list.CheckItem(index, check) - GCPcnt += 1 + self.AddGCP(event=None) + self.SetGCPData("source", (coords[0], coords[1]), sourceMapWin) + self.SetGCPData("target", (coords[2], coords[3]), targetMapWin) + index = self.list.GetSelected() + if index != wx.NOT_FOUND: + self.list.CheckItem(index, check) + GCPcnt += 1 except OSError as err: GError( @@ -1617,8 +1602,6 @@ def ReadGCPs(self): ) return - f.close() - if GCPcnt == 0: # 3 gcp is minimum for i in range(3): @@ -1824,16 +1807,13 @@ def OnGeorect(self, event): self.grwiz.SwitchEnv("source") # make list of vectors to georectify from VREF - f = open(self.file["vgrp"]) vectlist = [] - try: + with open(self.file["vgrp"]) as f: for vect in f: vect = vect.strip("\n") if len(vect) < 1: continue vectlist.append(vect) - finally: - f.close() # georectify each vector in VREF using v.rectify for vect in vectlist: @@ -2037,8 +2017,7 @@ def RMSError(self, xygroup, order): GError( parent=self, message=_( - "Could not calculate RMS Error.\n" - "Possible error with m.transform." + "Could not calculate RMS Error.\nPossible error with m.transform." ), ) return @@ -2112,8 +2091,7 @@ def GetNewExtent(self, region, map=None): "w": 0.0, } - try: - f = open(coord_file, mode="w") + with open(coord_file, mode="w") as f: # NW corner f.write(str(region["e"]) + " " + str(region["n"]) + "\n") # NE corner @@ -2122,8 +2100,6 @@ def GetNewExtent(self, region, map=None): f.write(str(region["w"]) + " " + str(region["n"]) + "\n") # SE corner f.write(str(region["w"]) + " " + str(region["s"]) + "\n") - finally: - f.close() # save GCPs to points file to make sure that all checked GCPs are used self.SaveGCPs(None) @@ -2173,8 +2149,7 @@ def GetNewExtent(self, region, map=None): GError( parent=self, message=_( - "Could not calculate new extends.\n" - "Possible error with m.transform." + "Could not calculate new extends.\nPossible error with m.transform." ), ) return @@ -2687,8 +2662,7 @@ def __init__( self.listMap = CheckListBox(parent=self, id=wx.ID_ANY, choices=vectlist) if os.path.isfile(self.vgrpfile): - f = open(self.vgrpfile) - try: + with open(self.vgrpfile) as f: checked = [] for line in f: line = line.replace("\n", "") @@ -2696,8 +2670,6 @@ def __init__( continue checked.append(line.split("@")[0]) self.listMap.SetCheckedStrings(checked) - finally: - f.close() line = wx.StaticLine( parent=self, id=wx.ID_ANY, size=(20, -1), style=wx.LI_HORIZONTAL @@ -2761,12 +2733,8 @@ def MakeVGroup(self): if not os.path.exists(dirname): os.makedirs(dirname) - f = open(self.vgrpfile, mode="w") - try: - for vect in vgrouplist: - f.write(vect + "\n") - finally: - f.close() + with open(self.vgrpfile, mode="w") as f: + f.writelines(vect + "\n" for vect in vgrouplist) class EditGCP(wx.Dialog): @@ -2886,11 +2854,11 @@ def __init__( size=wx.DefaultSize, style=wx.DEFAULT_DIALOG_STYLE, ): - wx.Dialog.__init__(self, parent, id, title, pos, size, style) """ Dialog to set profile text options: font, title and font size, axis labels and font size """ + wx.Dialog.__init__(self, parent, id, title, pos, size, style) # # initialize variables # @@ -3285,8 +3253,7 @@ def OnSDFactor(self, event): GError( parent=self, message=_( - "RMS threshold factor is < 1\n" - "Too many points might be highlighted" + "RMS threshold factor is < 1\nToo many points might be highlighted" ), ) diff --git a/gui/wxpython/gmodeler/dialogs.py b/gui/wxpython/gmodeler/dialogs.py index ba966ea6a53..4a58c70a3cd 100644 --- a/gui/wxpython/gmodeler/dialogs.py +++ b/gui/wxpython/gmodeler/dialogs.py @@ -469,8 +469,7 @@ def _getOptions(self): GError( parent=self.parent, message=_( - "Relation doesn't point to GRASS command.\n" - "Unable to add relation." + "Relation doesn't point to GRASS command.\nUnable to add relation." ), ) return items @@ -978,9 +977,7 @@ def Populate(self, data): bId = action.GetBlockId() bId = _("No") if not bId else _("Yes") options = action.GetParameterizedParams() - params = [] - for f in options["flags"]: - params.append("-{0}".format(f["name"])) + params = ["-{0}".format(f["name"]) for f in options["flags"]] for p in options["params"]: params.append(p["name"]) diff --git a/gui/wxpython/gmodeler/g.gui.gmodeler.html b/gui/wxpython/gmodeler/g.gui.gmodeler.html index dae5aefea52..9a31c63c85c 100644 --- a/gui/wxpython/gmodeler/g.gui.gmodeler.html +++ b/gui/wxpython/gmodeler/g.gui.gmodeler.html @@ -46,7 +46,7 @@

    DESCRIPTION

    Main dialog

    The Graphical Modeler can be launched from the Layer Manager menu -File -> Graphical modeler or from the main +File -> Graphical modeler or from the main toolbar icon. It's also available as stand-alone module g.gui.gmodeler. @@ -152,8 +152,8 @@

    Components of models

    EXAMPLE

    -In this example the zipcodes_wake vector data and the -elev_state_500m raster data from the North Carolina +In this example the zipcodes_wake vector data and the +elev_state_500m raster data from the North Carolina sample dataset (original raster and vector data) are used to calculate average elevation for every @@ -209,7 +209,7 @@

    Defining the workflow in the Graphical Modeler

    Managing model parameters

    All used modules can be parameterized in the model. That causes launching the dialog with input options for model after the model is run. In this example, -input layers (zipcodes_wake vector map and elev_state_500m +input layers (zipcodes_wake vector map and elev_state_500m raster map) are parameterized. Parameterized elements show their diagram border slightly thicker than those of unparameterized elements. @@ -260,7 +260,7 @@

    Managing model parameters

    Managing model properties

    When the user wants to run the model again with the same data or the same names, it is -necessary to use --overwrite option. It will cause maps with identical +necessary to use --overwrite option. It will cause maps with identical names to be overwritten. Instead of setting it for every module separately it is handy to change the Model Property settings globally. This dialog includes also metadata settings, where model name, model description @@ -283,9 +283,9 @@

    Defining variables

    Then it is not necessary to set any parameters for input data. The dialog with variable settings is automatically displayed after the model is run. So, instead of model parameters -(e.g. r.import a v.import, see the Figure +(e.g. r.import a v.import, see the Figure Run model dialog above) -there are Variables. +there are Variables.
    @@ -295,13 +295,13 @@

    Defining variables

    -The key point is the usage of % before the substituting variable and -settings in the Variables dialog. For example, in the case of a model variable -raster that points to an input file path and which value is required to be +The key point is the usage of % before the substituting variable and +settings in the Variables dialog. For example, in the case of a model variable +raster that points to an input file path and which value is required to be used as one of inputs for a particular model, it should be specified in the -Variables dialog with its respective name (raster), data type, +Variables dialog with its respective name (raster), data type, default value and description. Then it should be set in the module dialog as -input called %raster. +input called %raster.

    @@ -320,13 +320,13 @@

    Defining variables

    Saving the model file

    Finally, the model settings can be stored as a GRASS GIS Model file with -*.gxm extension. The advantage is that it can be shared as a +*.gxm extension. The advantage is that it can be shared as a reusable workflow that may be run also by other users with different data.

    For example, this model can later be used to calculate the average precipitation -for every administrative region in Slovakia using the precip raster data from +for every administrative region in Slovakia using the precip raster data from Slovakia precipitation dataset and administration boundaries of Slovakia from Slovak Geoportal @@ -335,7 +335,7 @@

    Saving the model file

    Handling intermediate data

    There can be some data in a model that did not exist before the process and that it is not worth it to maintain after the process executes. They can -be described as being Intermediate by single clicking using the right +be described as being Intermediate by single clicking using the right mouse button, see figure below. All such data should be deleted following model completion. The boundary of intermediate component is dotted line. @@ -348,7 +348,7 @@

    Handling intermediate data

    Using the Script editor

    By using the Script editor in the Graphical Modeler, the user can add Python code and then -run it with Run button or just save it as a Python script *.py. +run it with Run button or just save it as a Python script *.py. The result is shown in the Figure below:
    @@ -388,21 +388,21 @@

    Using the Script editor

    By default GRASS script package API is used -(grass.script.core.run_command()). This can be changed in the +(grass.script.core.run_command()). This can be changed in the settings. Alternatively also PyGRASS API is supported -(grass.pygrass.modules.Module). +(grass.pygrass.modules.Module).

    Defining loops

    In the example below the MODIS MOD13Q1 (NDVI) satellite data products are used in a loop. The original data are stored as coded integer values that need to be multiplied by the -value 0.0001 to represent real ndvi values. Moreover, GRASS GIS -provides a predefined color table called ndvi to represent ndvi data. +value 0.0001 to represent real ndvi values. Moreover, GRASS GIS +provides a predefined color table called ndvi to represent ndvi data. In this case it is not necessary to work with every image separately.
    The Graphical Modeler is an appropriate tool to -process data in an effective way using loop and variables (%map for a -particular MODIS image in mapset and %ndvi for original data name suffix). +process data in an effective way using loop and variables (%map for a +particular MODIS image in mapset and %ndvi for original data name suffix). After the loop component is added to model, it is necessary to define series of maps with required settings of map type, mapset, etc. diff --git a/gui/wxpython/gmodeler/model.py b/gui/wxpython/gmodeler/model.py index 2a4673f511a..ee846c62653 100644 --- a/gui/wxpython/gmodeler/model.py +++ b/gui/wxpython/gmodeler/model.py @@ -39,6 +39,7 @@ import time import xml.etree.ElementTree as ET +from pathlib import Path from xml.sax import saxutils import wx @@ -100,12 +101,7 @@ def GetItems(self, objType=None): if not objType: return self.items - result = [] - for item in self.items: - if isinstance(item, objType): - result.append(item) - - return result + return [item for item in self.items if isinstance(item, objType)] def GetItem(self, aId, objType=None): """Get item of given id @@ -325,7 +321,8 @@ def LoadModel(self, filename): try: gxmXml = ProcessModelFile(ET.parse(filename)) except Exception as e: - raise GException("{}".format(e)) + msg = "{}".format(e) + raise GException(msg) if self.canvas: win = self.canvas.parent @@ -546,11 +543,8 @@ def _substituteFile(self, item, params=None, checkOnly=False): for finput in self.fileInput: # read lines - fd = open(finput) - try: - data = self.fileInput[finput] = fd.read() - finally: - fd.close() + data = Path(finput).read_text() + self.fileInput[finput] = data # substitute variables write = False @@ -583,11 +577,7 @@ def _substituteFile(self, item, params=None, checkOnly=False): if not checkOnly: if write: - fd = open(finput, "w") - try: - fd.write(data) - finally: - fd.close() + Path(finput).write_text(data) else: self.fileInput[finput] = None @@ -682,11 +672,12 @@ def Run(self, log, onDone, parent=None): GError(parent=parent, message="\n".join(err)) return - err = [] - for key, item in params.items(): - for p in item["params"]: - if p.get("value", "") == "": - err.append((key, p.get("name", ""), p.get("description", ""))) + err = [ + (key, p.get("name", ""), p.get("description", "")) + for key, item in params.items() + for p in item["params"] + if p.get("value", "") == "" + ] if err: GError( parent=parent, @@ -989,11 +980,7 @@ def GetBlockId(self): :return: list of ids """ - ret = [] - for mo in self.inBlock: - ret.append(mo.GetId()) - - return ret + return [mo.GetId() for mo in self.inBlock] class ModelAction(ModelObject, ogl.DividedShape): @@ -1408,20 +1395,14 @@ def OnDraw(self, dc): def GetLog(self, string=True): """Get logging info""" - name = [] - for rel in self.GetRelations(): - name.append(rel.GetLabel()) + name = [rel.GetLabel() for rel in self.GetRelations()] if name: return "/".join(name) + "=" + self.value + " (" + self.prompt + ")" return self.value + " (" + self.prompt + ")" def GetLabel(self): """Get list of names""" - name = [] - for rel in self.GetRelations(): - name.append(rel.GetLabel()) - - return name + return [rel.GetLabel() for rel in self.GetRelations()] def GetPrompt(self): """Get prompt""" @@ -1527,9 +1508,7 @@ def _setPen(self): def SetLabel(self): """Update text""" self.ClearText() - name = [] - for rel in self.GetRelations(): - name.append(rel.GetLabel()) + name = [rel.GetLabel() for rel in self.GetRelations()] self.AddText("/".join(name)) if self.value: self.AddText(self.value) @@ -1550,7 +1529,8 @@ def GetDisplayCmd(self): elif self.prompt == "vector": cmd.append("d.vect") else: - raise GException("Unsupported display prompt: {}".format(self.prompt)) + msg = "Unsupported display prompt: {}".format(self.prompt) + raise GException(msg) cmd.append("map=" + self.value) @@ -1793,12 +1773,7 @@ def Update(self): def GetItems(self, items): """Get sorted items by id""" - result = [] - for item in items: - if item.GetId() in self.itemIds: - result.append(item) - - return result + return [item for item in items if item.GetId() in self.itemIds] def SetItems(self, items): """Set items (id)""" @@ -2729,9 +2704,9 @@ def _writeActinia(self): self.fd.write( f"""{{ -{' ' * self.indent * 1}"id": "model", -{' ' * self.indent * 1}"description": "{'""'.join(description.splitlines())}", -{' ' * self.indent * 1}"version": "1", +{" " * self.indent * 1}"id": "model", +{" " * self.indent * 1}"description": "{'""'.join(description.splitlines())}", +{" " * self.indent * 1}"version": "1", """ ) @@ -2748,12 +2723,12 @@ def _writeActinia(self): if parameterized is True: self.fd.write(f'{" " * self.indent * 1}"template": {{\n') self.fd.write( - f"""{' ' * self.indent * 2}"list": [ + f"""{" " * self.indent * 2}"list": [ """ ) else: self.fd.write( - f"""{' ' * self.indent}"list": [ + f"""{" " * self.indent}"list": [ """ ) @@ -2806,7 +2781,6 @@ def _getPythonActionCmd(self, item, task, cmdIndent, variables={}): value = p.get("value", None) if (name and value) or (name in parameterizedParams): - if name in parameterizedParams: parameterizedParam = self._getParamName(name, item) default_val = p.get("value", "") @@ -3333,9 +3307,7 @@ def _writePython(self): # %module # % description: {description} # %end -""".format( - description=" ".join(properties["description"].splitlines()) - ) +""".format(description=" ".join(properties["description"].splitlines())) ) modelItems = self.model.GetItems(ModelAction) @@ -3729,11 +3701,7 @@ def _createPage(self, name, params): def GetErrors(self): """Check for errors, get list of messages""" - errList = [] - for task in self.tasks: - errList += task.get_cmd_error() - - return errList + return [task.get_cmd_error() for task in self.tasks] def DeleteIntermediateData(self) -> bool: """Check if to delete intermediate data""" diff --git a/gui/wxpython/gmodeler/panels.py b/gui/wxpython/gmodeler/panels.py index fbdc2048620..c05ce041047 100644 --- a/gui/wxpython/gmodeler/panels.py +++ b/gui/wxpython/gmodeler/panels.py @@ -401,11 +401,7 @@ def OnModelDone(self, event): if not data: continue - fd = open(finput, "w") - try: - fd.write(data) - finally: - fd.close() + Path(finput).write_text(data) del self.model.fileInput # delete intermediate data @@ -671,31 +667,27 @@ def WriteModelFile(self, filename): :return: False on failure """ self.ModelChanged(False) - tmpfile = tempfile.TemporaryFile(mode="w+") - try: - WriteModelFile(fd=tmpfile, model=self.model) - except Exception: - GError( - parent=self, message=_("Writing current settings to model file failed.") - ) - return False - - try: - mfile = open(filename, "w") - tmpfile.seek(0) - for line in tmpfile.readlines(): - mfile.write(line) - except OSError: - wx.MessageBox( - parent=self, - message=_("Unable to open file <%s> for writing.") % filename, - caption=_("Error"), - style=wx.OK | wx.ICON_ERROR | wx.CENTRE, - ) - return False - - mfile.close() - + with tempfile.TemporaryFile(mode="w+") as tmpfile: + try: + WriteModelFile(fd=tmpfile, model=self.model) + except Exception: + GError( + parent=self, + message=_("Writing current settings to model file failed."), + ) + return False + try: + with open(filename, "w") as mfile: + tmpfile.seek(0) + mfile.writelines(tmpfile.readlines()) + except OSError: + wx.MessageBox( + parent=self, + message=_("Unable to open file <%s> for writing.") % filename, + caption=_("Error"), + style=wx.OK | wx.ICON_ERROR | wx.CENTRE, + ) + return False return True def DefineLoop(self, loop): @@ -1702,17 +1694,15 @@ def RefreshScript(self): if ret == wx.ID_NO: return False - fd = tempfile.TemporaryFile(mode="r+") grassAPI = UserSettings.Get(group="modeler", key="grassAPI", subkey="selection") - self.write_object( - fd, - self.parent.GetModel(), - grassAPI="script" if grassAPI == 0 else "pygrass", - ) - - fd.seek(0) - self.body.SetText(fd.read()) - fd.close() + with tempfile.TemporaryFile(mode="r+") as fd: + self.write_object( + fd, + self.parent.GetModel(), + grassAPI="script" if grassAPI == 0 else "pygrass", + ) + fd.seek(0) + self.body.SetText(fd.read()) self.body.modified = False @@ -1765,18 +1755,13 @@ def SaveAs(self, force=False): dlg.Destroy() - fd = open(filename, "w") - try: + with open(filename, "w") as fd: if force: self.write_object(fd, self.parent.GetModel()) else: fd.write(self.body.GetText()) - finally: - fd.close() - # executable file os.chmod(filename, stat.S_IRWXU | stat.S_IWUSR) - return filename def OnRun(self, event): diff --git a/gui/wxpython/gui_core/dialogs.py b/gui/wxpython/gui_core/dialogs.py index 19e430d605e..f79578c47a6 100644 --- a/gui/wxpython/gui_core/dialogs.py +++ b/gui/wxpython/gui_core/dialogs.py @@ -1114,10 +1114,7 @@ def OnRemoveLayer(self, event): def GetLayers(self): """Get layers""" if self.edit_subg: - layers = [] - for maps, sel in self.subgmaps.items(): - if sel: - layers.append(maps) + layers = [maps for maps, sel in self.subgmaps.items() if sel] else: layers = self.gmaps[:] @@ -1342,8 +1339,7 @@ def GetSelectedGroup(self): def GetGroupLayers(self, group, subgroup=None): """Get layers in group""" - kwargs = {} - kwargs["group"] = group + kwargs = {"group": group} if subgroup: kwargs["subgroup"] = subgroup @@ -2139,9 +2135,10 @@ def _layout(self): mainSizer.Add(btnSizer, proportion=0, flag=wx.EXPAND | wx.ALL, border=5) # show panel with the largest number of images and fit size - count = [] - for folder in os.listdir(self.symbolPath): - count.append(len(os.listdir(os.path.join(self.symbolPath, folder)))) + count = [ + len(os.listdir(os.path.join(self.symbolPath, folder))) + for folder in os.listdir(self.symbolPath) + ] index = count.index(max(count)) self.folderChoice.SetSelection(index) @@ -2196,9 +2193,7 @@ def _createSymbolPanels(self, parent): def _getSymbols(self, path): # we assume that images are in subfolders (1 level only) - imageList = [] - for image in os.listdir(path): - imageList.append(os.path.join(path, image)) + imageList = [os.path.join(path, image) for image in os.listdir(path)] return sorted(imageList) @@ -2362,8 +2357,7 @@ def __init__( if self._shell_running: text = _( - "Do you want to quit GRASS GIS including shell " - "or just close the GUI?" + "Do you want to quit GRASS GIS including shell or just close the GUI?" ) else: text = _("Do you want to quit GRASS GIS?") diff --git a/gui/wxpython/gui_core/forms.py b/gui/wxpython/gui_core/forms.py index 7172835f001..14d87728057 100644 --- a/gui/wxpython/gui_core/forms.py +++ b/gui/wxpython/gui_core/forms.py @@ -73,8 +73,7 @@ if os.getenv("GISBASE") is None: # intentionally not translatable sys.exit( - "Failed to start. GRASS GIS is not running" - " or the installation is broken." + "Failed to start. GRASS GIS is not running or the installation is broken." ) from grass.script.setup import set_gui_path @@ -196,11 +195,7 @@ def run(self): map = pMap.get("value", "") if pMap else None # avoid running db.describe several times - cparams = {} - cparams[map] = { - "dbInfo": None, - "layers": None, - } + cparams = {map: {"dbInfo": None, "layers": None}} # update reference widgets for uid in p["wxId-bind"]: @@ -586,8 +581,10 @@ def __init__( self.btn_cancel.Bind(wx.EVT_BUTTON, self.OnCancel) # bind closing to ESC and CTRL+Q self.Bind(wx.EVT_MENU, self.OnCancel, id=wx.ID_CANCEL) - accelTableList = [(wx.ACCEL_NORMAL, wx.WXK_ESCAPE, wx.ID_CANCEL)] - accelTableList.append((wx.ACCEL_CTRL, ord("Q"), wx.ID_CANCEL)) + accelTableList = [ + (wx.ACCEL_NORMAL, wx.WXK_ESCAPE, wx.ID_CANCEL), + (wx.ACCEL_CTRL, ord("Q"), wx.ID_CANCEL), + ] # TODO: bind Ctrl-t for tile windows here (trac #2004) if self.get_dcmd is not None: # A callback has been set up @@ -1802,10 +1799,7 @@ def __init__(self, parent, giface, task, id=wx.ID_ANY, frame=None, *args, **kwar value = self._getValue(p) if prompt == "layer": - if p.get("element", "layer") == "layer_all": - all = True - else: - all = False + all = bool(p.get("element", "layer") == "layer_all") if p.get("age", "old") == "old": win = gselect.LayerSelect( parent=which_panel, all=all, default=p["default"] @@ -2414,15 +2408,9 @@ def OnCheckItem(index=None, flag=None, event=None): pSqlWhere.append(p) # collect ids - pColumnIds = [] - for p in pColumn: - pColumnIds += p["wxId"] - pLayerIds = [] - for p in pLayer: - pLayerIds += p["wxId"] - pSqlWhereIds = [] - for p in pSqlWhere: - pSqlWhereIds += p["wxId"] + pColumnIds = [p["wxId"] for p in pColumn] + pLayerIds = [p["wxId"] for p in pLayer] + pSqlWhereIds = [p["wxId"] for p in pSqlWhere] # set wxId-bindings if pMap: @@ -2840,9 +2828,7 @@ def OnCheckBoxMulti(self, event): myIndex = p["wxId"].index(me) # Unpack current value list - currentValues = {} - for isThere in theParam.get("value", "").split(","): - currentValues[isThere] = 1 + currentValues = dict.fromkeys(theParam.get("value", "").split(","), 1) theValue = theParam["values"][myIndex] if event.IsChecked(): @@ -2851,10 +2837,7 @@ def OnCheckBoxMulti(self, event): del currentValues[theValue] # Keep the original order, so that some defaults may be recovered - currentValueList = [] - for v in theParam["values"]: - if v in currentValues: - currentValueList.append(v) + currentValueList = [v for v in theParam["values"] if v in currentValues] # Pack it back theParam["value"] = ",".join(currentValueList) diff --git a/gui/wxpython/gui_core/ghelp.py b/gui/wxpython/gui_core/ghelp.py index 0c4f6cdadfb..b2a0d0500e9 100644 --- a/gui/wxpython/gui_core/ghelp.py +++ b/gui/wxpython/gui_core/ghelp.py @@ -23,6 +23,9 @@ import re import textwrap import sys + +from pathlib import Path + import wx from wx.html import HtmlWindow from operator import itemgetter @@ -274,9 +277,7 @@ def _pageCopyright(self): """Copyright information""" copyfile = os.path.join(os.getenv("GISBASE"), "COPYING") if os.path.exists(copyfile): - copyrightFile = open(copyfile) - copytext = copyrightFile.read() - copyrightFile.close() + copytext = Path(copyfile).read_text() else: copytext = _("%s file missing") % "COPYING" @@ -303,9 +304,8 @@ def _pageLicense(self): """Licence about""" licfile = os.path.join(os.getenv("GISBASE"), "GPL.TXT") if os.path.exists(licfile): - licenceFile = open(licfile) - license = "".join(licenceFile.readlines()) - licenceFile.close() + with open(licfile) as licenceFile: + license = "".join(licenceFile.readlines()) else: license = _("%s file missing") % "GPL.TXT" # put text into a scrolling panel @@ -384,33 +384,31 @@ def _pageContributors(self, extra=False): else: contribfile = os.path.join(os.getenv("GISBASE"), "contributors.csv") if os.path.exists(contribfile): - contribFile = codecs.open(contribfile, encoding="utf-8", mode="r") contribs = [] errLines = [] - for line in contribFile.readlines()[1:]: - line = line.rstrip("\n") - try: + with codecs.open(contribfile, encoding="utf-8", mode="r") as contribFile: + for line in contribFile.readlines()[1:]: + line = line.rstrip("\n") + try: + if extra: + name, email, country, rfc2_agreed = line.split(",") + else: + ( + cvs_id, + name, + email, + country, + osgeo_id, + rfc2_agreed, + orcid, + ) = line.split(",") + except ValueError: + errLines.append(line) + continue if extra: - name, email, country, rfc2_agreed = line.split(",") + contribs.append((name, email, country)) else: - ( - cvs_id, - name, - email, - country, - osgeo_id, - rfc2_agreed, - orcid, - ) = line.split(",") - except ValueError: - errLines.append(line) - continue - if extra: - contribs.append((name, email, country)) - else: - contribs.append((name, email, country, osgeo_id, orcid)) - - contribFile.close() + contribs.append((name, email, country, osgeo_id, orcid)) if errLines: GError( @@ -469,21 +467,20 @@ def _pageTranslators(self): """Translators info""" translatorsfile = os.path.join(os.getenv("GISBASE"), "translators.csv") if os.path.exists(translatorsfile): - translatorsFile = codecs.open(translatorsfile, encoding="utf-8", mode="r") translators = {} errLines = [] - for line in translatorsFile.readlines()[1:]: - line = line.rstrip("\n") - try: - name, email, languages = line.split(",") - except ValueError: - errLines.append(line) - continue - for language in languages.split(" "): - if language not in translators: - translators[language] = [] - translators[language].append((name, email)) - translatorsFile.close() + with codecs.open(translatorsfile, encoding="utf-8", mode="r") as fd: + for line in fd.readlines()[1:]: + line = line.rstrip("\n") + try: + name, email, languages = line.split(",") + except ValueError: + errLines.append(line) + continue + for language in languages.split(" "): + if language not in translators: + translators[language] = [] + translators[language].append((name, email)) if errLines: GError( @@ -650,10 +647,10 @@ def _pageStats(self): fname = "translation_status.json" statsfile = os.path.join(os.getenv("GISBASE"), fname) if os.path.exists(statsfile): - statsFile = open(statsfile) import json - jsStats = json.load(statsFile) + with open(statsfile) as statsFile: + jsStats = json.load(statsFile) else: jsStats = None self.statswin = ScrolledPanel(self.aboutNotebook) @@ -810,38 +807,39 @@ def fillContentsFromFile(self, htmlFile, skipDescription=True): try: contents = [] skip = False - for line in open(htmlFile, "rb"): - if "DESCRIPTION" in line: - skip = False - if not skip: - # do skip the options description if requested - if "SYNOPSIS" in line: - skip = skipDescription - else: - # FIXME: find only first item - findALink = aLink.search(line) - if findALink is not None: - contents.append( - aLink.sub( - findALink.group(1) - + self.fspath - + findALink.group(2), - line, + with open(htmlFile, "rb") as fd: + for line in fd: + if "DESCRIPTION" in line: + skip = False + if not skip: + # do skip the options description if requested + if "SYNOPSIS" in line: + skip = skipDescription + else: + # FIXME: find only first item + findALink = aLink.search(line) + if findALink is not None: + contents.append( + aLink.sub( + findALink.group(1) + + self.fspath + + findALink.group(2), + line, + ) ) - ) - findImgLink = imgLink.search(line) - if findImgLink is not None: - contents.append( - imgLink.sub( - findImgLink.group(1) - + self.fspath - + findImgLink.group(2), - line, + findImgLink = imgLink.search(line) + if findImgLink is not None: + contents.append( + imgLink.sub( + findImgLink.group(1) + + self.fspath + + findImgLink.group(2), + line, + ) ) - ) - if findALink is None and findImgLink is None: - contents.append(line) + if findALink is None and findImgLink is None: + contents.append(line) self.SetPage("".join(contents)) self.loaded = True except Exception: # The Manual file was not found diff --git a/gui/wxpython/gui_core/gselect.py b/gui/wxpython/gui_core/gselect.py index 67b8a1280e2..bcb744d8562 100644 --- a/gui/wxpython/gui_core/gselect.py +++ b/gui/wxpython/gui_core/gselect.py @@ -832,17 +832,15 @@ def __init__(self, map): def _CheckDBConnection(self): """Check DB connection""" - nuldev = open(os.devnull, "w+") - # if map is not defined (happens with vnet initialization) or it - # doesn't exist - try: - self.layers = gs.vector_db(map=self.map, stderr=nuldev) - except CalledModuleError: - return False - finally: # always close nuldev - nuldev.close() + with open(os.devnull, "w+") as nuldev: + # if map is not defined (happens with vnet initialization) or it + # doesn't exist + try: + self.layers = gs.vector_db(map=self.map, stderr=nuldev) + except CalledModuleError: + return False - return bool(len(self.layers.keys()) > 0) + return bool(len(self.layers.keys()) > 0) def _DescribeTables(self): """Describe linked tables""" @@ -1328,8 +1326,7 @@ def _onSelection(self, event): def UpdateItems(self, location, dbase=None): """Update list of mapsets for given location - :param str dbase: path to GIS database (None to use currently - selected) + :param str dbase: path to GIS database (None to use currently selected) :param str location: name of location """ if dbase: @@ -1396,16 +1393,12 @@ def __init__( ftype = "ogr" if ogr else "gdal" - formats = [] - for f in GetFormats()[ftype][srcType].items(): - formats += f + formats = list(GetFormats()[ftype][srcType].items()) self.SetItems(formats) def GetExtension(self, name): """Get file extension by format name""" - formatToExt = {} - formatToExt.update(rasterFormatExtension) - formatToExt.update(vectorFormatExtension) + formatToExt = {**rasterFormatExtension, **vectorFormatExtension} return formatToExt.get(name, "") @@ -1950,9 +1943,7 @@ def _layout(self): def _getExtension(self, name): """Get file extension by format name""" - formatToExt = {} - formatToExt.update(rasterFormatExtension) - formatToExt.update(vectorFormatExtension) + formatToExt = {**rasterFormatExtension, **vectorFormatExtension} return formatToExt.get(name, "") @@ -2222,8 +2213,7 @@ def hasRastSameProjAsLocation(dsn, table=None): :param str dsn: data source name :param str table: PG DB table name, default value is None - :return str: 1 if raster projection match location - projection else 0 + :return str: 1 if raster projection matches location projection, else 0 """ projectionMatch = "0" @@ -2521,10 +2511,8 @@ def _getPGDBtables(self, dsn): def _getPGDBTablesColumnsTypesSql(self, tables): """Get PostGIS DB tables columns data type SQL command - :param list tables: list of PG DB tables with - simple quotes ["'table'", ...] - :return str: SQL string for query all PG DB tables with - columns data types + :param list tables: list of PG DB tables with simple quotes ["'table'", ...] + :return str: SQL string for query all PG DB tables with columns data types """ return f""" SELECT @@ -2544,7 +2532,7 @@ def _getPGDBTablesColumnsTypesSql(self, tables): pg_catalog.pg_table_is_visible(c.oid) ) AS o ON a.attrelid = o.oid WHERE - relname IN ({', '.join(tables)}) + relname IN ({", ".join(tables)}) AND NOT a.attisdropped; """ @@ -2620,8 +2608,7 @@ def _getPGDBRasters(self, dsn): GError( parent=self, message=_( - "PostgreSQL DB <{psql}> program was not found." - " Please, install it." + "PostgreSQL DB <{psql}> program was not found. Please, install it." ).format(psql=self._psql), ) Debug.msg(3, f"GdalSelect._getPGDBRasters(): return {rasters}") diff --git a/gui/wxpython/gui_core/mapdisp.py b/gui/wxpython/gui_core/mapdisp.py index d72fabe4438..515900959c7 100644 --- a/gui/wxpython/gui_core/mapdisp.py +++ b/gui/wxpython/gui_core/mapdisp.py @@ -285,19 +285,23 @@ def GetProgressBar(self): def GetMap(self): """Returns current map (renderer) instance""" - raise NotImplementedError("GetMap") + msg = self.GetMap.__name__ + raise NotImplementedError(msg) def GetWindow(self): """Returns current map window""" - raise NotImplementedError("GetWindow") + msg = self.GetWindow.__name__ + raise NotImplementedError(msg) def GetWindows(self): """Returns list of map windows""" - raise NotImplementedError("GetWindows") + msg = self.GetWindows.__name__ + raise NotImplementedError(msg) def GetMapToolbar(self): """Returns toolbar with zooming tools""" - raise NotImplementedError("GetMapToolbar") + msg = self.GetMapToolbar.__name__ + raise NotImplementedError(msg) def GetToolbar(self, name): """Returns toolbar if exists and is active, else None.""" @@ -393,7 +397,8 @@ def GetToolbarNames(self): def AddToolbar(self): """Add defined toolbar to the window""" - raise NotImplementedError("AddToolbar") + msg = self.AddToolbar.__name__ + raise NotImplementedError(msg) def RemoveToolbar(self, name, destroy=False): """Removes defined toolbar from the window @@ -419,7 +424,8 @@ def IsPaneShown(self, name): def OnRender(self, event): """Re-render map composition (each map layer)""" - raise NotImplementedError("OnRender") + msg = self.OnRender.__name__ + raise NotImplementedError(msg) def OnEnableDisableRender(self, event): """Enable/disable auto-rendering map composition (each map layer)""" diff --git a/gui/wxpython/gui_core/menu.py b/gui/wxpython/gui_core/menu.py index 34d6eeffd31..a512b6eea19 100644 --- a/gui/wxpython/gui_core/menu.py +++ b/gui/wxpython/gui_core/menu.py @@ -342,8 +342,7 @@ class RecentFilesMenu: written into the .recent_files file :param obj parent_menu: menu widget instance where be inserted recent files menu on the specified position - :param int pos: position (index) where insert recent files menu in - the parent menu + :param int pos: position (index) where insert recent files menu in the parent menu :param int history_len: the maximum number of file paths written into the .recent_files file to app name group """ diff --git a/gui/wxpython/gui_core/preferences.py b/gui/wxpython/gui_core/preferences.py index 85e5313da0e..73ad6f3d3bd 100644 --- a/gui/wxpython/gui_core/preferences.py +++ b/gui/wxpython/gui_core/preferences.py @@ -2186,8 +2186,7 @@ def OnSetOutputFont(self, event): dlg = wx.FontDialog(self, fontdata) - "FIXME: native font dialog does not initialize with current font" - + # FIXME: native font dialog does not initialize with current font if dlg.ShowModal() == wx.ID_OK: outdata = dlg.GetFontData() font = outdata.GetChosenFont() diff --git a/gui/wxpython/gui_core/pyedit.py b/gui/wxpython/gui_core/pyedit.py index 5958ffb6d68..86b29bc178b 100644 --- a/gui/wxpython/gui_core/pyedit.py +++ b/gui/wxpython/gui_core/pyedit.py @@ -305,8 +305,7 @@ def _openFile(self, file_path): except PermissionError: GError( message=_( - "Permission denied <{}>. Please change file " - "permission for reading." + "Permission denied <{}>. Please change file permission for reading." ).format(file_path), parent=self.guiparent, showTraceback=False, diff --git a/gui/wxpython/gui_core/query.py b/gui/wxpython/gui_core/query.py index 5e0f734fa6a..bf46df8688e 100644 --- a/gui/wxpython/gui_core/query.py +++ b/gui/wxpython/gui_core/query.py @@ -119,11 +119,10 @@ def ShowContextMenu(self, node): menu = Menu() texts = [] if len(nodes) > 1: - values = [] - for node in nodes: - values.append( - (node.label, node.data[self._colNames[1]] if node.data else "") - ) + values = [ + (node.label, node.data[self._colNames[1]] if node.data else "") + for node in nodes + ] col1 = "\n".join([val[1] for val in values if val[1]]) col2 = "\n".join([val[0] for val in values if val[0]]) table = "\n".join([val[0] + ": " + val[1] for val in values]) @@ -253,8 +252,7 @@ def PrepareQueryResults(coordinates, result): Adds coordinates, improves vector results tree structure. """ - data = [] - data.append({_("east, north"): ", ".join(map(str, coordinates))}) + data = [{_("east, north"): ", ".join(map(str, coordinates))}] for part in result: if "Map" in part: itemText = part["Map"] diff --git a/gui/wxpython/gui_core/toolbars.py b/gui/wxpython/gui_core/toolbars.py index d47a850a6a5..33ca987e142 100644 --- a/gui/wxpython/gui_core/toolbars.py +++ b/gui/wxpython/gui_core/toolbars.py @@ -18,6 +18,7 @@ import platform import os +from itertools import starmap import wx from wx.lib.agw import aui @@ -243,10 +244,7 @@ def EnableAll(self, enable=True): def _getToolbarData(self, data): """Define tool""" - retData = [] - for args in data: - retData.append(self._defineTool(*args)) - return retData + return list(starmap(self._defineTool, data)) def _defineTool( self, name=None, icon=None, handler=None, item=wx.ITEM_NORMAL, pos=-1 @@ -328,7 +326,6 @@ class BaseToolbar(ToolBar): Following code shows how to create new basic toolbar: - class MyToolbar(BaseToolbar): def __init__(self, parent): BaseToolbar.__init__(self, parent) diff --git a/gui/wxpython/gui_core/vselect.py b/gui/wxpython/gui_core/vselect.py index 16bb64b7a57..206c1dbb43e 100644 --- a/gui/wxpython/gui_core/vselect.py +++ b/gui/wxpython/gui_core/vselect.py @@ -332,9 +332,7 @@ def OnExportMap(self, event): GMessage(_("No features selected")) return lst = "" - for ( - cat - ) in ( + for cat in ( self.selectedFeatures ): # build text string of categories for v.extract input lst += str(cat["Category"]) + "," @@ -371,23 +369,21 @@ def OnExportMap(self, event): else: GError(_("Unable to create a new vector map.\n\nReason: %s") % err) - """ - def SetSelectedCat(self, cats): - # allows setting selected vector categories by list of cats (per line) - info = self.QuerySelectedMap() - if 'Category' not in info: - return - - for cat in cats.splitlines(): - tmpDict = {} - tmpDict['Category'] = cat - tmpDict['Map'] = info['Map'] - tmpDict['Layer'] = info['Layer'] - tmpDict['Type'] = '-' - self.AddVecInfo(tmpDict) - - self._draw() - """ + # def SetSelectedCat(self, cats): + # # allows setting selected vector categories by list of cats (per line) + # info = self.QuerySelectedMap() + # if "Category" not in info: + # return + # + # for cat in cats.splitlines(): + # tmpDict = {} + # tmpDict["Category"] = cat + # tmpDict["Map"] = info["Map"] + # tmpDict["Layer"] = info["Layer"] + # tmpDict["Type"] = "-" + # self.AddVecInfo(tmpDict) + # + # self._draw() class VectorSelectHighlighter: @@ -397,6 +393,9 @@ class VectorSelectHighlighter: """ def __init__(self, mapdisp, giface): + """ + :param mapdisp: Map display frame + """ self.qlayer = None self.mapdisp = mapdisp self.giface = giface diff --git a/gui/wxpython/gui_core/widgets.py b/gui/wxpython/gui_core/widgets.py index 40c9e6c5e19..47bd3a1957c 100644 --- a/gui/wxpython/gui_core/widgets.py +++ b/gui/wxpython/gui_core/widgets.py @@ -1520,30 +1520,29 @@ def _writeSettings(self): :return: -1 on failure """ try: - fd = open(self.settingsFile, "w") - fd.write("format_version=2.0\n") - for key, values in self._settings.items(): - first = True - for v in values: - # escaping characters - for e_ch in self.esc_chars: - v = v.replace(e_ch, self.esc_chars[self.e_char_i] + e_ch) - if first: + with open(self.settingsFile, "w") as fd: + fd.write("format_version=2.0\n") + for key, values in self._settings.items(): + first = True + for v in values: # escaping characters for e_ch in self.esc_chars: - key = key.replace( - e_ch, self.esc_chars[self.e_char_i] + e_ch - ) - fd.write("%s;%s;" % (key, v)) - first = False - else: - fd.write("%s;" % (v)) - fd.write("\n") + v = v.replace(e_ch, self.esc_chars[self.e_char_i] + e_ch) + if first: + # escaping characters + for e_ch in self.esc_chars: + key = key.replace( + e_ch, self.esc_chars[self.e_char_i] + e_ch + ) + fd.write("%s;%s;" % (key, v)) + first = False + else: + fd.write("%s;" % (v)) + fd.write("\n") except OSError: GError(parent=self, message=_("Unable to save settings")) return -1 - fd.close() return 0 diff --git a/gui/wxpython/gui_core/wrap.py b/gui/wxpython/gui_core/wrap.py index b971e5e6eab..b163b069e20 100644 --- a/gui/wxpython/gui_core/wrap.py +++ b/gui/wxpython/gui_core/wrap.py @@ -15,6 +15,8 @@ @author Anna Petrasova """ +from __future__ import annotations + import sys import wx import wx.lib.agw.floatspin as fs @@ -80,7 +82,7 @@ def convertToInt(argsOrKwargs, roundVal=False): return result -def IsDark(): +def IsDark() -> bool: """Detects if used theme is dark. Wraps wx method for different versions.""" @@ -96,25 +98,25 @@ def luminance(c): return luminance(fg) - luminance(bg) > 0.2 -def BitmapFromImage(image, depth=-1): +def BitmapFromImage(image: wx.Image, depth=-1) -> wx.Bitmap: if wxPythonPhoenix: return wx.Bitmap(img=image, depth=depth) return wx.BitmapFromImage(image, depth=depth) -def ImageFromBitmap(bitmap): +def ImageFromBitmap(bitmap: wx.Bitmap) -> wx.Image: if wxPythonPhoenix: return bitmap.ConvertToImage() return wx.ImageFromBitmap(bitmap) -def EmptyBitmap(width, height, depth=-1): +def EmptyBitmap(width, height, depth=-1) -> wx.Bitmap: if wxPythonPhoenix: return wx.Bitmap(width=width, height=height, depth=depth) return wx.EmptyBitmap(width=width, height=height, depth=depth) -def EmptyImage(width, height, clear=True): +def EmptyImage(width, height, clear=True) -> wx.Image: if wxPythonPhoenix: return wx.Image(width=width, height=height, clear=clear) return wx.EmptyImage(width=width, height=height, clear=clear) @@ -618,17 +620,11 @@ class DragImage(wx.GenericDragImage if wxPythonPhoenix else wx.DragImage): """Wrapper around wx.DragImage to have more control over the widget on different platforms/wxpython versions""" - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - class PseudoDC(wx.adv.PseudoDC if wxPythonPhoenix else wx.PseudoDC): """Wrapper around wx.PseudoDC to have more control over the widget on different platforms/wxpython versions""" - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - def DrawLinePoint(self, *args, **kwargs): args = convertToInt(argsOrKwargs=args, roundVal=True) kwargs = convertToInt(argsOrKwargs=kwargs, roundVal=True) @@ -671,9 +667,6 @@ class ClientDC(wx.ClientDC): """Wrapper around wx.ClientDC to have more control over the widget on different platforms/wxpython versions""" - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - def GetFullMultiLineTextExtent(self, string, font=None): if wxPythonPhoenix: return super().GetFullMultiLineTextExtent(string, font) @@ -689,17 +682,17 @@ def __init__(self, *args, **kwargs): kwargs = convertToInt(argsOrKwargs=kwargs) wx.Rect.__init__(self, *args, **kwargs) - def ContainsXY(self, x, y): + def ContainsXY(self, x: float, y: float) -> bool: if wxPythonPhoenix: return wx.Rect.Contains(self, x=int(x), y=int(y)) return wx.Rect.ContainsXY(self, int(x), int(y)) - def ContainsRect(self, rect): + def ContainsRect(self, rect: wx.Rect) -> bool: if wxPythonPhoenix: return wx.Rect.Contains(self, rect=rect) return wx.Rect.ContainsRect(self, rect) - def OffsetXY(self, dx, dy): + def OffsetXY(self, dx: float, dy: float) -> wx.Rect: if wxPythonPhoenix: return wx.Rect.Offset(self, int(dx), int(dy)) return wx.Rect.OffsetXY(self, int(dx), int(dy)) diff --git a/gui/wxpython/history/browser.py b/gui/wxpython/history/browser.py index 03e39a884a3..9589cee3f0d 100644 --- a/gui/wxpython/history/browser.py +++ b/gui/wxpython/history/browser.py @@ -326,11 +326,11 @@ def _get_current_region(self): def _get_history_region(self): """Get computational region settings of executed command.""" - history_region = {} - for key, value in self.region_settings.items(): - if self._region_settings_filter(key): - history_region[key] = value - return history_region + return { + key: value + for key, value in self.region_settings.items() + if self._region_settings_filter(key) + } def OnUpdateRegion(self, event): """Set current region to the region of executed command.""" diff --git a/gui/wxpython/history/tree.py b/gui/wxpython/history/tree.py index 3701fefb001..18045cd1cb9 100644 --- a/gui/wxpython/history/tree.py +++ b/gui/wxpython/history/tree.py @@ -216,6 +216,10 @@ def _popupMenuCommand(self): """Create popup menu for commands""" menu = Menu() + copyItem = wx.MenuItem(menu, wx.ID_ANY, _("&Copy")) + menu.AppendItem(copyItem) + self.Bind(wx.EVT_MENU, self.OnCopyCmd, copyItem) + item = wx.MenuItem(menu, wx.ID_ANY, _("&Remove")) menu.AppendItem(item) self.Bind(wx.EVT_MENU, self.OnRemoveCmd, item) @@ -313,7 +317,6 @@ def _initHistoryModel(self): Populate the tree history model based on the current history log. """ for entry in self.ReadFromHistory(): - # Get history day node day_node = self.GetHistoryNode(entry) @@ -658,3 +661,25 @@ def OnDoubleClick(self, node): self.CollapseNode(node, recursive=False) else: self.ExpandNode(node, recursive=False) + + def OnCopyCmd(self, event): + """Copy selected cmd to clipboard""" + self.DefineItems(self.GetSelected()) + if not self.selected_command: + return + + selected_command = self.selected_command[0] + command = selected_command.data["name"] + + # Copy selected command to clipboard + try: + if wx.TheClipboard.Open(): + try: + wx.TheClipboard.SetData(wx.TextDataObject(command)) + self.showNotification.emit( + message=_("Command <{}> copied to clipboard").format(command) + ) + finally: + wx.TheClipboard.Close() + except wx.PyWidgetError: + self.showNotification.emit(message=_("Failed to copy command to clipboard")) diff --git a/gui/wxpython/iclass/frame.py b/gui/wxpython/iclass/frame.py index b19d8d7d3ac..4bb9e66b6c4 100644 --- a/gui/wxpython/iclass/frame.py +++ b/gui/wxpython/iclass/frame.py @@ -18,17 +18,38 @@ @author Anna Kratochvilova """ -import os import copy +import os import tempfile +from ctypes import byref, pointer import wx -from ctypes import * - try: - from grass.lib.imagery import * - from grass.lib.vector import * + from grass.lib.imagery import ( + I_free_group_ref, + I_free_signatures, + I_iclass_add_signature, + I_iclass_analysis, + I_iclass_create_raster, + I_iclass_free_statistics, + I_iclass_init_group, + I_iclass_init_signatures, + I_iclass_init_statistics, + I_iclass_statistics_set_nstd, + I_iclass_write_signatures, + I_init_group_ref, + I_init_signatures, + IClass_statistics, + Ref, + Signature, + ) + from grass.lib.vector import ( + Vect_area_alive, + Vect_get_map_box, + Vect_get_num_areas, + bound_box, + ) haveIClass = True errMsg = "" @@ -37,37 +58,35 @@ errMsg = _("Loading imagery lib failed.\n%s") % e import grass.script as gs - -from mapdisp import statusbar as sb -from mapdisp.main import StandaloneMapDisplayGrassInterface -from mapwin.buffered import BufferedMapWindow -from vdigit.toolbars import VDigitToolbar -from gui_core.mapdisp import DoubleMapPanel, FrameMixin from core import globalvar +from core.gcmd import GError, GMessage, RunCommand from core.render import Map -from core.gcmd import RunCommand, GMessage, GError +from dbmgr.vinfo import VectorDBInfo +from grass.pydispatch.signal import Signal from gui_core.dialogs import SetOpacityDialog +from gui_core.mapdisp import DoubleMapPanel, FrameMixin from gui_core.wrap import Menu -from dbmgr.vinfo import VectorDBInfo +from mapdisp import statusbar as sb +from mapdisp.main import StandaloneMapDisplayGrassInterface +from mapwin.buffered import BufferedMapWindow +from vdigit.toolbars import VDigitToolbar -from iclass.digit import IClassVDigitWindow, IClassVDigit -from iclass.toolbars import ( - IClassMapToolbar, - IClassMiscToolbar, - IClassToolbar, - IClassMapManagerToolbar, -) -from iclass.statistics import StatisticsData from iclass.dialogs import ( IClassCategoryManagerDialog, - IClassGroupDialog, - IClassSignatureFileDialog, IClassExportAreasDialog, + IClassGroupDialog, IClassMapDialog, + IClassSignatureFileDialog, ) +from iclass.digit import IClassVDigit, IClassVDigitWindow from iclass.plots import PlotPanel - -from grass.pydispatch.signal import Signal +from iclass.statistics import StatisticsData +from iclass.toolbars import ( + IClassMapManagerToolbar, + IClassMapToolbar, + IClassMiscToolbar, + IClassToolbar, +) class IClassMapPanel(DoubleMapPanel): @@ -1483,10 +1502,7 @@ def AddLayerRGB(self, cmd): :param cmd: d.rgb command as a list """ - name = [] - for param in cmd: - if "=" in param: - name.append(param.split("=")[1]) + name = [param.split("=")[1] for param in cmd if "=" in param] name = ",".join(name) self.map.AddLayer( ltype="rgb", diff --git a/gui/wxpython/iclass/statistics.py b/gui/wxpython/iclass/statistics.py index 059ce5e6b03..c1727bc1676 100644 --- a/gui/wxpython/iclass/statistics.py +++ b/gui/wxpython/iclass/statistics.py @@ -18,13 +18,28 @@ """ import os -from ctypes import * +import sys +from ctypes import byref, c_char_p, c_float, c_int import grass.script as gs try: - from grass.lib.imagery import * -except ImportError as e: + from grass.lib.imagery import ( + I_iclass_statistics_get_cat, + I_iclass_statistics_get_color, + I_iclass_statistics_get_histo, + I_iclass_statistics_get_max, + I_iclass_statistics_get_mean, + I_iclass_statistics_get_min, + I_iclass_statistics_get_name, + I_iclass_statistics_get_nbands, + I_iclass_statistics_get_ncells, + I_iclass_statistics_get_nstd, + I_iclass_statistics_get_range_max, + I_iclass_statistics_get_range_min, + I_iclass_statistics_get_stddev, + ) +except ImportError: sys.stderr.write(_("Loading imagery lib failed")) from grass.pydispatch.signal import Signal diff --git a/gui/wxpython/image2target/ii2t_gis_set.py b/gui/wxpython/image2target/ii2t_gis_set.py index 0329516e04a..1022ef2c9b5 100644 --- a/gui/wxpython/image2target/ii2t_gis_set.py +++ b/gui/wxpython/image2target/ii2t_gis_set.py @@ -104,9 +104,8 @@ def __init__(self, parent=None, id=wx.ID_ANY, style=wx.DEFAULT_FRAME_STYLE): # labels # crashes when LOCATION doesn't exist # get version & revision - versionFile = open(os.path.join(globalvar.ETCDIR, "VERSIONNUMBER")) - versionLine = versionFile.readline().rstrip("\n") - versionFile.close() + with open(os.path.join(globalvar.ETCDIR, "VERSIONNUMBER")) as versionFile: + versionLine = versionFile.readline().rstrip("\n") try: grassVersion, grassRevision = versionLine.split(" ", 1) if grassVersion.endswith("dev"): @@ -542,8 +541,7 @@ def _readGisRC(self): gisrc = os.getenv("GISRC") if gisrc and os.path.isfile(gisrc): - try: - rc = open(gisrc) + with open(gisrc) as rc: for line in rc: try: key, val = line.split(":", 1) @@ -552,8 +550,6 @@ def _readGisRC(self): _("Invalid line in GISRC file (%s):%s\n") % (e, line) ) grassrc[key.strip()] = DecodeString(val.strip()) - finally: - rc.close() return grassrc @@ -868,8 +864,7 @@ def UpdateLocations(self, dbase): GError( parent=self, message=_( - "Unicode error detected. " - "Check your locale settings. Details: {0}" + "Unicode error detected. Check your locale settings. Details: {0}" ).format(e), showTraceback=False, ) diff --git a/gui/wxpython/image2target/ii2t_manager.py b/gui/wxpython/image2target/ii2t_manager.py index bfc5bbc3489..97d528bcf7d 100644 --- a/gui/wxpython/image2target/ii2t_manager.py +++ b/gui/wxpython/image2target/ii2t_manager.py @@ -31,51 +31,54 @@ # TODO: i.ortho.transform has 6 appearances, check each of them and configure # TODO: i.ortho.transform looks for REF_POINTS/CONTROL_POINTS and not POINTS # TODO: CHECK CONTROL_POINTS format and create it for i.ortho.transform to use. - +from __future__ import annotations import os -import sys import shutil +import sys from copy import copy +from typing import TYPE_CHECKING import wx -from wx.lib.mixins.listctrl import ColumnSorterMixin, ListCtrlAutoWidthMixin import wx.lib.colourselect as csel - from core import globalvar +from wx.lib.mixins.listctrl import ColumnSorterMixin, ListCtrlAutoWidthMixin -if globalvar.wxPythonPhoenix: +if globalvar.wxPythonPhoenix or TYPE_CHECKING: from wx import adv as wiz else: from wx import wizard as wiz import grass.script as gs +# isort: split from core import utils +from core.gcmd import GError, GMessage, GWarning, RunCommand +from core.giface import Notification from core.render import Map -from gui_core.gselect import Select, LocationSelect, MapsetSelect -from gui_core.dialogs import GroupDialog -from gui_core.mapdisp import FrameMixin -from core.gcmd import RunCommand, GMessage, GError, GWarning from core.settings import UserSettings from gcp.mapdisplay import MapPanel -from core.giface import Notification +from gui_core.dialogs import GroupDialog +from gui_core.gselect import LocationSelect, MapsetSelect, Select +from gui_core.mapdisp import FrameMixin from gui_core.wrap import ( - SpinCtrl, + BitmapFromImage, Button, - StaticText, - StaticBox, CheckListBox, - TextCtrl, - Menu, - ListCtrl, - BitmapFromImage, CheckListCtrlMixin, + ListCtrl, + Menu, + SpinCtrl, + StaticBox, + StaticText, + TextCtrl, ) - from location_wizard.wizard import GridBagSizerTitledPage as TitledPage +if TYPE_CHECKING: + from wx.adv import WizardEvent + # # global variables # @@ -87,22 +90,13 @@ def getSmallUpArrowImage(): - stream = open(os.path.join(globalvar.IMGDIR, "small_up_arrow.png"), "rb") - try: - img = wx.Image(stream) - finally: - stream.close() - return img + with open(os.path.join(globalvar.IMGDIR, "small_up_arrow.png"), "rb") as stream: + return wx.Image(stream) def getSmallDnArrowImage(): - stream = open(os.path.join(globalvar.IMGDIR, "small_down_arrow.png"), "rb") - try: - img = wx.Image(stream) - finally: - stream.close() - stream.close() - return img + with open(os.path.join(globalvar.IMGDIR, "small_down_arrow.png"), "rb") as stream: + return wx.Image(stream) class GCPWizard: @@ -361,8 +355,9 @@ def SetSrcEnv(self, location, mapset): try: f = open(self.source_gisrc, mode="w") - for line in self.gisrc_dict.items(): - f.write(line[0] + ": " + line[1] + "\n") + f.writelines( + line[0] + ": " + line[1] + "\n" for line in self.gisrc_dict.items() + ) finally: f.close() @@ -529,13 +524,10 @@ def OnMapset(self, event): if not wx.FindWindowById(wx.ID_FORWARD).IsEnabled(): wx.FindWindowById(wx.ID_FORWARD).Enable(True) - def OnPageChanging(self, event=None): + def OnPageChanging(self, event: WizardEvent | None = None) -> None: if event.GetDirection() and (self.xylocation == "" or self.xymapset == ""): GMessage( - _( - "You must select a valid location " - "and mapset in order to continue" - ), + _("You must select a valid location and mapset in order to continue"), parent=self, ) event.Veto() @@ -543,7 +535,7 @@ def OnPageChanging(self, event=None): self.parent.SetSrcEnv(self.xylocation, self.xymapset) - def OnEnterPage(self, event=None): + def OnEnterPage(self, event: WizardEvent | None = None) -> None: if self.xylocation == "" or self.xymapset == "": wx.FindWindowById(wx.ID_FORWARD).Enable(False) else: @@ -690,7 +682,7 @@ def OnVGroup(self, event): def OnExtension(self, event): self.extension = self.ext_txt.GetValue() - def OnPageChanging(self, event=None): + def OnPageChanging(self, event: WizardEvent | None = None) -> None: if event.GetDirection() and self.xygroup == "": GMessage( _("You must select a valid image/map group in order to continue"), @@ -707,7 +699,7 @@ def OnPageChanging(self, event=None): event.Veto() return - def OnEnterPage(self, event=None): + def OnEnterPage(self, event: WizardEvent | None = None) -> None: global maptype self.groupList = [] @@ -892,7 +884,7 @@ def OnTgtVectSelection(self, event): tgt_map["vector"] = self.tgtvectselection.GetValue() - def OnPageChanging(self, event=None): + def OnPageChanging(self, event: WizardEvent | None = None) -> None: global src_map, tgt_map if event.GetDirection() and (src_map == ""): @@ -904,7 +896,7 @@ def OnPageChanging(self, event=None): self.parent.SwitchEnv("target") - def OnEnterPage(self, event=None): + def OnEnterPage(self, event: WizardEvent | None = None) -> None: global maptype, src_map, tgt_map self.srcselection.SetElementList(maptype) @@ -946,15 +938,12 @@ def OnEnterPage(self, event=None): "VREF", ) - f = open(vgrpfile) - try: + with open(vgrpfile) as f: for vect in f: vect = vect.strip("\n") if len(vect) < 1: continue self.parent.src_maps.append(vect) - finally: - f.close() if len(self.parent.src_maps) < 1: GError( @@ -1003,6 +992,7 @@ def __init__( Map=None, lmgr=None, ): + # pylint: disable=super-init-not-called; See InitMapDisplay() self.grwiz = grwiz # GR Wizard self._giface = giface @@ -1387,9 +1377,8 @@ def SetSettings(self): font = self.GetFont() font.SetPointSize(int(spx) + 2) - textProp = {} - textProp["active"] = True - textProp["font"] = font + textProp = {"active": True, "font": font} + self.pointsToDrawSrc.SetPropertyVal("text", textProp) self.pointsToDrawTgt.SetPropertyVal("text", copy(textProp)) @@ -1497,6 +1486,7 @@ def SetGCPData(self, coordtype, coord, mapdisp=None, confirm=False): ) # Get the elevation height from the map given by i.ortho.elev from subprocess import PIPE + from grass.pygrass.modules import Module rwhat = Module( @@ -1778,16 +1768,13 @@ def OnGeorect(self, event): self.grwiz.SwitchEnv("source") # make list of vectors to georectify from VREF - f = open(self.file["vgrp"]) vectlist = [] - try: + with open(self.file["vgrp"]) as f: for vect in f: vect = vect.strip("\n") if len(vect) < 1: continue vectlist.append(vect) - finally: - f.close() # georectify each vector in VREF using v.rectify for vect in vectlist: @@ -2634,8 +2621,7 @@ def __init__( self.listMap = CheckListBox(parent=self, id=wx.ID_ANY, choices=vectlist) if os.path.isfile(self.vgrpfile): - f = open(self.vgrpfile) - try: + with open(self.vgrpfile) as f: checked = [] for line in f: line = line.replace("\n", "") @@ -2643,8 +2629,6 @@ def __init__( continue checked.append(line) self.listMap.SetCheckedStrings(checked) - finally: - f.close() line = wx.StaticLine( parent=self, id=wx.ID_ANY, size=(20, -1), style=wx.LI_HORIZONTAL @@ -2703,12 +2687,8 @@ def MakeVGroup(self): continue vgrouplist.append(self.listMap.GetString(item) + "@" + self.xymapset) - f = open(self.vgrpfile, mode="w") - try: - for vect in vgrouplist: - f.write(vect + "\n") - finally: - f.close() + with open(self.vgrpfile, mode="w") as f: + f.writelines(vect + "\n" for vect in vgrouplist) class EditGCP(wx.Dialog): @@ -2835,11 +2815,11 @@ def __init__( size=wx.DefaultSize, style=wx.DEFAULT_DIALOG_STYLE, ): - wx.Dialog.__init__(self, parent, id, title, pos, size, style) """ Dialog to set profile text options: font, title and font size, axis labels and font size """ + wx.Dialog.__init__(self, parent, id, title, pos, size, style) # # initialize variables # @@ -3237,8 +3217,7 @@ def OnSDFactor(self, event): GError( parent=self, message=_( - "RMS threshold factor is < 1\n" - "Too many points might be highlighted" + "RMS threshold factor is < 1\nToo many points might be highlighted" ), ) diff --git a/gui/wxpython/iscatt/controllers.py b/gui/wxpython/iscatt/controllers.py index 6a5bcb6bb9e..f87569b2698 100644 --- a/gui/wxpython/iscatt/controllers.py +++ b/gui/wxpython/iscatt/controllers.py @@ -1133,11 +1133,9 @@ def DeletAllCategories(self): def SetCategory(self, cat, stats): self.cats_mgr.setCategoryAttrs.disconnect(self.SetStatistics) - cats_attr = {} - - for attr in ["name", "color", "nstd"]: - if attr in stats: - cats_attr[attr] = stats[attr] + cats_attr = { + attr: stats[attr] for attr in ["name", "color", "nstd"] if attr in stats + } if cats_attr: self.cats_mgr.SetCategoryAttrs(cat, cats_attr) diff --git a/gui/wxpython/iscatt/dialogs.py b/gui/wxpython/iscatt/dialogs.py index a2d054b64a4..4dd10aeb832 100644 --- a/gui/wxpython/iscatt/dialogs.py +++ b/gui/wxpython/iscatt/dialogs.py @@ -425,9 +425,7 @@ def __init__( gridSizer = wx.GridBagSizer(vgap=1, hgap=1) row = 0 - setts = {} - setts.update(self.colorsSetts) - setts.update(self.sizeSetts) + setts = {**self.colorsSetts, **self.sizeSetts} settsOrder = [ "sel_pol", diff --git a/gui/wxpython/iscatt/iscatt_core.py b/gui/wxpython/iscatt/iscatt_core.py index c23d9ff97f7..1d56ffe88c3 100644 --- a/gui/wxpython/iscatt/iscatt_core.py +++ b/gui/wxpython/iscatt/iscatt_core.py @@ -155,12 +155,12 @@ def UpdateCategoryWithPolygons(self, cat_id, scatts_pols, value): b1, b2 = idScattToidBands(scatt_id, len(self.an_data.GetBands())) b = self.scatts_dt.GetBandsInfo(scatt_id) - region = {} - region["s"] = b["b2"]["min"] - 0.5 - region["n"] = b["b2"]["max"] + 0.5 - - region["w"] = b["b1"]["min"] - 0.5 - region["e"] = b["b1"]["max"] + 0.5 + region = { + "s": b["b2"]["min"] - 0.5, + "n": b["b2"]["max"] + 0.5, + "w": b["b1"]["min"] - 0.5, + "e": b["b1"]["max"] + 0.5, + } arr = self.scatt_conds_dt.GetValuesArr(cat_id, scatt_id) arr = Rasterize(polygon=coords, rast=arr, region=region, value=value) @@ -218,11 +218,12 @@ def SyncWithMap(self): region = self.an_data.GetRegion() - bbox = {} - bbox["maxx"] = region["e"] - bbox["minx"] = region["w"] - bbox["maxy"] = region["n"] - bbox["miny"] = region["s"] + bbox = { + "maxx": region["e"], + "minx": region["w"], + "maxy": region["n"], + "miny": region["s"], + } updated_cats = [] @@ -551,8 +552,7 @@ def GetScatt(self, scatt_id, cats_ids=None): class ScattPlotsData(ScattPlotsCondsData): - """Data structure for computed points (classes) in scatter plots.\ - """ + """Data structure for computed points (classes) in scatter plots.""" def __init__(self, an_data): self.cats_rasts = {} @@ -727,35 +727,36 @@ def GetCatsRasts(self): return cats_rasts -# not used, using iclass_perimeter algorithm instead of scipy convolve2d -""" -def RasterizePolygon(pol, height, min_h, width, min_w): - - # Joe Kington - # https://stackoverflow.com/questions/3654289/scipy-create-2d-polygon-mask - - #poly_verts = [(1,1), (1,4), (4,4),(4,1), (1,1)] - - nx = width - ny = height - - x, y = np.meshgrid(np.arange(-0.5 + min_w, nx + 0.5 + min_w, dtype=float), - np.arange(-0.5 + min_h, ny + 0.5 + min_h, dtype=float)) - x, y = x.flatten(), y.flatten() - - points = np.vstack((x,y)).T - - p = Path(pol) - grid = p.contains_points(points) - grid = grid.reshape((ny + 1, nx + 1)) - raster = np.zeros((height, width), dtype=np.uint8)#TODO bool - - #TODO shift by 0.5 - B = np.ones((2,2))/4 - raster = convolve2d(grid, B, 'valid') - - return raster -""" +# not used, using iclass_perimeter algorithm instead of scipy convolve2d + +# def RasterizePolygon(pol, height, min_h, width, min_w): +# +# # Joe Kington +# # https://stackoverflow.com/questions/3654289/scipy-create-2d-polygon-mask +# +# # poly_verts = [(1,1), (1,4), (4,4),(4,1), (1,1)] +# +# nx = width +# ny = height +# +# x, y = np.meshgrid( +# np.arange(-0.5 + min_w, nx + 0.5 + min_w, dtype=float), +# np.arange(-0.5 + min_h, ny + 0.5 + min_h, dtype=float), +# ) +# x, y = x.flatten(), y.flatten() +# +# points = np.vstack((x, y)).T +# +# p = Path(pol) +# grid = p.contains_points(points) +# grid = grid.reshape((ny + 1, nx + 1)) +# raster = np.zeros((height, width), dtype=np.uint8) # TODO bool +# +# # TODO shift by 0.5 +# B = np.ones((2, 2)) / 4 +# raster = convolve2d(grid, B, "valid") +# +# return raster def idScattToidBands(scatt_id, n_bands): diff --git a/gui/wxpython/iscatt/plots.py b/gui/wxpython/iscatt/plots.py index 30498c35fbe..1c081958fed 100644 --- a/gui/wxpython/iscatt/plots.py +++ b/gui/wxpython/iscatt/plots.py @@ -175,7 +175,7 @@ def OnRelease(self, event): self.canvas.draw() def OnPress(self, event): - "on button press we will see if the mouse is over us and store some data" + """on button press we will see if the mouse is over us and store some data""" if not event.inaxes: return if self.mode == "zoom_extend": @@ -189,7 +189,7 @@ def OnPress(self, event): self.zoom_rect_coords = None def _stopCategoryEdit(self): - "disconnect all the stored connection ids" + """disconnect all the stored connection ids""" if self.cidpress: self.canvas.mpl_disconnect(self.cidpress) @@ -389,7 +389,7 @@ def OnCanvasLeave(self, event): self.cursorMove.emit(x=None, y=None, scatt_id=self.scatt_id) def PanMotion(self, event): - "on mouse movement" + """on mouse movement""" if self.mode != "pan": return if event.inaxes is None: @@ -513,30 +513,29 @@ def MergeImg(cats_order, scatts, styles, rend_dt, output_queue): else: MergeArrays(merged_img, rend_dt[cat_id]["dt"], styles[cat_id]["opacity"]) - """ - # c_img_a = np.memmap( - # grass.tempfile(), dtype="uint16", mode="w+", shape=shape - # ) - c_img_a = colored_cat.astype("uint16")[:, :, 3] * styles[cat_id]["opacity"] - - # TODO apply strides and there will be no need for loop - # b = as_strided( - # a, - # strides=(0, a.strides[3], a.strides[3], a.strides[3]), - # shape=(3, a.shape[0], a.shape[1]), - # ) - - for i in range(3): - merged_img[:, :, i] = ( - merged_img[:, :, i] * (255 - c_img_a) - + colored_cat[:, :, i] * c_img_a - ) / 255 - merged_img[:, :, 3] = ( - merged_img[:, :, 3] * (255 - c_img_a) + 255 * c_img_a - ) / 255 - - del c_img_a - """ + # # c_img_a = np.memmap( + # # grass.tempfile(), dtype="uint16", mode="w+", shape=shape + # # ) + # c_img_a = colored_cat.astype("uint16")[:,:, 3] * styles[cat_id]["opacity"] + # + # # TODO apply strides and there will be no need for loop + # # b = as_strided( + # # a, + # # strides=(0, a.strides[3], a.strides[3], a.strides[3]), + # # shape=(3, a.shape[0], a.shape[1]), + # # ) + # + # for i in range(3): + # merged_img[:, :, i] = ( + # merged_img[:, :, i] * (255 - c_img_a) + # + colored_cat[:, :, i] * c_img_a + # ) / 255 + # merged_img[:, :, 3] = ( + # merged_img[:, :, 3] * (255 - c_img_a) + 255 * c_img_a + # ) / 255 + # + # del c_img_a + _rendDtMemmapsToFiles(rend_dt) merged_img = {"dt": merged_img.filename, "sh": merged_img.shape} @@ -633,10 +632,11 @@ class PolygonDrawer: def __init__(self, ax, pol, empty_pol): if pol.figure is None: - raise RuntimeError( + msg = ( "You must first add the polygon to a figure or canvas before defining " "the interactor" ) + raise RuntimeError(msg) self.ax = ax self.canvas = pol.figure.canvas @@ -667,13 +667,14 @@ def __init__(self, ax, pol, empty_pol): self.it = 0 def _getPolygonStyle(self): - style = {} - style["sel_pol"] = UserSettings.Get( - group="scatt", key="selection", subkey="sel_pol" - ) - style["sel_pol_vertex"] = UserSettings.Get( - group="scatt", key="selection", subkey="sel_pol_vertex" - ) + style = { + "sel_pol": UserSettings.Get( + group="scatt", key="selection", subkey="sel_pol" + ), + "sel_pol_vertex": UserSettings.Get( + group="scatt", key="selection", subkey="sel_pol_vertex" + ), + } style["sel_pol"] = [i / 255.0 for i in style["sel_pol"]] style["sel_pol_vertex"] = [i / 255.0 for i in style["sel_pol_vertex"]] @@ -739,14 +740,14 @@ def DrawCallback(self, event): self.ax.draw_artist(self.line) def poly_changed(self, pol): - "this method is called whenever the polygon object is called" + """this method is called whenever the polygon object is called""" # only copy the artist props to the line (except visibility) vis = self.line.get_visible() Artist.update_from(self.line, pol) self.line.set_visible(vis) # don't use the pol visibility state def get_ind_under_point(self, event): - "get the index of the vertex under point if within threshold" + """get the index of the vertex under point if within threshold""" # display coords xy = np.asarray(self.pol.xy) @@ -779,7 +780,7 @@ def OnButtonPressed(self, event): self.moving_ver_idx = self.get_ind_under_point(event) def ButtonReleaseCallback(self, event): - "whenever a mouse button is released" + """whenever a mouse button is released""" if not self.showverts: return if event.button != 1: @@ -859,7 +860,7 @@ def _addVertex(self, event): self.Redraw() def motion_notify_callback(self, event): - "on mouse movement" + """on mouse movement""" if self.mode != "move_vertex": return if not self.showverts: @@ -912,7 +913,8 @@ class ModestImage(mi.AxesImage): def __init__(self, minx=0.0, miny=0.0, *args, **kwargs): if "extent" in kwargs and kwargs["extent"] is not None: - raise NotImplementedError("ModestImage does not support extents") + msg = f"{ModestImage.__name__} does not support extents" + raise NotImplementedError(msg) self._full_res = None self._sx, self._sy = None, None @@ -932,12 +934,14 @@ def set_data(self, A): self._A = A if self._A.dtype != np.uint8 and not np.can_cast(self._A.dtype, float): - raise TypeError("Image data can not convert to float") + msg = "Image data can not convert to float" + raise TypeError(msg) if self._A.ndim not in (2, 3) or ( self._A.ndim == 3 and self._A.shape[-1] not in (3, 4) ): - raise TypeError("Invalid dimensions for image data") + msg = "Invalid dimensions for image data" + raise TypeError(msg) self._imcache = None self._rgbacache = None diff --git a/gui/wxpython/iscatt/toolbars.py b/gui/wxpython/iscatt/toolbars.py index 9633c147500..6745d401823 100644 --- a/gui/wxpython/iscatt/toolbars.py +++ b/gui/wxpython/iscatt/toolbars.py @@ -26,8 +26,7 @@ def get_tool_name(tool_name, tool_name_type=tuple): """Get tool name :param str|tuple tool_name: tool name - :param type tool_name_type: tool name type with default - tuple type + :param type tool_name_type: tool name type with default tuple type :return str: tool name """ diff --git a/gui/wxpython/lmgr/frame.py b/gui/wxpython/lmgr/frame.py index 1b69a517717..95e5ec0fd83 100644 --- a/gui/wxpython/lmgr/frame.py +++ b/gui/wxpython/lmgr/frame.py @@ -578,8 +578,7 @@ def CanCloseDisplay(askIfSaveWorkspace): map display notebook layers tree page index """ - pgnum_dict = {} - pgnum_dict["layers"] = self.notebookLayers.GetPageIndex(page) + pgnum_dict = {"layers": self.notebookLayers.GetPageIndex(page)} name = self.notebookLayers.GetPageText(pgnum_dict["layers"]) caption = _("Close Map Display {}").format(name) if not askIfSaveWorkspace or ( @@ -687,8 +686,7 @@ def show_demo(): def AddNvizTools(self, firstTime): """Add nviz notebook page - :param firstTime: if a mapdisplay is starting 3D mode for the - first time + :param firstTime: if a mapdisplay is starting 3D mode for the first time """ Debug.msg(5, "GMFrame.AddNvizTools()") from nviz.main import haveNviz @@ -970,8 +968,7 @@ def RunSpecialCmd(self, command): else: result = False raise ValueError( - "Layer Manager special command (%s)" - " not supported." % " ".join(command) + "Layer Manager special command (%s) not supported." % " ".join(command) ) if result: self._gconsole.UpdateHistory(status=Status.SUCCESS) @@ -1087,11 +1084,10 @@ def GetMapDisplay(self, onlyCurrent=True): return self.GetLayerTree().GetMapDisplay() return None # -> return list of all mapdisplays - mlist = [] - for idx in range(self.notebookLayers.GetPageCount()): - mlist.append(self.notebookLayers.GetPage(idx).maptree.GetMapDisplay()) - - return mlist + return [ + self.notebookLayers.GetPage(idx).maptree.GetMapDisplay() + for idx in range(self.notebookLayers.GetPageCount()) + ] def GetAllMapDisplays(self): """Get all (open) map displays""" @@ -1188,8 +1184,7 @@ def OnVDigit(self, event): GMessage( parent=self, message=_( - "Editing is allowed only for vector maps from the " - "current mapset." + "Editing is allowed only for vector maps from the current mapset." ), ) return @@ -1396,10 +1391,11 @@ def write_help(): # this is programmer's error # can be relaxed in future # but keep it strict unless needed otherwise - raise ValueError( - "OnChangeCWD cmd parameter must be list of" + msg = ( + f"{self.OnChangeCWD.__name__} cmd parameter must be list of" " length 1 or 2 and 'cd' as a first item" ) + raise ValueError(msg) if cmd and len(cmd) > 2: # this might be a user error write_beginning(command=cmd) @@ -1705,10 +1701,11 @@ def OnAnimationTool(self, event=None, cmd=None): tree = self.GetLayerTree() if tree: - rasters = [] - for layer in tree.GetSelectedLayers(checkedOnly=False): - if tree.GetLayerInfo(layer, key="type") == "raster": - rasters.append(tree.GetLayerInfo(layer, key="maplayer").GetName()) + rasters = [ + tree.GetLayerInfo(layer, key="maplayer").GetName() + for layer in tree.GetSelectedLayers(checkedOnly=False) + if tree.GetLayerInfo(layer, key="type") == "raster" + ] if len(rasters) >= 2: from core.layerlist import LayerList from animation.data import AnimLayer diff --git a/gui/wxpython/lmgr/giface.py b/gui/wxpython/lmgr/giface.py index 67669039d5d..065dfa1d411 100644 --- a/gui/wxpython/lmgr/giface.py +++ b/gui/wxpython/lmgr/giface.py @@ -56,7 +56,7 @@ def __init__(self, tree): def __len__(self): # The list constructor calls __len__ as an optimization if available, # causing a RecursionError - return len([layer for layer in self]) # noqa: C416 + return len([layer for layer in self]) # noqa: C416 # pylint: disable=R1721 def __iter__(self): """Iterates over the contents of the list.""" @@ -121,11 +121,11 @@ def CheckLayer(self, layer, checked=True): self._tree.CheckItem(layer._layer, checked=checked) def SelectLayer(self, layer, select=True): - "Select or unselect layer" + """Select or unselect layer""" self._tree.SelectItem(layer._layer, select) def ChangeLayer(self, layer, **kwargs): - "Change layer (cmd, ltype, opacity)" + """Change layer (cmd, ltype, opacity)""" if "cmd" in kwargs: layer._pydata[0]["cmd"] = kwargs["cmd"] layerName, found = GetLayerNameFromCmd(kwargs["cmd"], fullyQualified=True) diff --git a/gui/wxpython/lmgr/layertree.py b/gui/wxpython/lmgr/layertree.py index 350320d97a7..afed1cccc6f 100644 --- a/gui/wxpython/lmgr/layertree.py +++ b/gui/wxpython/lmgr/layertree.py @@ -1111,9 +1111,10 @@ def OnCopyMap(self, event): def OnHistogram(self, event): """Plot histogram for given raster map layer""" - rasterList = [] - for layer in self.GetSelectedLayers(): - rasterList.append(self.GetLayerInfo(layer, key="maplayer").GetName()) + rasterList = [ + self.GetLayerInfo(layer, key="maplayer").GetName() + for layer in self.GetSelectedLayers() + ] if not rasterList: GError( @@ -1151,11 +1152,12 @@ def OnUnivariateStats(self, event): def OnReportStats(self, event): """Print 2D statistics""" - rasters = [] # TODO: Implement self.GetSelectedLayers(ltype='raster') - for layer in self.GetSelectedLayers(): - if self.GetLayerInfo(layer, key="type") == "raster": - rasters.append(self.GetLayerInfo(layer, key="maplayer").GetName()) + rasters = [ + self.GetLayerInfo(layer, key="maplayer").GetName() + for layer in self.GetSelectedLayers() + if self.GetLayerInfo(layer, key="type") == "raster" + ] if rasters: self._giface.RunCmd( @@ -2256,9 +2258,7 @@ def ChangeLayer(self, item): win = self.FindWindowById(self.GetLayerInfo(item, key="ctrl")) if win.GetValue() is not None: cmd = win.GetValue().split(";") - cmdlist = [] - for c in cmd: - cmdlist.append(c.split(" ")) + cmdlist = [c.split(" ") for c in cmd] opac = 1.0 chk = self.IsItemChecked(item) hidden = not self.IsVisible(item) diff --git a/gui/wxpython/lmgr/statusbar.py b/gui/wxpython/lmgr/statusbar.py index ac4748bfe1f..c30fde67d5c 100644 --- a/gui/wxpython/lmgr/statusbar.py +++ b/gui/wxpython/lmgr/statusbar.py @@ -74,15 +74,14 @@ class SbMask: def __init__(self, parent, giface): self.name = "mask" - self.mask_layer = "MASK" self.parent = parent self.giface = giface self.widget = Button( - parent=parent, id=wx.ID_ANY, label=_(self.mask_layer), style=wx.NO_BORDER + parent=parent, id=wx.ID_ANY, label=_("Mask"), style=wx.NO_BORDER ) self.widget.Bind(wx.EVT_BUTTON, self.OnRemoveMask) self.widget.SetForegroundColour(wx.Colour(255, 0, 0)) - self.widget.SetToolTip(tip=_("Left mouse click to remove the MASK")) + self.widget.SetToolTip(tip=_("Left mouse click to remove the raster mask")) self.giface.currentMapsetChanged.connect(self.Refresh) if not watchdog_used: self.giface.grassdbChanged.connect(self.dbChanged) @@ -94,7 +93,12 @@ def dbChanged(self, map=None, newname=None): :param str map: map that is changed :param str newname: new map """ - if self.mask_layer in {map, newname}: + mask_layer = gs.parse_command("r.mask.status", format="json")["name"].split( + "@", maxsplit=1 + )[0] + # This assumes mask is always in the current mapset (or the event is triggered + # only for the current mapset). + if mask_layer in {map, newname}: self.Refresh() self.giface.updateMap.emit() @@ -124,9 +128,7 @@ def GetWidget(self): def Refresh(self): """Show mask in the statusbar if mask file found""" - if gs.find_file( - name=self.mask_layer, element="cell", mapset=gs.gisenv()["MAPSET"] - )["name"]: + if gs.parse_command("r.mask.status", format="json")["present"]: self.Show() else: self.Hide() @@ -134,20 +136,22 @@ def Refresh(self): def OnRemoveMask(self, event): dlg = wx.MessageDialog( self.parent, - message=_("Are you sure that you want to remove the MASK?"), - caption=_("Remove MASK"), + message=_("Are you sure that you want to remove the current raster mask?"), + caption=_("Remove raster mask"), style=wx.YES_NO | wx.YES_DEFAULT | wx.ICON_QUESTION, ) if dlg.ShowModal() != wx.ID_YES: dlg.Destroy() return RunCommand("r.mask", flags="r") + mask_full_name = gs.parse_command("r.mask.status", format="json")["name"] + mask_name, mask_mapset = mask_full_name.split("@", maxsplit=1) gisenv = gs.gisenv() self.giface.grassdbChanged.emit( grassdb=gisenv["GISDBASE"], location=gisenv["LOCATION_NAME"], - mapset=gisenv["MAPSET"], - map=self.mask_layer, + mapset=mask_mapset, + map=mask_name, action="delete", element="raster", ) diff --git a/gui/wxpython/lmgr/workspace.py b/gui/wxpython/lmgr/workspace.py index b1e7aa400f0..7971e3c4064 100644 --- a/gui/wxpython/lmgr/workspace.py +++ b/gui/wxpython/lmgr/workspace.py @@ -46,7 +46,7 @@ def __init__(self, lmgr, giface): self._giface.workspaceChanged.connect(self.WorkspaceChanged) def WorkspaceChanged(self): - "Update window title" + """Update window title""" self.workspaceChanged = True def New(self): @@ -431,36 +431,30 @@ def SaveToFile(self, filename): """Save layer tree layout to workspace file :return: True on success, False on error """ - tmpfile = tempfile.TemporaryFile(mode="w+b") - try: - WriteWorkspaceFile(lmgr=self.lmgr, file=tmpfile) - except Exception as e: - GError( - parent=self.lmgr, - message=_( - "Writing current settings to workspace file <%s> failed.\n" - "Error details: %s" + with tempfile.TemporaryFile(mode="w+b") as tmpfile: + try: + WriteWorkspaceFile(lmgr=self.lmgr, file=tmpfile) + except Exception as e: + GError( + parent=self.lmgr, + message=_( + "Writing current settings to workspace file <%s> failed.\n" + "Error details: %s" + ) + % (tmpfile, str(e)), ) - % (tmpfile, str(e)), - ) - return False - - try: - mfile = open(filename, "wb") - tmpfile.seek(0) - for line in tmpfile.readlines(): - mfile.write(line) - except OSError: - GError( - parent=self.lmgr, - message=_("Unable to open file <%s> for writing.") % filename, - ) - return False - - mfile.close() - + return False + try: + with open(filename, "wb") as mfile: + tmpfile.seek(0) + mfile.writelines(tmpfile.readlines()) + except OSError: + GError( + parent=self.lmgr, + message=_("Unable to open file <%s> for writing.") % filename, + ) + return False self.AddFileToHistory(file_path=filename) - return True def CanClosePage(self, caption): diff --git a/gui/wxpython/location_wizard/wizard.py b/gui/wxpython/location_wizard/wizard.py index 41d309178f8..885b80a26a5 100644 --- a/gui/wxpython/location_wizard/wizard.py +++ b/gui/wxpython/location_wizard/wizard.py @@ -34,48 +34,49 @@ @author Hamish Bowman (planetary ellipsoids) """ -import os -import locale -import functools +from __future__ import annotations +import functools +import locale +import os from pathlib import Path +from typing import TYPE_CHECKING import wx import wx.lib.mixins.listctrl as listmix from core import globalvar -if globalvar.wxPythonPhoenix: +if globalvar.wxPythonPhoenix or TYPE_CHECKING: from wx import adv as wiz - from wx.adv import Wizard - from wx.adv import WizardPageSimple + from wx.adv import Wizard, WizardPageSimple else: from wx import wizard as wiz from wx.wizard import Wizard from wx.wizard import WizardPageSimple -import wx.lib.scrolledpanel as scrolled +import wx.lib.scrolledpanel as scrolled from core import utils +from core.gcmd import GError, GWarning, RunCommand from core.utils import cmp -from core.gcmd import RunCommand, GError, GWarning from gui_core.widgets import GenericMultiValidator from gui_core.wrap import ( - SpinCtrl, - SearchCtrl, - StaticText, - TextCtrl, Button, CheckBox, - StaticBox, - NewId, - ListCtrl, HyperlinkCtrl, + ListCtrl, + NewId, + SearchCtrl, + SpinCtrl, + StaticBox, + StaticText, + TextCtrl, ) from location_wizard.dialogs import SelectTransformDialog +from grass.exceptions import OpenError from grass.grassdb.checks import location_exists -from grass.script import decode from grass.script import core as grass -from grass.exceptions import OpenError +from grass.script import decode global coordsys, north, south, east, west, resolution, wizerror, translist @@ -84,6 +85,9 @@ else: search_cancel_evt = wx.EVT_SEARCHCTRL_CANCEL_BTN +if TYPE_CHECKING: + from wx.adv import WizardEvent + class TitledPage(WizardPageSimple): """Class to make wizard pages. Generic methods to make labels, @@ -331,7 +335,7 @@ def OnBrowse(self, event): dlg.Destroy() - def OnPageChanging(self, event=None): + def OnPageChanging(self, event: WizardEvent | None = None) -> None: self.location = self.tlocation.GetValue() self.grassdatabase = self.tgisdbase.GetLabel() self.locTitle = self.tlocTitle.GetValue() @@ -416,7 +420,7 @@ def __init__(self, wizard, parent): self.Bind(wx.EVT_RADIOBUTTON, self.SetVal, id=self.radioXy.GetId()) self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage) - def OnEnterPage(self, event): + def OnEnterPage(self, event: WizardEvent) -> None: global coordsys if not coordsys: @@ -566,7 +570,7 @@ def __init__(self, wizard, parent): self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging) self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage) - def OnPageChanging(self, event): + def OnPageChanging(self, event: WizardEvent) -> None: if event.GetDirection() and self.proj not in self.parent.projections.keys(): event.Veto() @@ -598,7 +602,7 @@ def OnText(self, event): self.projdesc = self.parent.projections[self.proj][0] nextButton.Enable() - def OnEnterPage(self, event): + def OnEnterPage(self, event: WizardEvent) -> None: if len(self.proj) == 0: # disable 'next' button by default wx.FindWindowById(wx.ID_FORWARD).Enable(False) @@ -614,7 +618,7 @@ def OnSearch(self, event): self.proj, self.projdesc = self.projlist.Search( index=[0, 1], pattern=search_str ) - except: + except (IndexError, ValueError): self.proj = self.projdesc = "" event.Skip() @@ -892,7 +896,7 @@ def OnParamEntry(self, event): event.Skip() - def OnPageChange(self, event=None): + def OnPageChange(self, event: WizardEvent | None = None) -> None: """Go to next page""" if event.GetDirection(): self.p4projparams = "" @@ -914,7 +918,7 @@ def OnPageChange(self, event=None): " +" + param["proj4"] + "=" + str(param["value"]) ) - def OnEnterPage(self, event): + def OnEnterPage(self, event: WizardEvent) -> None: """Page entered""" self.projdesc = self.parent.projections[self.parent.projpage.proj][0] if self.prjParamSizer is None: @@ -1059,9 +1063,9 @@ def __init__(self, wizard, parent): self.searchb.ShowCancelButton(True) # create list control for datum/elipsoid list - data = [] - for key in self.parent.datums.keys(): - data.append([key, self.parent.datums[key][0], self.parent.datums[key][1]]) + data = [ + [key, datum[0], datum[1]] for (key, datum) in self.parent.datums.items() + ] self.datumlist = ItemList( self, data=data, columns=[_("Code"), _("Ellipsoid"), _("Description")] ) @@ -1115,7 +1119,7 @@ def __init__(self, wizard, parent): # do page layout # self.DoLayout() - def OnPageChanging(self, event): + def OnPageChanging(self, event: WizardEvent): self.proj4params = "" proj = self.parent.projpage.p4proj @@ -1159,7 +1163,7 @@ def OnPageChanging(self, event): self.ellipse ][1] - def OnEnterPage(self, event): + def OnEnterPage(self, event: WizardEvent) -> None: self.parent.datum_trans = None if event.GetDirection(): if len(self.datum) == 0: @@ -1184,15 +1188,15 @@ def OnDText(self, event): self.datumparams = self.parent.datums[self.datum][2] try: self.datumparams.remove("dx=0.0") - except: + except ValueError: pass try: self.datumparams.remove("dy=0.0") - except: + except ValueError: pass try: self.datumparams.remove("dz=0.0") - except: + except ValueError: pass nextButton.Enable(True) @@ -1207,7 +1211,7 @@ def OnDSearch(self, event): self.datum, self.ellipsoid, self.datumdesc = self.datumlist.Search( index=[0, 1, 2], pattern=search_str ) - except: + except (IndexError, ValueError): self.datum = self.datumdesc = self.ellipsoid = "" event.Skip() @@ -1258,10 +1262,10 @@ def __init__(self, wizard, parent): ) # create list control for ellipse list - data = [] # extract code, desc - for key in self.parent.ellipsoids.keys(): - data.append([key, self.parent.ellipsoids[key][0]]) + data = [ + [key, ellipsoid[0]] for (key, ellipsoid) in self.parent.ellipsoids.items() + ] self.ellipselist = ItemList( self, data=data, columns=[_("Code"), _("Description")] @@ -1330,7 +1334,7 @@ def __init__(self, wizard, parent): self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage) self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging) - def OnEnterPage(self, event): + def OnEnterPage(self, event: WizardEvent) -> None: if len(self.ellipse) == 0: # disable 'next' button by default wx.FindWindowById(wx.ID_FORWARD).Enable(False) @@ -1339,7 +1343,7 @@ def OnEnterPage(self, event): self.scope = "earth" event.Skip() - def OnPageChanging(self, event): + def OnPageChanging(self, event: WizardEvent) -> None: if ( event.GetDirection() and self.ellipse not in self.parent.ellipsoids @@ -1388,7 +1392,7 @@ def OnSearch(self, event): self.ellipseparams = self.parent.ellipsoids[self.ellipse][1] else: self.ellipseparams = self.parent.planetary_ellipsoids[self.ellipse][1] - except: + except (IndexError, ValueError, KeyError): self.ellipse = self.ellipsedesc = self.ellipseparams = "" event.Skip() @@ -1465,7 +1469,7 @@ def __init__(self, wizard, parent): # do page layout # self.DoLayout() - def OnEnterPage(self, event): + def OnEnterPage(self, event: WizardEvent) -> None: if len(self.georeffile) == 0: # disable 'next' button by default wx.FindWindowById(wx.ID_FORWARD).Enable(False) @@ -1474,7 +1478,7 @@ def OnEnterPage(self, event): event.Skip() - def OnPageChanging(self, event): + def OnPageChanging(self, event: WizardEvent) -> None: if event.GetDirection() and not os.path.isfile(self.georeffile): event.Veto() self.GetNext().SetPrev(self) @@ -1540,7 +1544,7 @@ def __init__(self, wizard, parent): self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging) self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage) - def OnEnterPage(self, event): + def OnEnterPage(self, event: WizardEvent) -> None: if len(self.wktstring) == 0: # disable 'next' button by default wx.FindWindowById(wx.ID_FORWARD).Enable(False) @@ -1549,7 +1553,7 @@ def OnEnterPage(self, event): event.Skip() - def OnPageChanging(self, event): + def OnPageChanging(self, event: WizardEvent) -> None: if event.GetDirection() and not self.wktstring.strip(): event.Veto() self.GetNext().SetPrev(self) @@ -1641,7 +1645,7 @@ def __init__(self, wizard, parent): self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging) self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage) - def OnEnterPage(self, event): + def OnEnterPage(self, event: WizardEvent) -> None: self.parent.datum_trans = None if event.GetDirection(): if not self.epsgcode: @@ -1654,7 +1658,7 @@ def OnEnterPage(self, event): event.Skip() - def OnPageChanging(self, event): + def OnPageChanging(self, event: WizardEvent): if event.GetDirection(): if not self.epsgcode: event.Veto() @@ -1749,10 +1753,11 @@ def OnBrowseCodes(self, event, search=None): self.epsglist.Populate([], update=True) return - data = [] - for code, val in self.epsgCodeDict.items(): - if code is not None: - data.append((code, val[0], val[1])) + data = [ + (code, val[0], val[1]) + for code, val in self.epsgCodeDict.items() + if code is not None + ] self.epsglist.Populate(data, update=True) @@ -1859,7 +1864,7 @@ def __init__(self, wizard, parent): self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging) self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage) - def OnEnterPage(self, event): + def OnEnterPage(self, event: WizardEvent) -> None: self.parent.datum_trans = None if event.GetDirection(): if not self.epsgcode: @@ -1873,7 +1878,7 @@ def OnEnterPage(self, event): event.Skip() - def OnPageChanging(self, event): + def OnPageChanging(self, event: WizardEvent): if event.GetDirection(): if not self.epsgcode: event.Veto() @@ -1930,7 +1935,7 @@ def OnText(self, event): self.epsgcode = event.GetString() try: self.epsgcode = int(self.epsgcode) - except: + except ValueError: self.epsgcode = None nextButton = wx.FindWindowById(wx.ID_FORWARD) @@ -2021,10 +2026,11 @@ def OnBrowseCodes(self, event, search=None): self.epsglist.Populate([], update=True) return - data = [] - for code, val in self.epsgCodeDict.items(): - if code is not None: - data.append((code, val[0], val[1])) + data = [ + (code, val[0], val[1]) + for code, val in self.epsgCodeDict.items() + if code is not None + ] self.epsglist.Populate(data, update=True) @@ -2070,14 +2076,14 @@ def __init__(self, wizard, parent): self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnPageChanging) self.Bind(wiz.EVT_WIZARD_PAGE_CHANGED, self.OnEnterPage) - def OnEnterPage(self, event): + def OnEnterPage(self, event: WizardEvent) -> None: if len(self.customstring) == 0: # disable 'next' button by default wx.FindWindowById(wx.ID_FORWARD).Enable(False) else: wx.FindWindowById(wx.ID_FORWARD).Enable(True) - def OnPageChanging(self, event): + def OnPageChanging(self, event: WizardEvent): if event.GetDirection(): self.custom_dtrans_string = "" @@ -2268,7 +2274,7 @@ def _doLayout(self): self.sizer.AddGrowableRow(4, 1) self.sizer.AddGrowableRow(5, 5) - def OnEnterPage(self, event): + def OnEnterPage(self, event: WizardEvent) -> None: """Insert values into text controls for summary of location creation options """ @@ -2283,10 +2289,7 @@ def OnEnterPage(self, event): # print coordsys,proj4string if coordsys in {"proj", "epsg", "iau", "wkt", "file"}: - extra_opts = {} - extra_opts["project"] = "project" - extra_opts["getErrorMsg"] = True - extra_opts["read"] = True + extra_opts = {"project": "project", "getErrorMsg": True, "read": True} if coordsys == "proj": if len(datum) > 0: @@ -2537,89 +2540,86 @@ def __readData(self): """Get georeferencing information from tables in $GISBASE/etc/proj""" # read projection and parameters - f = open(os.path.join(globalvar.ETCDIR, "proj", "parms.table")) self.projections = {} self.projdesc = {} - for line in f: - line = line.strip() - try: - proj, projdesc, params = line.split(":") - paramslist = params.split(";") - plist = [] - for p in paramslist: - if p == "": - continue - p1, pdefault = p.split(",") - pterm, pask = p1.split("=") - p = [pterm.strip(), pask.strip(), pdefault.strip()] - plist.append(p) - self.projections[proj.lower().strip()] = (projdesc.strip(), plist) - self.projdesc[proj.lower().strip()] = projdesc.strip() - except: - continue - f.close() + with open(os.path.join(globalvar.ETCDIR, "proj", "parms.table")) as f: + for line in f: + line = line.strip() + try: + proj, projdesc, params = line.split(":") + paramslist = params.split(";") + plist = [] + for p in paramslist: + if p == "": + continue + p1, pdefault = p.split(",") + pterm, pask = p1.split("=") + p = [pterm.strip(), pask.strip(), pdefault.strip()] + plist.append(p) + self.projections[proj.lower().strip()] = (projdesc.strip(), plist) + self.projdesc[proj.lower().strip()] = projdesc.strip() + except (ValueError, IndexError): + continue # read datum definitions - f = open(os.path.join(globalvar.ETCDIR, "proj", "datum.table")) self.datums = {} paramslist = [] - for line in f: - line = line.expandtabs(1) - line = line.strip() - if line == "" or line[0] == "#": - continue - datum, info = line.split(" ", 1) - info = info.strip() - datumdesc, params = info.split(" ", 1) - datumdesc = datumdesc.strip('"') - paramlist = params.split() - ellipsoid = paramlist.pop(0) - self.datums[datum] = (ellipsoid, datumdesc.replace("_", " "), paramlist) - f.close() + with open(os.path.join(globalvar.ETCDIR, "proj", "datum.table")) as f: + for line in f: + line = line.expandtabs(1) + line = line.strip() + if line == "" or line[0] == "#": + continue + datum, info = line.split(" ", 1) + info = info.strip() + datumdesc, params = info.split(" ", 1) + datumdesc = datumdesc.strip('"') + paramlist = params.split() + ellipsoid = paramlist.pop(0) + self.datums[datum] = (ellipsoid, datumdesc.replace("_", " "), paramlist) # read Earth-based ellipsiod definitions - f = open(os.path.join(globalvar.ETCDIR, "proj", "ellipse.table")) self.ellipsoids = {} - for line in f: - line = line.expandtabs(1) - line = line.strip() - if line == "" or line[0] == "#": - continue - ellipse, rest = line.split(" ", 1) - rest = rest.strip('" ') - desc, params = rest.split('"', 1) - desc = desc.strip('" ') - paramslist = params.split() - self.ellipsoids[ellipse] = (desc, paramslist) - f.close() + with open(os.path.join(globalvar.ETCDIR, "proj", "ellipse.table")) as f: + for line in f: + line = line.expandtabs(1) + line = line.strip() + if line == "" or line[0] == "#": + continue + ellipse, rest = line.split(" ", 1) + rest = rest.strip('" ') + desc, params = rest.split('"', 1) + desc = desc.strip('" ') + paramslist = params.split() + self.ellipsoids[ellipse] = (desc, paramslist) # read Planetary ellipsiod definitions - f = open(os.path.join(globalvar.ETCDIR, "proj", "ellipse.table.solar.system")) self.planetary_ellipsoids = {} - for line in f: - line = line.expandtabs(1) - line = line.strip() - if line == "" or line[0] == "#": - continue - ellipse, rest = line.split(" ", 1) - rest = rest.strip('" ') - desc, params = rest.split('"', 1) - desc = desc.strip('" ') - paramslist = params.split() - self.planetary_ellipsoids[ellipse] = (desc, paramslist) - f.close() + with open( + os.path.join(globalvar.ETCDIR, "proj", "ellipse.table.solar.system") + ) as f: + for line in f: + line = line.expandtabs(1) + line = line.strip() + if line == "" or line[0] == "#": + continue + ellipse, rest = line.split(" ", 1) + rest = rest.strip('" ') + desc, params = rest.split('"', 1) + desc = desc.strip('" ') + paramslist = params.split() + self.planetary_ellipsoids[ellipse] = (desc, paramslist) # read projection parameter description and parsing table - f = open(os.path.join(globalvar.ETCDIR, "proj", "desc.table")) self.paramdesc = {} - for line in f: - line = line.strip() - try: - pparam, datatype, proj4term, desc = line.split(":") - self.paramdesc[pparam] = (datatype, proj4term, desc) - except: - continue - f.close() + with open(os.path.join(globalvar.ETCDIR, "proj", "desc.table")) as f: + for line in f: + line = line.strip() + try: + pparam, datatype, proj4term, desc = line.split(":") + self.paramdesc[pparam] = (datatype, proj4term, desc) + except ValueError: + continue def OnWizFinished(self): """Wizard finished, create new location @@ -2769,7 +2769,7 @@ def CreateProj4String(self): return "%s +no_defs" % proj4string - def OnHelp(self, event): + def OnHelp(self, event: WizardEvent) -> None: """'Help' button clicked""" # help text in lib/init/helptext.html diff --git a/gui/wxpython/main_window/frame.py b/gui/wxpython/main_window/frame.py index a4a4dfea8e2..dba017f6ec8 100644 --- a/gui/wxpython/main_window/frame.py +++ b/gui/wxpython/main_window/frame.py @@ -521,9 +521,11 @@ def CanCloseDisplay(askIfSaveWorkspace): map display notebook page index (single window mode) """ - pgnum_dict = {} - pgnum_dict["layers"] = self.notebookLayers.GetPageIndex(page) - pgnum_dict["mainnotebook"] = self.mainnotebook.GetPageIndex(mapdisplay) + pgnum_dict = { + "layers": self.notebookLayers.GetPageIndex(page), + "mainnotebook": self.mainnotebook.GetPageIndex(mapdisplay), + } + name = self.notebookLayers.GetPageText(pgnum_dict["layers"]) caption = _("Close Map Display {}").format(name) if not askIfSaveWorkspace or ( @@ -790,8 +792,7 @@ def show_demo(): def AddNvizTools(self, firstTime): """Add nviz notebook page - :param firstTime: if a mapdisplay is starting 3D mode for the - first time + :param firstTime: if a mapdisplay is starting 3D mode for the first time """ Debug.msg(5, "GMFrame.AddNvizTools()") from nviz.main import haveNviz @@ -1051,7 +1052,7 @@ def _closePageNoEvent(self, pgnum_dict, is_docked): notebook layers tree page index and "mainnotebook" key represent map display notebook page index (single window mode) - boolean is_docked: "True" means that map display is docked in map + :param boolean is_docked: "True" means that map display is docked in map display notebook, "False" means that map display is undocked to independent frame """ @@ -1108,8 +1109,7 @@ def RunSpecialCmd(self, command): else: result = False raise ValueError( - "Layer Manager special command (%s)" - " not supported." % " ".join(command) + "Layer Manager special command (%s) not supported." % " ".join(command) ) if result: self._gconsole.UpdateHistory(status=Status.SUCCESS) @@ -1239,11 +1239,10 @@ def GetMapDisplay(self, onlyCurrent=True): return self.GetLayerTree().GetMapDisplay() return None # -> return list of all mapdisplays - mlist = [] - for idx in range(self.notebookLayers.GetPageCount()): - mlist.append(self.notebookLayers.GetPage(idx).maptree.GetMapDisplay()) - - return mlist + return [ + self.notebookLayers.GetPage(idx).maptree.GetMapDisplay() + for idx in range(self.notebookLayers.GetPageCount()) + ] def GetAllMapDisplays(self): """Get all (open) map displays""" @@ -1340,8 +1339,7 @@ def OnVDigit(self, event): GMessage( parent=self, message=_( - "Editing is allowed only for vector maps from the " - "current mapset." + "Editing is allowed only for vector maps from the current mapset." ), ) return @@ -1548,10 +1546,11 @@ def write_help(): # this is programmer's error # can be relaxed in future # but keep it strict unless needed otherwise - raise ValueError( - "OnChangeCWD cmd parameter must be list of" + msg = ( + f"{self.OnChangeCWD.__name__} cmd parameter must be a list of" " length 1 or 2 and 'cd' as a first item" ) + raise ValueError(msg) if cmd and len(cmd) > 2: # this might be a user error write_beginning(command=cmd) @@ -1856,10 +1855,11 @@ def OnAnimationTool(self, event=None, cmd=None): tree = self.GetLayerTree() if tree: - rasters = [] - for layer in tree.GetSelectedLayers(checkedOnly=False): - if tree.GetLayerInfo(layer, key="type") == "raster": - rasters.append(tree.GetLayerInfo(layer, key="maplayer").GetName()) + rasters = [ + tree.GetLayerInfo(layer, key="maplayer").GetName() + for layer in tree.GetSelectedLayers(checkedOnly=False) + if tree.GetLayerInfo(layer, key="type") == "raster" + ] if len(rasters) >= 2: from core.layerlist import LayerList from animation.data import AnimLayer diff --git a/gui/wxpython/mapdisp/frame.py b/gui/wxpython/mapdisp/frame.py index a7622616b34..52eae9f6224 100644 --- a/gui/wxpython/mapdisp/frame.py +++ b/gui/wxpython/mapdisp/frame.py @@ -22,46 +22,53 @@ @author Stepan Turek (handlers support) """ -import os +from __future__ import annotations + import copy +import os +from typing import TYPE_CHECKING from core import globalvar + +# isort: split import wx import wx.aui - -from mapdisp.toolbars import MapToolbar, NvizIcons -from mapdisp.gprint import PrintOptions -from core.gcmd import GError, GMessage, RunCommand -from core.utils import ListOfCatsToRange, GetLayerNameFromCmd -from gui_core.dialogs import GetImageHandlers, ImageSizeDialog from core.debug import Debug +from core.gcmd import GError, GMessage, RunCommand +from core.giface import Notification from core.settings import UserSettings -from gui_core.mapdisp import SingleMapPanel, FrameMixin -from gui_core.query import QueryDialog, PrepareQueryResults +from core.utils import GetLayerNameFromCmd, ListOfCatsToRange +from gui_core.dialogs import GetImageHandlers, ImageSizeDialog +from gui_core.forms import GUI +from gui_core.mapdisp import FrameMixin, SingleMapPanel +from gui_core.query import PrepareQueryResults, QueryDialog +from gui_core.vselect import VectorSelectBase, VectorSelectHighlighter +from gui_core.wrap import Menu +from main_window.page import MainPageBase +from mapdisp import statusbar as sb +from mapdisp.gprint import PrintOptions +from mapdisp.toolbars import MapToolbar, NvizIcons +from mapwin.analysis import ( + MeasureAreaController, + MeasureDistanceController, + ProfileController, +) from mapwin.buffered import BufferedMapWindow from mapwin.decorations import ( - LegendController, - BarscaleController, ArrowController, + BarscaleController, DtextController, + LegendController, LegendVectController, ) -from mapwin.analysis import ( - ProfileController, - MeasureDistanceController, - MeasureAreaController, -) -from gui_core.forms import GUI -from core.giface import Notification -from gui_core.vselect import VectorSelectBase, VectorSelectHighlighter -from gui_core.wrap import Menu -from mapdisp import statusbar as sb -from main_window.page import MainPageBase import grass.script as gs - from grass.pydispatch.signal import Signal +if TYPE_CHECKING: + import lmgr.frame + import main_window.frame + class MapPanel(SingleMapPanel, MainPageBase): """Main panel for map display window. Drawing takes place in @@ -76,7 +83,7 @@ def __init__( toolbars=["map"], statusbar=True, tree=None, - lmgr=None, + lmgr: main_window.frame.GMFrame | lmgr.frame.GMFrame | None = None, Map=None, auimgr=None, dockable=False, @@ -296,7 +303,7 @@ def GetMapWindow(self): def _addToolbarVDigit(self): """Add vector digitizer toolbar""" - from vdigit.main import haveVDigit, VDigit + from vdigit.main import VDigit, haveVDigit from vdigit.toolbars import VDigitToolbar if not haveVDigit: @@ -401,7 +408,7 @@ def _updateVDigitLayers(self, layer): def AddNviz(self): """Add 3D view mode window""" - from nviz.main import haveNviz, GLWindow, errorMsg + from nviz.main import GLWindow, errorMsg, haveNviz # check for GLCanvas and OpenGL if not haveNviz: @@ -1142,9 +1149,7 @@ def _queryHighlight(self, vectQuery): self._highlighter_layer.SetMap( vectQuery[0]["Map"] + "@" + vectQuery[0]["Mapset"] ) - tmp = [] - for i in vectQuery: - tmp.append(i["Category"]) + tmp = [i["Category"] for i in vectQuery] self._highlighter_layer.SetCats(tmp) self._highlighter_layer.DrawSelected() @@ -1158,7 +1163,9 @@ def OnQuery(self, event): # change the cursor self.MapWindow.SetNamedCursor("cross") - def AddTmpVectorMapLayer(self, name, cats, useId=False, addLayer=True): + def AddTmpVectorMapLayer( + self, name, cats, useId: bool = False, addLayer: bool = True + ): """Add temporal vector map layer to map composition :param name: name of map layer @@ -1277,11 +1284,11 @@ def Profile(self, rasters=None): def OnHistogramPyPlot(self, event): """Init PyPlot histogram display canvas and tools""" - raster = [] - - for layer in self._giface.GetLayerList().GetSelectedLayers(): - if layer.maplayer.GetType() == "raster": - raster.append(layer.maplayer.GetName()) + raster = [ + layer.maplayer.GetName() + for layer in self._giface.GetLayerList().GetSelectedLayers() + if layer.maplayer.GetType() == "raster" + ] from wxplot.histogram import HistogramPlotFrame @@ -1291,11 +1298,11 @@ def OnHistogramPyPlot(self, event): def OnScatterplot(self, event): """Init PyPlot scatterplot display canvas and tools""" - raster = [] - - for layer in self._giface.GetLayerList().GetSelectedLayers(): - if layer.maplayer.GetType() == "raster": - raster.append(layer.maplayer.GetName()) + raster = [ + layer.maplayer.GetName() + for layer in self._giface.GetLayerList().GetSelectedLayers() + if layer.maplayer.GetType() == "raster" + ] from wxplot.scatter import ScatterFrame @@ -1601,7 +1608,7 @@ def _switchMapWindow(self, map_win): def AddRDigit(self): """Adds raster digitizer: creates toolbar and digitizer controller, binds events and signals.""" - from rdigit.controller import RDigitController, EVT_UPDATE_PROGRESS + from rdigit.controller import EVT_UPDATE_PROGRESS, RDigitController from rdigit.toolbars import RDigitToolbar self.rdigit = RDigitController(self._giface, mapWindow=self.GetMapWindow()) diff --git a/gui/wxpython/mapdisp/gprint.py b/gui/wxpython/mapdisp/gprint.py index 3172f58eab5..e95ca1003f7 100644 --- a/gui/wxpython/mapdisp/gprint.py +++ b/gui/wxpython/mapdisp/gprint.py @@ -26,21 +26,6 @@ def __init__(self, canvas): wx.Printout.__init__(self) self.canvas = canvas - def OnBeginDocument(self, start, end): - return super().OnBeginDocument(start, end) - - def OnEndDocument(self): - super().OnEndDocument() - - def OnBeginPrinting(self): - super().OnBeginPrinting() - - def OnEndPrinting(self): - super().OnEndPrinting() - - def OnPreparePrinting(self): - super().OnPreparePrinting() - def HasPage(self, page) -> bool: return page <= 2 diff --git a/gui/wxpython/mapdisp/main.py b/gui/wxpython/mapdisp/main.py index 6449bc35d7c..76ae6218d5a 100644 --- a/gui/wxpython/mapdisp/main.py +++ b/gui/wxpython/mapdisp/main.py @@ -123,9 +123,8 @@ def GetLayersFromCmdFile(self): nlayers = 0 try: - fd = open(self.cmdfile) - lines = fd.readlines() - fd.close() + with open(self.cmdfile) as fd: + lines = fd.readlines() # detect d.out.file, delete the line from the cmd file and export # graphics if len(lines) > 0: @@ -133,9 +132,8 @@ def GetLayersFromCmdFile(self): "d.to.rast" ): dCmd = lines[-1].strip() - fd = open(self.cmdfile, "w") - fd.writelines(lines[:-1]) - fd.close() + with open(self.cmdfile, "w") as fd: + fd.writelines(lines[:-1]) if lines[-1].startswith("d.out.file"): self.saveToFile.emit(cmd=utils.split(dCmd)) else: @@ -143,9 +141,8 @@ def GetLayersFromCmdFile(self): return if lines[-1].startswith("d.what"): dWhatCmd = lines[-1].strip() - fd = open(self.cmdfile, "w") - fd.writelines(lines[:-1]) - fd.close() + with open(self.cmdfile, "w") as fd: + fd.writelines(lines[:-1]) if "=" in utils.split(dWhatCmd)[1]: maps = utils.split(dWhatCmd)[1].split("=")[1].split(",") else: @@ -176,7 +173,7 @@ def GetLayersFromCmdFile(self): mapFile = line.split("=", 1)[1].strip() try: k, v = line[2:].strip().split("=", 1) - except: + except (ValueError, IndexError): pass render_env[k] = v continue @@ -655,11 +652,10 @@ def GetMapDisplay(self): # create pid file pidFile = os.path.join(monPath, "pid") - fd = open(pidFile, "w") - if not fd: - grass.fatal(_("Unable to create file <%s>") % pidFile) - fd.write("%s\n" % os.getpid()) - fd.close() + with open(pidFile, "w") as fd: + if not fd: + grass.fatal(_("Unable to create file <%s>") % pidFile) + fd.write("%s\n" % os.getpid()) RunCommand("g.gisenv", set="MONITOR_%s_PID=%d" % (monName.upper(), os.getpid())) diff --git a/gui/wxpython/mapdisp/statusbar.py b/gui/wxpython/mapdisp/statusbar.py index ca69416af85..7e60e762281 100644 --- a/gui/wxpython/mapdisp/statusbar.py +++ b/gui/wxpython/mapdisp/statusbar.py @@ -223,11 +223,13 @@ def Reposition(self): It should not be necessary to call it manually. """ - widgets = [] - for item in self.statusbarItems.values(): - widgets.append((item.GetPosition(), item.GetWidget())) - - widgets.append((1, self.progressbar.GetWidget())) + widgets = [ + *[ + (item.GetPosition(), item.GetWidget()) + for item in self.statusbarItems.values() + ], + (1, self.progressbar.GetWidget()), + ] for idx, win in widgets: if not win: diff --git a/gui/wxpython/mapdisp/test_mapdisp.py b/gui/wxpython/mapdisp/test_mapdisp.py index 1494988fafb..d2265e235b7 100755 --- a/gui/wxpython/mapdisp/test_mapdisp.py +++ b/gui/wxpython/mapdisp/test_mapdisp.py @@ -44,7 +44,7 @@ # %end """ -Module to run test map window (BufferedWidnow) and map display (MapFrame). +Module to run test map window (BufferedMapWindow) and map display (MapFrame). @author Vaclav Petras """ @@ -267,10 +267,11 @@ def testMapWindowProfile(self, giface, map_): self.controller = ProfileController(giface, window) self.controller.Start() - rasters = [] - for layer in giface.GetLayerList().GetSelectedLayers(): - if layer.maplayer.GetType() == "raster": - rasters.append(layer.maplayer.GetName()) + rasters = [ + layer.maplayer.GetName() + for layer in giface.GetLayerList().GetSelectedLayers() + if layer.maplayer.GetType() == "raster" + ] from wxplot.profile import ProfileFrame diff --git a/gui/wxpython/mapdisp/toolbars.py b/gui/wxpython/mapdisp/toolbars.py index 18459b26e17..9b0eb4a9c69 100644 --- a/gui/wxpython/mapdisp/toolbars.py +++ b/gui/wxpython/mapdisp/toolbars.py @@ -67,8 +67,7 @@ img="flythrough", label=_("Fly-through mode"), desc=_( - "Drag with mouse, hold Ctrl down for different mode" - " or Shift to accelerate" + "Drag with mouse, hold Ctrl down for different mode or Shift to accelerate" ), ), "zoomIn": BaseIcons["zoomIn"].SetLabel(desc=_("Click mouse to zoom")), diff --git a/gui/wxpython/mapswipe/g.gui.mapswipe.html b/gui/wxpython/mapswipe/g.gui.mapswipe.html index ae4fe135a0f..105cf612c74 100644 --- a/gui/wxpython/mapswipe/g.gui.mapswipe.html +++ b/gui/wxpython/mapswipe/g.gui.mapswipe.html @@ -6,7 +6,7 @@

    DESCRIPTION

    which allows the user to interactively compare two raster maps of the same area by revealing different parts of the raster maps. It is useful e.g. for comparing raster maps from different time periods. -Map Swipe can be launched from the menu File -> Map Swipe. +Map Swipe can be launched from the menu File -> Map Swipe.

    Map Swipe allows you to: @@ -32,7 +32,7 @@

    DESCRIPTION

    MODIS image on March 13, 2011, shows a clear view of tsunami flooding along the coastline. Water, black and dark blue in these false-color images, still covers the ground as much as five kilometers (three miles) from the coast. -Source: Earth Observatory/NASA +Source: Earth Observatory/NASA
    diff --git a/gui/wxpython/mapswipe/mapwindow.py b/gui/wxpython/mapswipe/mapwindow.py index a203ef2e6d1..279ec103609 100644 --- a/gui/wxpython/mapswipe/mapwindow.py +++ b/gui/wxpython/mapswipe/mapwindow.py @@ -17,13 +17,15 @@ @author Anna Kratochvilova """ -import wx +from __future__ import annotations + +from typing import Literal +import wx from core.debug import Debug from core.settings import UserSettings +from gui_core.wrap import NewId, Rect from mapwin.buffered import BufferedMapWindow -from gui_core.wrap import Rect, NewId - EVT_MY_MOUSE_EVENTS = wx.NewEventType() EVT_MY_MOTION = wx.NewEventType() @@ -38,17 +40,17 @@ class SwipeBufferedWindow(BufferedMapWindow): Special mouse events with changed coordinates are used. """ - def __init__(self, parent, giface, Map, properties, **kwargs): + def __init__(self, parent, giface, Map, properties, **kwargs) -> None: BufferedMapWindow.__init__( self, parent=parent, giface=giface, Map=Map, properties=properties, **kwargs ) Debug.msg(2, "SwipeBufferedWindow.__init__()") - self.specialSize = super().GetClientSize() + self.specialSize: wx.Size = super().GetClientSize() self.specialCoords = [0, 0] self.imageId = 99 self.movingSash = False - self._mode = "swipe" + self._mode: Literal["swipe", "mirror"] = "swipe" self.lineid = NewId() def _bindMouseEvents(self): @@ -77,18 +79,18 @@ def _mouseActions(self, event): def _mouseMotion(self, event): self._RaiseMouseEvent(event, EVT_MY_MOTION) - def GetClientSize(self): + def GetClientSize(self) -> wx.Size: """Overridden method which returns simulated window size.""" if self._mode == "swipe": return self.specialSize return super().GetClientSize() - def SetClientSize(self, size): + def SetClientSize(self, size: wx.Size) -> None: """Overridden method which sets simulated window size.""" Debug.msg(3, "SwipeBufferedWindow.SetClientSize(): size = %s" % size) self.specialSize = size - def SetMode(self, mode): + def SetMode(self, mode: Literal["swipe", "mirror"]) -> None: """Sets mode of the window. :param mode: mode can be 'swipe' or 'mirror' @@ -101,7 +103,7 @@ def GetImageCoords(self): return self.specialCoords return (0, 0) - def SetImageCoords(self, coords): + def SetImageCoords(self, coords) -> None: """Sets coordinates of rendered image""" Debug.msg( 3, diff --git a/gui/wxpython/mapswipe/toolbars.py b/gui/wxpython/mapswipe/toolbars.py index 943eddaa1ec..4788578987b 100644 --- a/gui/wxpython/mapswipe/toolbars.py +++ b/gui/wxpython/mapswipe/toolbars.py @@ -55,7 +55,7 @@ def __init__(self, parent, toolSwitcher): def _toolbarData(self): """Returns toolbar data (name, icon, handler)""" # BaseIcons are a set of often used icons. It is possible - # to reuse icons in ./trunk/gui/icons/grass or add new ones there. + # to reuse icons in gui/icons/grass or add new ones there. icons = BaseIcons return self._getToolbarData( ( diff --git a/gui/wxpython/mapwin/base.py b/gui/wxpython/mapwin/base.py index c05cc8f840b..d141fe44382 100644 --- a/gui/wxpython/mapwin/base.py +++ b/gui/wxpython/mapwin/base.py @@ -220,7 +220,7 @@ def HandlersCaller(self, event, handlers): for handler in handlers: try: handler(event) - except: + except Exception: handlers.remove(handler) GError( parent=self, @@ -271,7 +271,6 @@ def OnMouseAction(self, event): ) event.Skip() - Emits mouseHandlerRegistered signal before handler is registered. :param event: one of mouse events @@ -309,7 +308,7 @@ def UnregisterAllHandlers(self): try: handler("unregistered") handlers.remove(handler) - except: + except Exception: GError( parent=self, message=_( @@ -348,7 +347,7 @@ def UnregisterMouseEventHandler(self, event, handler): grass.warning( _("Handler: %s was not registered") % handler.__name__ ) - except: + except Exception: GError( parent=self, message=_( diff --git a/gui/wxpython/mapwin/buffered.py b/gui/wxpython/mapwin/buffered.py index e30bf99414c..a808875f2a8 100644 --- a/gui/wxpython/mapwin/buffered.py +++ b/gui/wxpython/mapwin/buffered.py @@ -555,14 +555,14 @@ def TextBounds(self, textinfo, relcoords=False): boxh = math.fabs(math.sin(math.radians(rotation)) * w) + h boxw = math.fabs(math.cos(math.radians(rotation)) * w) + h - if rotation > 0 and rotation < 90: + if 0 < rotation < 90: bbox[1] -= boxh relCoords = (0, boxh) - elif rotation >= 90 and rotation < 180: + elif 90 <= rotation < 180: bbox[0] -= boxw bbox[1] -= boxh relCoords = (boxw, boxh) - elif rotation >= 180 and rotation < 270: + elif 180 <= rotation < 270: bbox[0] -= boxw relCoords = (boxw, 0) bbox[2] = boxw @@ -743,8 +743,9 @@ def _saveToFileDone(self, callback=None): # draw any active and defined overlays if self.imagedict[img]["layer"].IsActive(): id = self.imagedict[img]["id"] - coords = int(ratio[0] * self.overlays[id].coords[0]), int( - ratio[1] * self.overlays[id].coords[1] + coords = ( + int(ratio[0] * self.overlays[id].coords[0]), + int(ratio[1] * self.overlays[id].coords[1]), ) self.Draw( self.pdc, @@ -1033,7 +1034,7 @@ def _updateMFinished(self, renderVector=True): for item in self.graphicsSetList: try: item.Draw() - except: + except Exception: GError( parent=self, message=_( @@ -1062,16 +1063,13 @@ def DrawCompRegionExtent(self): dispReg = self.Map.GetCurrentRegion() reg = dispReg if utils.isInRegion(dispReg, compReg) else compReg - regionCoords = [] - regionCoords.extend( - ( - (reg["w"], reg["n"]), - (reg["e"], reg["n"]), - (reg["e"], reg["s"]), - (reg["w"], reg["s"]), - (reg["w"], reg["n"]), - ) - ) + regionCoords = [ + (reg["w"], reg["n"]), + (reg["e"], reg["n"]), + (reg["e"], reg["s"]), + (reg["w"], reg["s"]), + (reg["w"], reg["n"]), + ] # draw region extent self.polypen = wx.Pen( @@ -1183,10 +1181,7 @@ def MouseDraw(self, pdc=None, begin=None, end=None): if isinstance(r, list): r = Rect(r[0], r[1], r[2], r[3]) r.Inflate(4, 4) - try: - pdc.ClearId(boxid) - except: - pass + pdc.ClearId(boxid) self.RefreshRect(r, False) pdc.SetId(boxid) self.Draw(pdc, drawid=boxid, pdctype="box", coords=mousecoords) @@ -1200,10 +1195,7 @@ def MouseDraw(self, pdc=None, begin=None, end=None): y2 = max(begin[1], end[1]) r = Rect(x1, y1, x2 - x1, y2 - y1) r.Inflate(4, 4) - try: - pdc.ClearId(self.lineid) - except: - pass + pdc.ClearId(self.lineid) self.RefreshRect(r, False) pdc.SetId(self.lineid) self.Draw(pdc, drawid=self.lineid, pdctype="line", coords=mousecoords) @@ -1214,8 +1206,7 @@ def DrawLines(self, pdc=None, polycoords=None): Set self.pline to wx.NEW_ID + 1 :param polycoords: list of polyline vertices, geographical - coordinates (if not given, self.polycoords - is used) + coordinates (if not given, self.polycoords is used) """ if not pdc: pdc = self.pdcTmp @@ -1546,8 +1537,6 @@ def OnLeftDown(self, event): if idlist != []: self.dragid = idlist[0] # drag whatever is on top - else: - pass coords = self.Pixel2Cell(self.mouse["begin"]) self.mouseLeftDown.emit(x=coords[0], y=coords[1]) @@ -1596,8 +1585,6 @@ def OnLeftUp(self, event): self.textdict[self.dragid]["bbox"] = self.pdc.GetIdBounds( self.dragid ) - else: - pass self.dragid = None self.mouseLeftUpPointer.emit(x=coordinates[0], y=coordinates[1]) @@ -1744,13 +1731,13 @@ def ClearLines(self, pdc=None): try: pdc.ClearId(self.lineid) pdc.RemoveId(self.lineid) - except: + except (KeyError, TypeError): pass try: pdc.ClearId(self.plineid) pdc.RemoveId(self.plineid) - except: + except (KeyError, TypeError): pass Debug.msg( @@ -1772,7 +1759,7 @@ def Pixel2Cell(self, xyCoords): try: x = int(xyCoords[0]) y = int(xyCoords[1]) - except: + except (TypeError, ValueError, IndexError): return None if self.Map.region["ewres"] > self.Map.region["nsres"]: @@ -1793,7 +1780,7 @@ def Cell2Pixel(self, enCoords): try: east = float(enCoords[0]) north = float(enCoords[1]) - except: + except (TypeError, ValueError, IndexError): return None if self.Map.region["ewres"] > self.Map.region["nsres"]: diff --git a/gui/wxpython/mapwin/decorations.py b/gui/wxpython/mapwin/decorations.py index 9c643d17752..57da7ec2265 100644 --- a/gui/wxpython/mapwin/decorations.py +++ b/gui/wxpython/mapwin/decorations.py @@ -301,7 +301,7 @@ def GetPlacement(self, screensize): else: b, t, l, r = ( float(number) for number in param.split("=")[1].split(",") - ) # pylint: disable-msg=W0612 + ) # pylint: disable=W0612 x = int((l / 100.0) * screensize[0]) y = int((1 - t / 100.0) * screensize[1]) diff --git a/gui/wxpython/mapwin/graphics.py b/gui/wxpython/mapwin/graphics.py index 0bc94bae6d2..ad673fd24ce 100644 --- a/gui/wxpython/mapwin/graphics.py +++ b/gui/wxpython/mapwin/graphics.py @@ -200,6 +200,7 @@ def AddItem(self, coords, penName=None, label=None, hide=False): Could be 'point' or 'line' according to graphicsType. :param coords: list of east, north coordinates (double) of item. + Example: * point: [1023, 122] @@ -211,8 +212,7 @@ def AddItem(self, coords, penName=None, label=None, hide=False): relevant just for 'point' type. :type label: str :param hide: if it is True, the item is not drawn when self.Draw - is called. Hidden items are also counted in drawing - order. + is called. Hidden items are also counted in drawing order. :type hide: bool :return: (GraphicsSetItem) - added item reference """ @@ -363,7 +363,7 @@ def SetItemDrawOrder(self, item, drawNum): :return: True if order was changed :return: False if drawNum is out of range or item was not found """ - if drawNum < len(self.itemsList) and drawNum >= 0 and item in self.itemsList: + if 0 <= drawNum < len(self.itemsList) and item in self.itemsList: self.itemsList.insert( drawNum, self.itemsList.pop(self.itemsList.index(item)) ) @@ -388,7 +388,7 @@ def _clearId(self, drawid): """Clears old object before drawing new object.""" try: self.pdc.ClearId(drawid) - except: + except (wx.PyDeadObjectError, KeyError): pass diff --git a/gui/wxpython/modules/colorrules.py b/gui/wxpython/modules/colorrules.py index dcb34619b57..b222c08091e 100644 --- a/gui/wxpython/modules/colorrules.py +++ b/gui/wxpython/modules/colorrules.py @@ -203,8 +203,7 @@ def OnRuleEnable(self, event): self.mainPanel.FindWindowById(id + 1000).Enable() self.mainPanel.FindWindowById(id + 2000).Enable() if self.mapType == "vector" and not self.parent.GetParent().colorTable: - vals = [] - vals.append(self.mainPanel.FindWindowById(id + 1000).GetValue()) + vals = [self.mainPanel.FindWindowById(id + 1000).GetValue()] try: vals.append(self.mainPanel.FindWindowById(id + 1 + 1000).GetValue()) except AttributeError: @@ -273,8 +272,7 @@ def SetRasterRule(self, num, val): def SetVectorRule(self, num, val): """Set vector rule""" - vals = [] - vals.append(val) + vals = [val] try: vals.append(self.mainPanel.FindWindowById(num + 1).GetValue()) except AttributeError: @@ -689,9 +687,7 @@ def OnSaveRulesFile(self, event): GMessage(message=_("Nothing to save."), parent=self) return - fd = open(path, "w") - fd.write(rulestxt) - fd.close() + Path(path).write_text(rulestxt) def OnLoadRulesFile(self, event): """Load color table from file""" @@ -700,10 +696,7 @@ def OnLoadRulesFile(self, event): return self.rulesPanel.Clear() - - fd = open(path) - self.ReadColorTable(ctable=fd.read()) - fd.close() + self.ReadColorTable(ctable=Path(path).read_text()) def ReadColorTable(self, ctable): """Read color table @@ -725,9 +718,7 @@ def ReadColorTable(self, ctable): self.rulesPanel.ruleslines[count]["value"] = value self.rulesPanel.ruleslines[count]["color"] = color self.rulesPanel.mainPanel.FindWindowById(count + 1000).SetValue(value) - rgb = [] - for c in color.split(":"): - rgb.append(int(c)) + rgb = [int(c) for c in color.split(":")] self.rulesPanel.mainPanel.FindWindowById(count + 2000).SetColour(rgb) # range try: @@ -805,11 +796,7 @@ def CreateColorTable(self, tmp=False) -> bool: return False gtemp = utils.GetTempfile() - output = open(gtemp, "w") - try: - output.write(rulestxt) - finally: - output.close() + Path(gtemp).write_text(rulestxt) cmd = [ "%s.colors" % self.mapType[0], # r.colors/v.colors @@ -1830,11 +1817,7 @@ def UpdateColorColumn(self, tmp): return False gtemp = utils.GetTempfile() - output = open(gtemp, "w") - try: - output.write(rulestxt) - finally: - output.close() + Path(gtemp).write_text(rulestxt) RunCommand("db.execute", parent=self, input=gtemp) return True diff --git a/gui/wxpython/modules/import_export.py b/gui/wxpython/modules/import_export.py index db4e3b517a2..d83d468facf 100644 --- a/gui/wxpython/modules/import_export.py +++ b/gui/wxpython/modules/import_export.py @@ -337,11 +337,9 @@ def AddLayers(self, returncode, cmd=None, userData=None): if self.importType == "gdal": nBands = int(userData.get("nbands", 1)) if userData else 1 - if UserSettings.Get(group="rasterLayer", key="opaque", subkey="enabled"): - nFlag = True - else: - nFlag = False - + nFlag = bool( + UserSettings.Get(group="rasterLayer", key="opaque", subkey="enabled") + ) for i in range(1, nBands + 1): nameOrig = name if nBands > 1: @@ -504,8 +502,7 @@ def OnRun(self, event): GError( parent=self, message=_( - "The Python GDAL package is missing." - " Please install it." + "The Python GDAL package is missing. Please install it." ), ) return diff --git a/gui/wxpython/modules/mcalc_builder.py b/gui/wxpython/modules/mcalc_builder.py index 4294a9730de..5567d6bd66d 100644 --- a/gui/wxpython/modules/mcalc_builder.py +++ b/gui/wxpython/modules/mcalc_builder.py @@ -18,6 +18,7 @@ import os import re +from pathlib import Path import wx import grass.script as gs @@ -733,11 +734,7 @@ def OnSaveExpression(self, event): dlg.Destroy() return - try: - fobj = open(path, "w") - fobj.write(mctxt) - finally: - fobj.close() + Path(path).write_text(mctxt) dlg.Destroy() @@ -755,11 +752,7 @@ def OnLoadExpression(self, event): dlg.Destroy() return - try: - fobj = open(path) - mctxt = fobj.read() - finally: - fobj.close() + mctxt = Path(path).read_text() try: result, exp = mctxt.split("=", 1) diff --git a/gui/wxpython/nviz/mapwindow.py b/gui/wxpython/nviz/mapwindow.py index c459e5f4f89..b3f05ed6f04 100644 --- a/gui/wxpython/nviz/mapwindow.py +++ b/gui/wxpython/nviz/mapwindow.py @@ -18,32 +18,39 @@ @author Anna Kratochvilova (Google SoC 2011) """ +from __future__ import annotations + +import copy +import math import os import sys import time -import copy -import math - from threading import Thread - -import wx -from wx.lib.newevent import NewEvent -from wx import glcanvas -from wx.glcanvas import WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE +from typing import TYPE_CHECKING, Literal, TypedDict import grass.script as gs from grass.pydispatch.signal import Signal -from core.gcmd import GMessage, GException, GError +# isort: split + +import wx from core.debug import Debug -from mapwin.base import MapWindowBase -from core.settings import UserSettings -from nviz.workspace import NvizSettings -from nviz.animation import Animation -from nviz import wxnviz +from core.gcmd import GError, GException, GMessage +from core.giface import Notification from core.globalvar import CheckWxVersion +from core.settings import UserSettings from core.utils import str2rgb -from core.giface import Notification +from mapwin.base import MapWindowBase +from nviz import wxnviz +from nviz.animation import Animation +from nviz.workspace import NvizSettings +from wx import glcanvas +from wx.glcanvas import WX_GL_DEPTH_SIZE, WX_GL_DOUBLEBUFFER, WX_GL_RGBA +from wx.lib.newevent import NewEvent + +if TYPE_CHECKING: + import lmgr.frame + import main_window.frame wxUpdateProperties, EVT_UPDATE_PROP = NewEvent() wxUpdateView, EVT_UPDATE_VIEW = NewEvent() @@ -51,22 +58,32 @@ wxUpdateCPlane, EVT_UPDATE_CPLANE = NewEvent() +class RenderTypedDict(TypedDict): + """Typed dictionary to store the render flags for GLWindow. + At runtime, it is a plain dict.""" + + quick: bool + vlines: bool + vpoints: bool + overlays: bool + + class NvizThread(Thread): - def __init__(self, log, progressbar, window): + def __init__(self, log, progressbar, window) -> None: Thread.__init__(self) Debug.msg(5, "NvizThread.__init__():") self.log = log self.progressbar = progressbar self.window = window - self._display = None + self._display: wxnviz.Nviz | None = None self.daemon = True - def run(self): + def run(self) -> None: self._display = wxnviz.Nviz(self.log, self.progressbar) - def GetDisplay(self): + def GetDisplay(self) -> wxnviz.Nviz | None: """Get display instance""" return self._display @@ -74,7 +91,16 @@ def GetDisplay(self): class GLWindow(MapWindowBase, glcanvas.GLCanvas): """OpenGL canvas for Map Display Window""" - def __init__(self, parent, giface, frame, Map, tree, lmgr, id=wx.ID_ANY): + def __init__( + self, + parent, + giface, + frame, + Map, + tree, + lmgr: main_window.frame.GMFrame | lmgr.frame.GMFrame, + id=wx.ID_ANY, + ) -> None: """All parameters except for id are mandatory. The todo is to remove them completely.""" self.parent = parent @@ -119,12 +145,12 @@ def __init__(self, parent, giface, frame, Map, tree, lmgr, id=wx.ID_ANY): self.init = False self.initView = False - self.context = None + self.context: glcanvas.GLContext | None = None if CheckWxVersion(version=[2, 9]): self.context = glcanvas.GLContext(self) # render mode - self.render = { + self.render: RenderTypedDict = { "quick": False, # do not render vector lines in quick mode "vlines": False, @@ -172,7 +198,10 @@ def __init__(self, parent, giface, frame, Map, tree, lmgr, id=wx.ID_ANY): self.nvizThread = NvizThread(logerr, self.parent.GetProgressBar(), logmsg) self.nvizThread.start() time.sleep(0.1) - self._display = self.nvizThread.GetDisplay() + # self.nvizThread.start() invokes NvizThread.run() in a separate thread, + # which sets the _display attribute returned by GetDisplay(), + # so GetDisplay() shouldn't return None after calling self.nvizThread.start(). + self._display: wxnviz.Nviz | None = self.nvizThread.GetDisplay() # GRASS_REGION needed only for initialization del os.environ["GRASS_REGION"] @@ -236,7 +265,15 @@ def _warningDepthBuffer(self): ) GMessage(message) - def GetContentScaleFactor(self): + def GetContentScaleFactor(self) -> float: + """See note that wx.glcanvas.GLContext always uses physical pixels, even on the + platforms where wx.Window uses logical pixels, in wx.glcanvas.GLCanvas docs + https://docs.wxpython.org/wx.glcanvas.GLCanvas.html + + Docs for wx.glcanvas.GLCanvas.GetContentScaleFactor() point to + wx.Window.GetContentScaleFactor() at + https://docs.wxpython.org/wx.Window.html#wx.Window.GetContentScaleFactor + """ if sys.platform == "darwin" and not CheckWxVersion(version=[4, 1, 0]): return 1 return super().GetContentScaleFactor() @@ -735,7 +772,7 @@ def OnDragging(self, event): event.Skip() - def Pixel2Cell(self, xyCoords): + def Pixel2Cell(self, xyCoords) -> tuple[float, float] | None: """Convert image coordinates to real word coordinates :param xyCoords: image coordinates @@ -755,9 +792,11 @@ def Pixel2Cell(self, xyCoords): return (x, y) - def DoZoom(self, zoomtype, pos): + def DoZoom(self, zoomtype, pos) -> None: """Change perspective and focus""" - + if self.view is None: + # Cannot do any useful actions here if self.view is None + return prev_value = self.view["persp"]["value"] if zoomtype > 0: value = -1 * self.view["persp"]["step"] @@ -770,7 +809,7 @@ def DoZoom(self, zoomtype, pos): self.view["persp"]["value"] = 180 if prev_value != self.view["persp"]["value"]: - if hasattr(self.lmgr, "nviz"): + if hasattr(self.lmgr, "nviz") and self._display is not None: self.lmgr.nviz.UpdateSettings() x, y = pos[0], self.GetClientSize()[1] - pos[1] result = self._display.GetPointOnSurface( @@ -780,14 +819,16 @@ def DoZoom(self, zoomtype, pos): self._display.LookHere(x, y, self.GetContentScaleFactor()) focus = self._display.GetFocus() for i, coord in enumerate(("x", "y", "z")): - self.iview["focus"][coord] = focus[i] - self._display.SetView( - self.view["position"]["x"], - self.view["position"]["y"], - self.iview["height"]["value"], - self.view["persp"]["value"], - self.view["twist"]["value"], - ) + if self.iview is not None: + self.iview["focus"][coord] = focus[i] + if self.iview is not None: + self._display.SetView( + self.view["position"]["x"], + self.view["position"]["y"], + self.iview["height"]["value"], + self.view["persp"]["value"], + self.view["twist"]["value"], + ) self.saveHistory = True # redraw map self.DoPaint() @@ -863,6 +904,9 @@ def OnDClick(self, event): def FocusPanning(self, event): """Simulation of panning using focus""" size = self.GetClientSize() + if self._display is None: + msg = "self._display should not be None after starting NvizThread" + raise GException(msg) id1, x1, y1, z1 = self._display.GetPointOnSurface( self.mouse["tmp"][0], size[1] - self.mouse["tmp"][1], @@ -871,6 +915,18 @@ def FocusPanning(self, event): id2, x2, y2, z2 = self._display.GetPointOnSurface( event.GetX(), size[1] - event.GetY(), self.GetContentScaleFactor() ) + if ( + id1 is None + or id2 is None + or x1 is None + or x2 is None + or y1 is None + or y2 is None + or z1 is None + or z2 is None + or self.iview is None + ): + return if id1 and id1 == id2: dx, dy, dz = x2 - x1, y2 - y1, z2 - z1 focus = self.iview["focus"] @@ -886,11 +942,14 @@ def FocusPanning(self, event): self.render["quick"] = True self.Refresh(False) - def HorizontalPanning(self, event): + def HorizontalPanning(self, event) -> None: """Move all layers in horizontal (x, y) direction. Currently not used. """ - size = self.GetClientSize() + size: wx.Size | tuple[int, int] = self.GetClientSize() + if self._display is None: + msg = "self._display should not be None after starting NvizThread" + raise GException(msg) id1, x1, y1, z1 = self._display.GetPointOnSurface( self.mouse["tmp"][0], size[1] - self.mouse["tmp"][1], @@ -1027,7 +1086,11 @@ def GoTo(self, e, n): def QuerySurface(self, x, y): """Query surface on given position""" size = self.GetClientSize() - result = self._display.QueryMap(x, size[1] - y, self.GetContentScaleFactor()) + result = None + if self._display is not None: + result = self._display.QueryMap( + x, size[1] - y, self.GetContentScaleFactor() + ) if result: self.qpoints.append((result["x"], result["y"], result["z"])) self.log.WriteLog("%-30s: %.3f" % (_("Easting"), result["x"])) @@ -1170,16 +1233,15 @@ def UpdateLight(self, event): if event.refresh: self.Refresh(False) - def UpdateMap(self, render=True, reRenderTool=False): + def UpdateMap(self, render: bool = True, reRenderTool: bool = False) -> None: """Updates the canvas anytime there is a change to the underlying images or to the geometry of the canvas. :param render: re-render map composition - :type render: bool - :param reRenderTool bool: enable re-render map if True, when - auto re-render map is disabled and - Render map tool is activated from the - Map Display toolbar + :param reRenderTool: enable re-render map if True, when + auto re-render map is disabled and + Render map tool is activated from the + Map Display toolbar """ if not self.parent.mapWindowProperties.autoRender and not reRenderTool: return @@ -1239,7 +1301,7 @@ def UpdateMap(self, render=True, reRenderTool=False): Debug.msg( 3, "GLWindow.UpdateMap(): quick = %d, -> time = %g" - % (self.render["quick"], (stop - start)), + % (int(self.render["quick"]), (stop - start)), ) def EraseMap(self): @@ -1759,8 +1821,7 @@ def LoadVector(self, item, points=None, append=True): """Load 2D or 3D vector map overlay :param item: layer item - :param points: True to load points, False to load lines, None - to load both + :param points: True to load points, False to load lines, None to load both :param bool append: append vector to layer list """ layer = self.tree.GetLayerInfo(item, key="maplayer") @@ -2049,7 +2110,9 @@ def UpdateSurfaceProperties(self, id, data): data["position"].pop("update") data["draw"]["all"] = False - def UpdateVolumeProperties(self, id, data, isosurfId=None): + def UpdateVolumeProperties( + self, id: wxnviz.VolumeId, data, isosurfId: wxnviz.IsosurfaceId | None = None + ) -> None: """Update volume (isosurface/slice) map object properties""" if "update" in data["draw"]["resolution"]: if data["draw"]["mode"]["value"] == 0: @@ -2085,7 +2148,7 @@ def UpdateVolumeProperties(self, id, data, isosurfId=None): # # isosurface attributes # - isosurfId = 0 + isosurfId: wxnviz.IsosurfaceId = 0 for isosurf in data["isosurface"]: self._display.AddIsosurface(id, 0, isosurf_id=isosurfId) for attrb in ("topo", "color", "mask", "transp", "shine"): @@ -2172,7 +2235,7 @@ def UpdateVectorProperties(self, id, data, type): else: self.UpdateVectorLinesProperties(id, data[type]) - def UpdateVectorLinesProperties(self, id, data): + def UpdateVectorLinesProperties(self, id: wxnviz.VectorId, data) -> None: """Update vector line map object properties""" # mode if ( @@ -2239,8 +2302,11 @@ def UpdateVectorLinesProperties(self, id, data): if "update" in data["mode"]: data["mode"].pop("update") - def UpdateVectorPointsProperties(self, id, data): + def UpdateVectorPointsProperties(self, id: wxnviz.PointId, data) -> None: """Update vector point map object properties""" + if self._display is None: + msg = "self._display should not be None after starting NvizThread" + raise GException(msg) if ( "update" in data["size"] or "update" in data["width"] @@ -2714,7 +2780,7 @@ def OnNvizCmd(self): """Generate and write command to command output""" self.log.WriteLog(self.NvizCmdCommand(), notification=Notification.RAISE_WINDOW) - def SaveToFile(self, FileName, FileType, width, height): + def SaveToFile(self, FileName, FileType: Literal["ppm", "tif"], width, height): """This draws the DC to a buffer that can be saved to a file. .. todo:: @@ -2739,7 +2805,7 @@ def GetDisplay(self): """Get display instance""" return self._display - def ZoomToMap(self, layers): + def ZoomToMap(self, layers: list | None = None) -> None: """Reset view :param layers: so far unused diff --git a/gui/wxpython/nviz/tools.py b/gui/wxpython/nviz/tools.py index a56a062f6f6..f43de80944d 100644 --- a/gui/wxpython/nviz/tools.py +++ b/gui/wxpython/nviz/tools.py @@ -296,10 +296,8 @@ def _resizeScrolledPanel(self, foldPanelBar, scrolledPanel, collapsed, expanded) :param obj foldPanelBar: FolPanelBar widget obj instance :param obj scrolledPanel: ScrolledPanel widget obj instance - :param int collapsed: number of collapsed panels of FoldPanelBar - widget - :param int expanded: number of expanded panels of FoldPanelBar - widget + :param int collapsed: number of collapsed panels of FoldPanelBar widget + :param int expanded: number of expanded panels of FoldPanelBar widget """ if expanded > 0: foldPanelBar.Expand(foldPanelBar.GetFoldPanel(0)) @@ -1612,12 +1610,12 @@ def _createVectorPage(self, parent): checkThematicWidth = wx.CheckBox( parent=panel, id=wx.ID_ANY, label=_("use width for thematic mapping") ) - self.win["vector"]["lines"]["thematic"][ - "checkcolor" - ] = checkThematicColor.GetId() - self.win["vector"]["lines"]["thematic"][ - "checkwidth" - ] = checkThematicWidth.GetId() + self.win["vector"]["lines"]["thematic"]["checkcolor"] = ( + checkThematicColor.GetId() + ) + self.win["vector"]["lines"]["thematic"]["checkwidth"] = ( + checkThematicWidth.GetId() + ) checkThematicColor.Bind(wx.EVT_CHECKBOX, self.OnCheckThematic) checkThematicWidth.Bind(wx.EVT_CHECKBOX, self.OnCheckThematic) checkThematicColor.SetValue(False) @@ -1827,12 +1825,12 @@ def _createVectorPage(self, parent): checkThematicSize = wx.CheckBox( parent=panel, id=wx.ID_ANY, label=_("use size for thematic mapping") ) - self.win["vector"]["points"]["thematic"][ - "checkcolor" - ] = checkThematicColor.GetId() - self.win["vector"]["points"]["thematic"][ - "checksize" - ] = checkThematicSize.GetId() + self.win["vector"]["points"]["thematic"]["checkcolor"] = ( + checkThematicColor.GetId() + ) + self.win["vector"]["points"]["thematic"]["checksize"] = ( + checkThematicSize.GetId() + ) checkThematicColor.Bind(wx.EVT_CHECKBOX, self.OnCheckThematic) checkThematicSize.Bind(wx.EVT_CHECKBOX, self.OnCheckThematic) checkThematicColor.SetValue(False) @@ -1938,9 +1936,10 @@ def _createVectorPage(self, parent): def GselectOnPopup(self, ltype, exclude=False): """Update gselect.Select() items""" - maps = [] - for layer in self.mapWindow.Map.GetListOfLayers(ltype=ltype, active=True): - maps.append(layer.GetName()) + maps = [ + layer.GetName() + for layer in self.mapWindow.Map.GetListOfLayers(ltype=ltype, active=True) + ] return maps, exclude def _createVolumePage(self, parent): @@ -3835,10 +3834,7 @@ def _get3dRange(self, name): """Gelper func for getting range of 3d map""" ret = RunCommand("r3.info", read=True, flags="r", map=name) if ret: - range = [] - for value in ret.strip("\n").split("\n"): - range.append(float(value.split("=")[1])) - return range + return [float(value.split("=")[1]) for value in ret.strip("\n").split("\n")] return -1e6, 1e6 @@ -5185,11 +5181,9 @@ def UpdatePage(self, pageId): self.EnablePage("constant", True) elif pageId == "cplane": count = self._display.GetCPlanesCount() - choices = [ - _("None"), + choices = [_("None")] + [ + "%s %i" % (_("Plane"), plane + 1) for plane in range(count) ] - for plane in range(count): - choices.append("%s %i" % (_("Plane"), plane + 1)) self.FindWindowById(self.win["cplane"]["planes"]).SetItems(choices) current = 0 for i, cplane in enumerate(self.mapWindow.cplanes): diff --git a/gui/wxpython/nviz/workspace.py b/gui/wxpython/nviz/workspace.py index ac9e0582b2d..e0a540acb2b 100644 --- a/gui/wxpython/nviz/workspace.py +++ b/gui/wxpython/nviz/workspace.py @@ -30,9 +30,7 @@ def __init__(self): def SetConstantDefaultProp(self): """Set default constant data properties""" - data = {} - for key, value in UserSettings.Get(group="nviz", key="constant").items(): - data[key] = value + data = dict(UserSettings.Get(group="nviz", key="constant").items()) color = ( str(data["color"][0]) + ":" @@ -137,10 +135,7 @@ def SetVolumeDefaultProp(self): group="nviz", key="volume", subkey=["draw", "mode"] ) desc = "isosurface" if sel == 0 else "slice" - data["draw"]["mode"] = { - "value": sel, - "desc": desc, - } + data["draw"]["mode"] = {"value": sel, "desc": desc} elif control == "box": box = UserSettings.Get( group="nviz", key="volume", subkey=["draw", "box"] @@ -184,10 +179,11 @@ def SetIsosurfaceDefaultProp(self): def SetSliceDefaultProp(self): """Set default slice properties""" - data = {} - data["position"] = copy.deepcopy( - UserSettings.Get(group="nviz", key="volume", subkey="slice_position") - ) + data = { + "position": copy.deepcopy( + UserSettings.Get(group="nviz", key="volume", subkey="slice_position") + ) + } data["position"]["update"] = None data["transp"] = copy.deepcopy( @@ -321,8 +317,7 @@ def GetDrawMode(self, mode=None, style=None, shade=None, string=False): :param mode: :param style: :param shade: - :param string: if True input parameters are strings otherwise - selections + :param string: if True input parameters are strings otherwise selections """ if not wxnviz: return None @@ -390,8 +385,9 @@ def SetDecorDefaultProp(self, type): # arrow if type == "arrow": data["arrow"] = copy.deepcopy(UserSettings.Get(group="nviz", key="arrow")) - data["arrow"]["color"] = "%d:%d:%d" % ( - UserSettings.Get(group="nviz", key="arrow", subkey="color")[:3] + data["arrow"]["color"] = ( + "%d:%d:%d" + % (UserSettings.Get(group="nviz", key="arrow", subkey="color")[:3]) ) data["arrow"].update( copy.deepcopy( @@ -407,8 +403,9 @@ def SetDecorDefaultProp(self, type): data["scalebar"] = copy.deepcopy( UserSettings.Get(group="nviz", key="scalebar") ) - data["scalebar"]["color"] = "%d:%d:%d" % ( - UserSettings.Get(group="nviz", key="scalebar", subkey="color")[:3] + data["scalebar"]["color"] = ( + "%d:%d:%d" + % (UserSettings.Get(group="nviz", key="scalebar", subkey="color")[:3]) ) data["scalebar"].update( copy.deepcopy( diff --git a/gui/wxpython/nviz/wxnviz.py b/gui/wxpython/nviz/wxnviz.py index 06812e81be6..d4875212343 100644 --- a/gui/wxpython/nviz/wxnviz.py +++ b/gui/wxpython/nviz/wxnviz.py @@ -22,24 +22,22 @@ @author Anna Kratochvilova (Google SoC 2011) """ +from __future__ import annotations + import locale import struct import sys from math import sqrt - -try: - from numpy import matrix -except ImportError: - msg = _( - "This module requires the NumPy module, which could not be " - "imported. It probably is not installed (it's not part of the " - "standard Python distribution). See the Numeric Python site " - "(http://numpy.scipy.org) for information on downloading source or " - "binaries." - ) - print("wxnviz.py: " + msg, file=sys.stderr) +from typing import TYPE_CHECKING, Literal, TypedDict, overload import wx +from core.debug import Debug +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 try: from ctypes import ( @@ -241,18 +239,57 @@ from grass.lib.vector import Vect_read_colors except (ImportError, OSError, TypeError) as e: print("wxnviz.py: {}".format(e), file=sys.stderr) +try: + from numpy import matrix +except ImportError: + msg = _( + "This module requires the NumPy module, which could not be " + "imported. It probably is not installed (it's not part of the " + "standard Python distribution). See the Numeric Python site " + "(https://numpy.org) for information on downloading source or " + "binaries." + ) + print("wxnviz.py: " + msg, file=sys.stderr) -import grass.script as gs +if TYPE_CHECKING: + from collections.abc import Iterable, Mapping + + from _typeshed import StrPath -from core.debug import Debug -from core.gcmd import DecodeString -from core.globalvar import wxPythonPhoenix -from core.utils import autoCropImageFromFile -from gui_core.wrap import Rect log = None progress = None +PointId = int +"""Point set id, as used with GP_site_exists(id)""" + +VectorId = int +"""Vector set id, as used with GV_vect_exists(id)""" + +VolumeId = int +"""Volume set id, as used with GVL_vol_exists(id)""" + +SurfaceId = int +"""Surface id, as used with GS_surf_exists(id)""" + +IsosurfaceId = int +"""Isosurface id (0 - MAX_ISOSURFS), as used with GVL_isosurf_get_att, for isosurf_id""" + +SliceId = int +"""Slice id, as used with volume sets in GVL_slice_del(id, slice_id)""" + +ClipPlaneId = int +"""Clip plane id (cplane), as returned by Nviz_get_current_cplane()""" + + +class QueryMapResult(TypedDict): + id: SurfaceId + x: int + y: int + z: int + elevation: str + color: str + def print_error(msg, type): """Redirect stderr""" @@ -289,7 +326,7 @@ def print_progress(value): class Nviz: - def __init__(self, glog, gprogress): + def __init__(self, glog, gprogress) -> None: """Initialize Nviz class instance :param glog: logging area @@ -310,7 +347,8 @@ def __init__(self, glog, gprogress): self.data = pointer(self.data_obj) self.color_obj = Colors() self.color = pointer(self.color_obj) - + self.width: int + self.height: int self.width = self.height = -1 self.showLight = False @@ -335,7 +373,7 @@ def Init(self): GVL_libinit() GVL_init_region() - def ResizeWindow(self, width, height, scale=1): + def ResizeWindow(self, width: int, height: int, scale: float = 1) -> Literal[1, 0]: """GL canvas resized :param width: window width @@ -355,13 +393,13 @@ def GetLongDim(self): """Get longest dimension, used for initial size of north arrow""" return Nviz_get_longdim(self.data) - def SetViewDefault(self): + def SetViewDefault(self) -> tuple[float, float, float, float]: """Set default view (based on loaded data) :return: z-exag value, default, min and max height """ # determine z-exag - z_exag = Nviz_get_exag() + z_exag: float = Nviz_get_exag() Nviz_change_exag(self.data, z_exag) # determine height @@ -402,7 +440,7 @@ def SetView(self, x, y, height, persp, twist): twist, ) - def GetViewpointPosition(self): + def GetViewpointPosition(self) -> tuple[float, float, float]: x = c_double() y = c_double() h = c_double() @@ -411,7 +449,7 @@ def GetViewpointPosition(self): return (x.value, y.value, h.value) - def LookHere(self, x, y, scale=1): + def LookHere(self, x, y, scale: float = 1) -> None: """Look here feature :param x,y: screen coordinates """ @@ -424,7 +462,9 @@ def LookAtCenter(self): Nviz_set_focus_map(MAP_OBJ_UNDEFINED, -1) Debug.msg(3, "Nviz::LookAtCenter()") - def GetFocus(self): + def GetFocus( + self, + ) -> tuple[float, float, float] | tuple[Literal[-1], Literal[-1], Literal[-1]]: """Get focus""" Debug.msg(3, "Nviz::GetFocus()") if Nviz_has_focus(self.data): @@ -433,14 +473,14 @@ def GetFocus(self): z = c_float() Nviz_get_focus(self.data, byref(x), byref(y), byref(z)) return x.value, y.value, z.value - return -1, -1, -1 + return (-1, -1, -1) - def SetFocus(self, x, y, z): + def SetFocus(self, x: float, y: float, z: float) -> None: """Set focus""" Debug.msg(3, "Nviz::SetFocus()") Nviz_set_focus(self.data, x, y, z) - def GetViewdir(self): + def GetViewdir(self) -> tuple[float, float, float]: """Get viewdir""" Debug.msg(3, "Nviz::GetViewdir()") dir = (c_float * 3)() @@ -448,7 +488,7 @@ def GetViewdir(self): return dir[0], dir[1], dir[2] - def SetViewdir(self, x, y, z): + def SetViewdir(self, x: float, y: float, z: float) -> None: """Set viewdir""" Debug.msg(3, "Nviz::SetViewdir(): x=%f, y=%f, z=%f" % (x, y, z)) dir = (c_float * 3)() @@ -456,7 +496,7 @@ def SetViewdir(self, x, y, z): dir[i] = coord GS_set_viewdir(byref(dir)) - def SetZExag(self, z_exag): + def SetZExag(self, z_exag: float) -> Literal[1]: """Set z-exag value :param z_exag: value @@ -466,7 +506,7 @@ def SetZExag(self, z_exag): Debug.msg(3, "Nviz::SetZExag(): z_exag=%f", z_exag) return Nviz_change_exag(self.data, z_exag) - def Draw(self, quick, quick_mode): + def Draw(self, quick: bool, quick_mode: int) -> None: """Draw canvas Draw quick mode: @@ -487,12 +527,12 @@ def Draw(self, quick, quick_mode): else: Nviz_draw_all(self.data) - def EraseMap(self): + def EraseMap(self) -> None: """Erase map display (with background color)""" Debug.msg(1, "Nviz::EraseMap()") GS_clear(Nviz_get_bgcolor(self.data)) - def InitView(self): + def InitView(self) -> None: """Initialize view""" # initialize nviz data Nviz_init_data(self.data) @@ -508,14 +548,24 @@ def InitView(self): Debug.msg(1, "Nviz::InitView()") - def SetBgColor(self, color_str): + def SetBgColor(self, color_str: str) -> None: """Set background color :param str color_str: color string """ Nviz_set_bgcolor(self.data, Nviz_color_from_str(color_str)) - def SetLight(self, x, y, z, color, bright, ambient, w=0, lid=1): + def SetLight( + self, + x: float, + y: float, + z: float, + color, + bright: float, + ambient: float, + w: float = 0, + lid: int = 1, + ) -> None: """Change lighting settings :param x,y,z: position @@ -619,7 +669,7 @@ def AddConstant(self, value, color): Debug.msg(1, "Nviz::AddConstant(): id=%d", id) return id - def UnloadSurface(self, id): + def UnloadSurface(self, id: SurfaceId) -> Literal[1, 0]: """Unload surface :param id: surface id @@ -671,7 +721,15 @@ def LoadVector(self, name, points): return id, baseId - def UnloadVector(self, id, points): + @overload + def UnloadVector(self, id: PointId, points: Literal[True]) -> Literal[1, 0]: + pass + + @overload + def UnloadVector(self, id: VectorId, points: Literal[False]) -> Literal[1, 0]: + pass + + def UnloadVector(self, id: PointId | VectorId, points: bool) -> Literal[1, 0]: """Unload vector set :param id: vector set id @@ -775,7 +833,7 @@ def LoadVolume(self, name, color_name, color_value): return id - def UnloadVolume(self, id): + def UnloadVolume(self, id: VolumeId) -> Literal[0, 1]: """Unload volume :param id: volume id @@ -793,7 +851,21 @@ def UnloadVolume(self, id): return 1 - def SetSurfaceTopo(self, id, map, value): + @overload + def SetSurfaceTopo( + self, id: SurfaceId, map: Literal[True], value: str + ) -> Literal[1, -1, -2]: + pass + + @overload + def SetSurfaceTopo( + self, id: SurfaceId, map: Literal[False], value: float + ) -> Literal[1, -1, -2]: + pass + + def SetSurfaceTopo( + self, id: SurfaceId, map: bool, value: str | float + ) -> Literal[1, -1, -2]: """Set surface topography :param id: surface id @@ -806,7 +878,21 @@ def SetSurfaceTopo(self, id, map, value): """ return self.SetSurfaceAttr(id, ATT_TOPO, map, value) - def SetSurfaceColor(self, id, map, value): + @overload + def SetSurfaceColor( + self, id: SurfaceId, map: Literal[True], value: str + ) -> Literal[1, -1, -2]: + pass + + @overload + def SetSurfaceColor( + self, id: SurfaceId, map: Literal[False], value: float + ) -> Literal[1, -1, -2]: + pass + + def SetSurfaceColor( + self, id: SurfaceId, map: bool, value: str | float + ) -> Literal[1, -1, -2]: """Set surface color :param id: surface id @@ -819,14 +905,16 @@ def SetSurfaceColor(self, id, map, value): """ return self.SetSurfaceAttr(id, ATT_COLOR, map, value) - def SetSurfaceMask(self, id, invert, value): + def SetSurfaceMask( + self, id: SurfaceId, invert: bool, value: str + ) -> Literal[1, -1, -2]: """Set surface mask .. todo:: invert :param id: surface id - :param invert: if true invert mask + :param invert: if true invert mask (unimplemented, always true) :param value: map name of value :return: 1 on success @@ -835,7 +923,21 @@ def SetSurfaceMask(self, id, invert, value): """ return self.SetSurfaceAttr(id, ATT_MASK, True, value) - def SetSurfaceTransp(self, id, map, value): + @overload + def SetSurfaceTransp( + self, id: SurfaceId, map: Literal[True], value: str + ) -> Literal[1, -1, -2]: + pass + + @overload + def SetSurfaceTransp( + self, id: SurfaceId, map: Literal[False], value: float + ) -> Literal[1, -1, -2]: + pass + + def SetSurfaceTransp( + self, id: SurfaceId, map: bool, value: str | float + ) -> Literal[1, -1, -2]: """Set surface mask ..todo:: @@ -851,7 +953,21 @@ def SetSurfaceTransp(self, id, map, value): """ return self.SetSurfaceAttr(id, ATT_TRANSP, map, value) - def SetSurfaceShine(self, id, map, value): + @overload + def SetSurfaceShine( + self, id: SurfaceId, map: Literal[True], value: str + ) -> Literal[1, -1, -2]: + pass + + @overload + def SetSurfaceShine( + self, id: SurfaceId, map: Literal[False], value: float + ) -> Literal[1, -1, -2]: + pass + + def SetSurfaceShine( + self, id: SurfaceId, map: bool, value: str | float + ) -> Literal[1, -1, -2]: """Set surface shininess :param id: surface id @@ -864,7 +980,21 @@ def SetSurfaceShine(self, id, map, value): """ return self.SetSurfaceAttr(id, ATT_SHINE, map, value) - def SetSurfaceEmit(self, id, map, value): + @overload + def SetSurfaceEmit( + self, id: SurfaceId, map: Literal[True], value: str + ) -> Literal[1, -1, -2]: + pass + + @overload + def SetSurfaceEmit( + self, id: SurfaceId, map: Literal[False], value: float + ) -> Literal[1, -1, -2]: + pass + + def SetSurfaceEmit( + self, id: SurfaceId, map: bool, value: str | float + ) -> Literal[1, -1, -2]: """Set surface emission (currently unused) :param id: surface id @@ -877,7 +1007,33 @@ def SetSurfaceEmit(self, id, map, value): """ return self.SetSurfaceAttr(id, ATT_EMIT, map, value) - def SetSurfaceAttr(self, id, attr, map, value): + @overload + def SetSurfaceAttr( + self, id: SurfaceId, attr: int, map: Literal[True], value: str + ) -> Literal[1, -1, -2]: + pass + + @overload + def SetSurfaceAttr( + self, id: SurfaceId, attr: int, map: Literal[False], value: float + ) -> Literal[1, -1, -2]: + pass + + @overload + def SetSurfaceAttr( + self, id: SurfaceId, attr: Literal[2], map: Literal[False], value: str + ) -> Literal[1, -1, -2]: + pass + + @overload + def SetSurfaceAttr( + self, id: SurfaceId, attr: int, map: bool, value: str | float + ) -> Literal[1, -1, -2]: + pass + + def SetSurfaceAttr( + self, id: SurfaceId, attr: int, map: bool, value: str | float + ) -> Literal[1, -1, -2]: """Set surface attribute :param id: surface id @@ -895,7 +1051,9 @@ def SetSurfaceAttr(self, id, attr, map, value): if map: ret = Nviz_set_attr(id, MAP_OBJ_SURF, attr, MAP_ATT, value, -1.0, self.data) else: - val = Nviz_color_from_str(value) if attr == ATT_COLOR else float(value) + val: int | float = ( + Nviz_color_from_str(value) if attr == ATT_COLOR else float(value) + ) ret = Nviz_set_attr(id, MAP_OBJ_SURF, attr, CONST_ATT, None, val, self.data) Debug.msg( @@ -912,7 +1070,7 @@ def SetSurfaceAttr(self, id, attr, map, value): return 1 - def UnsetSurfaceMask(self, id): + def UnsetSurfaceMask(self, id: SurfaceId) -> Literal[1, -1, -2]: """Unset surface mask :param id: surface id @@ -924,7 +1082,7 @@ def UnsetSurfaceMask(self, id): """ return self.UnsetSurfaceAttr(id, ATT_MASK) - def UnsetSurfaceTransp(self, id): + def UnsetSurfaceTransp(self, id: SurfaceId) -> Literal[1, -1, -2]: """Unset surface transparency :param id: surface id @@ -935,7 +1093,7 @@ def UnsetSurfaceTransp(self, id): """ return self.UnsetSurfaceAttr(id, ATT_TRANSP) - def UnsetSurfaceEmit(self, id): + def UnsetSurfaceEmit(self, id: SurfaceId) -> Literal[1, -1, -2]: """Unset surface emission (currently unused) :param id: surface id @@ -946,7 +1104,7 @@ def UnsetSurfaceEmit(self, id): """ return self.UnsetSurfaceAttr(id, ATT_EMIT) - def UnsetSurfaceAttr(self, id, attr): + def UnsetSurfaceAttr(self, id: SurfaceId, attr: int) -> Literal[1, -1, -2]: """Unset surface attribute :param id: surface id @@ -968,7 +1126,9 @@ def UnsetSurfaceAttr(self, id, attr): return 1 - def SetSurfaceRes(self, id, fine, coarse): + def SetSurfaceRes( + self, id: SurfaceId, fine: int, coarse: int + ) -> Literal[1, -1, -2]: """Set surface resolution :param id: surface id @@ -994,7 +1154,7 @@ def SetSurfaceRes(self, id, fine, coarse): return 1 - def SetSurfaceStyle(self, id, style): + def SetSurfaceStyle(self, id: SurfaceId, style: int) -> Literal[1, -1, -2]: """Set draw style Draw styles: @@ -1031,7 +1191,7 @@ def SetSurfaceStyle(self, id, style): return 1 - def SetWireColor(self, id, color_str): + def SetWireColor(self, id: SurfaceId, color_str: str) -> Literal[1, -1]: """Set color of wire .. todo:: @@ -1043,12 +1203,11 @@ def SetWireColor(self, id, color_str): :return: 1 on success :return: -1 surface not found :return: -2 setting attributes failed - :return: 1 on success :return: 0 on failure """ Debug.msg(3, "Nviz::SetWireColor(): id=%d, color=%s", id, color_str) - color = Nviz_color_from_str(color_str) + color: int = Nviz_color_from_str(color_str) if id > 0: if not GS_surf_exists(id): @@ -1067,7 +1226,9 @@ def SetWireColor(self, id, color_str): return 1 - def GetSurfacePosition(self, id): + def GetSurfacePosition( + self, id: SurfaceId + ) -> tuple[()] | tuple[float, float, float]: """Get surface position :param id: surface id @@ -1076,7 +1237,7 @@ def GetSurfacePosition(self, id): :return: zero-length vector on error """ if not GS_surf_exists(id): - return [] + return () x, y, z = c_float(), c_float(), c_float() GS_get_trans(id, byref(x), byref(y), byref(z)) @@ -1090,9 +1251,11 @@ def GetSurfacePosition(self, id): z.value, ) - return [x.value, y.value, z.value] + return (x.value, y.value, z.value) - def SetSurfacePosition(self, id, x, y, z): + def SetSurfacePosition( + self, id: SurfaceId, x: float, y: float, z: float + ) -> Literal[1, -1]: """Set surface position :param id: surface id @@ -1111,13 +1274,15 @@ def SetSurfacePosition(self, id, x, y, z): return 1 - def SetVectorLineMode(self, id, color_str, width, use_z): + def SetVectorLineMode( + self, id: VectorId, color_str: str, width: int, use_z: bool | int + ) -> Literal[1, -1, -2]: """Set mode of vector line overlay :param id: vector id :param color_str: color string :param width: line width - :param use_z: display 3d or on surface + :param use_z: display 3d or on surface, true or non-zero to use z :return: -1 vector set not found :return: -2 on failure @@ -1135,7 +1300,7 @@ def SetVectorLineMode(self, id, color_str, width, use_z): use_z, ) - color = Nviz_color_from_str(color_str) + color: int = Nviz_color_from_str(color_str) # use memory by default if GV_set_style(id, 1, color, width, use_z) < 0: @@ -1143,7 +1308,7 @@ def SetVectorLineMode(self, id, color_str, width, use_z): return 1 - def SetVectorLineHeight(self, id, height): + def SetVectorLineHeight(self, id: VectorId, height: float) -> Literal[1, -1]: """Set vector height above surface (lines) :param id: vector set id @@ -1161,7 +1326,9 @@ def SetVectorLineHeight(self, id, height): return 1 - def SetVectorLineSurface(self, id, surf_id): + def SetVectorLineSurface( + self, id: VectorId, surf_id: SurfaceId + ) -> Literal[1, -1, -2, -3]: """Set reference surface of vector set (lines) :param id: vector set id @@ -1183,7 +1350,9 @@ def SetVectorLineSurface(self, id, surf_id): return 1 - def UnsetVectorLineSurface(self, id, surf_id): + def UnsetVectorLineSurface( + self, id: VectorId, surf_id: SurfaceId + ) -> Literal[1, -1, -2, -3]: """Unset reference surface of vector set (lines) :param id: vector set id @@ -1205,7 +1374,9 @@ def UnsetVectorLineSurface(self, id, surf_id): return 1 - def SetVectorPointMode(self, id, color_str, width, size, marker): + def SetVectorPointMode( + self, id: PointId, color_str: str, width: int, size: float, marker: int + ) -> Literal[1, -1, -2]: """Set mode of vector point overlay :param id: vector id @@ -1225,8 +1396,7 @@ def SetVectorPointMode(self, id, color_str, width, size, marker): Debug.msg( 3, - "Nviz::SetVectorPointMode(): id=%d, color=%s, " - "width=%d, size=%f, marker=%d", + "Nviz::SetVectorPointMode(): id=%d, color=%s, width=%d, size=%f, marker=%d", id, color_str, width, @@ -1234,17 +1404,17 @@ def SetVectorPointMode(self, id, color_str, width, size, marker): marker, ) - color = Nviz_color_from_str(color_str) + color: int = Nviz_color_from_str(color_str) if GP_set_style(id, color, width, size, marker) < 0: return -2 return 1 - def SetVectorPointHeight(self, id, height): + def SetVectorPointHeight(self, id: PointId, height: float) -> Literal[1, -1]: """Set vector height above surface (points) - :param id: vector set id + :param id: point set id :param height: :return: -1 vector set not found @@ -1259,7 +1429,9 @@ def SetVectorPointHeight(self, id, height): return 1 - def SetVectorPointSurface(self, id, surf_id): + def SetVectorPointSurface( + self, id: PointId, surf_id: SurfaceId + ) -> Literal[1, -1, -2, -3]: """Set reference surface of vector set (points) :param id: vector set id @@ -1281,7 +1453,7 @@ def SetVectorPointSurface(self, id, surf_id): return 1 - def ReadVectorColors(self, name, mapset): + def ReadVectorColors(self, name: str, mapset: str) -> Literal[1, 0, -1]: r"""Read vector colors :param name: vector map name @@ -1293,7 +1465,9 @@ def ReadVectorColors(self, name, mapset): """ return Vect_read_colors(name, mapset, self.color) - def CheckColorTable(self, id, type): + def CheckColorTable( + self, id: PointId | VectorId, type: Literal["points", "lines"] + ) -> Literal[1, 0, -1, -2]: """Check if color table exists. :param id: vector set id @@ -1318,14 +1492,14 @@ def CheckColorTable(self, id, type): def SetPointsStyleThematic( self, - id, - layer, - color=None, - colorTable=False, - width=None, - size=None, - symbol=None, - ): + id: PointId, + layer: int, + color: str | None = None, + colorTable: bool = False, + width: str | None = None, + size: str | None = None, + symbol: str | None = None, + ) -> Literal[-1] | None: """Set thematic style for vector points :param id: vector set id @@ -1351,8 +1525,13 @@ def SetPointsStyleThematic( GP_set_style_thematic(id, layer, color, width, size, symbol, None) def SetLinesStyleThematic( - self, id, layer, color=None, colorTable=False, width=None - ): + self, + id: VectorId, + layer: int, + color: str | None = None, + colorTable: bool = False, + width: str | None = None, + ) -> Literal[-1] | None: """Set thematic style for vector lines :param id: vector set id @@ -1375,18 +1554,20 @@ def SetLinesStyleThematic( else: GV_set_style_thematic(id, layer, color, width, None) - def UnsetLinesStyleThematic(self, id): - """Unset thematic style for vector points""" + def UnsetLinesStyleThematic(self, id: VectorId) -> None: + """Unset thematic style for vector lines""" GV_unset_style_thematic(id) - def UnsetPointsStyleThematic(self, id): - """Unset thematic style for vector lines""" + def UnsetPointsStyleThematic(self, id: PointId) -> None: + """Unset thematic style for vector points""" GP_unset_style_thematic(id) - def UnsetVectorPointSurface(self, id, surf_id): + def UnsetVectorPointSurface( + self, id: PointId, surf_id: SurfaceId + ) -> Literal[1, -1, -2, -3]: """Unset reference surface of vector set (points) - :param id: vector set id + :param id: vector point set id :param surf_id: surface id :return: 1 on success @@ -1405,10 +1586,10 @@ def UnsetVectorPointSurface(self, id, surf_id): return 1 - def SetVectorPointZMode(self, id, zMode): + def SetVectorPointZMode(self, id: PointId, zMode: bool) -> Literal[1, 0, -1]: """Set z mode (use z coordinate or not) - :param id: volume id + :param id: vector point set id :param zMode: bool :return: -1 on failure @@ -1420,7 +1601,9 @@ def SetVectorPointZMode(self, id, zMode): return GP_set_zmode(id, int(zMode)) - def AddIsosurface(self, id, level, isosurf_id=None): + def AddIsosurface( + self, id: VolumeId, level: float, isosurf_id: IsosurfaceId | None = None + ) -> Literal[1, -1]: """Add new isosurface :param id: volume id @@ -1446,7 +1629,9 @@ def AddIsosurface(self, id, level, isosurf_id=None): return GVL_isosurf_set_att_const(id, nisosurfs - 1, ATT_TOPO, level) - def AddSlice(self, id, slice_id=None): + def AddSlice( + self, id: VolumeId, slice_id: SliceId | None = None + ) -> int | Literal[-1]: """Add new slice :param id: volume id @@ -1468,7 +1653,9 @@ def AddSlice(self, id, slice_id=None): return GVL_slice_num_slices(id) - def DeleteIsosurface(self, id, isosurf_id): + def DeleteIsosurface( + self, id: VolumeId, isosurf_id: IsosurfaceId + ) -> Literal[1, -1, -2, -3]: """Delete isosurface :param id: volume id @@ -1492,7 +1679,7 @@ def DeleteIsosurface(self, id, isosurf_id): return 1 - def DeleteSlice(self, id, slice_id): + def DeleteSlice(self, id: VolumeId, slice_id: SliceId) -> Literal[1, -1, -2, -3]: """Delete slice :param id: volume id @@ -1516,7 +1703,9 @@ def DeleteSlice(self, id, slice_id): return 1 - def MoveIsosurface(self, id, isosurf_id, up): + def MoveIsosurface( + self, id: VolumeId, isosurf_id: IsosurfaceId, up: bool + ) -> Literal[1, -1, -2, -3]: """Move isosurface up/down in the list :param id: volume id @@ -1544,7 +1733,9 @@ def MoveIsosurface(self, id, isosurf_id, up): return 1 - def MoveSlice(self, id, slice_id, up): + def MoveSlice( + self, id: VolumeId, slice_id: SliceId, up: bool + ) -> Literal[1, -1, -2, -3]: """Move slice up/down in the list :param id: volume id @@ -1572,7 +1763,21 @@ def MoveSlice(self, id, slice_id, up): return 1 - def SetIsosurfaceTopo(self, id, isosurf_id, map, value): + @overload + def SetIsosurfaceTopo( + self, id: VolumeId, isosurf_id: IsosurfaceId, map: Literal[True], value: str + ) -> Literal[1, -1, -2, -3]: + pass + + @overload + def SetIsosurfaceTopo( + self, id: VolumeId, isosurf_id: IsosurfaceId, map: Literal[False], value: float + ) -> Literal[1, -1, -2, -3]: + pass + + def SetIsosurfaceTopo( + self, id: VolumeId, isosurf_id: IsosurfaceId, map: bool, value: str | float + ) -> Literal[1, -1, -2, -3]: """Set isosurface level :param id: volume id @@ -1587,7 +1792,9 @@ def SetIsosurfaceTopo(self, id, isosurf_id, map, value): """ return self.SetIsosurfaceAttr(id, isosurf_id, ATT_TOPO, map, value) - def SetIsosurfaceColor(self, id, isosurf_id, map, value): + def SetIsosurfaceColor( + self, id: VolumeId, isosurf_id: IsosurfaceId, map: bool, value: str + ) -> Literal[1, -1, -2, -3]: """Set isosurface color :param id: volume id @@ -1602,7 +1809,9 @@ def SetIsosurfaceColor(self, id, isosurf_id, map, value): """ return self.SetIsosurfaceAttr(id, isosurf_id, ATT_COLOR, map, value) - def SetIsosurfaceMask(self, id, isosurf_id, invert, value): + def SetIsosurfaceMask( + self, id: VolumeId, isosurf_id: IsosurfaceId, invert: bool, value: str + ) -> Literal[1, -1, -2, -3]: """Set isosurface mask .. todo:: @@ -1620,7 +1829,21 @@ def SetIsosurfaceMask(self, id, isosurf_id, invert, value): """ return self.SetIsosurfaceAttr(id, isosurf_id, ATT_MASK, True, value) - def SetIsosurfaceTransp(self, id, isosurf_id, map, value): + @overload + def SetIsosurfaceTransp( + self, id: VolumeId, isosurf_id: IsosurfaceId, map: Literal[True], value: str + ) -> Literal[1, -1, -2]: + pass + + @overload + def SetIsosurfaceTransp( + self, id: VolumeId, isosurf_id: IsosurfaceId, map: Literal[False], value: float + ) -> Literal[1, -1, -2]: + pass + + def SetIsosurfaceTransp( + self, id: VolumeId, isosurf_id: IsosurfaceId, map: bool, value: str | float + ) -> Literal[1, -1, -2]: """Set isosurface transparency :param id: volume id @@ -1635,7 +1858,21 @@ def SetIsosurfaceTransp(self, id, isosurf_id, map, value): """ return self.SetIsosurfaceAttr(id, isosurf_id, ATT_TRANSP, map, value) - def SetIsosurfaceShine(self, id, isosurf_id, map, value): + @overload + def SetIsosurfaceShine( + self, id: VolumeId, isosurf_id: IsosurfaceId, map: Literal[True], value: str + ) -> Literal[1, -1, -2]: + pass + + @overload + def SetIsosurfaceShine( + self, id: VolumeId, isosurf_id: IsosurfaceId, map: Literal[False], value: float + ) -> Literal[1, -1, -2]: + pass + + def SetIsosurfaceShine( + self, id: VolumeId, isosurf_id: IsosurfaceId, map: bool, value: str | float + ) -> Literal[1, -1, -2]: """Set isosurface shininess :param id: volume id @@ -1650,7 +1887,21 @@ def SetIsosurfaceShine(self, id, isosurf_id, map, value): """ return self.SetIsosurfaceAttr(id, isosurf_id, ATT_SHINE, map, value) - def SetIsosurfaceEmit(self, id, isosurf_id, map, value): + @overload + def SetIsosurfaceEmit( + self, id: VolumeId, isosurf_id: IsosurfaceId, map: Literal[True], value: str + ) -> Literal[1, -1, -2]: + pass + + @overload + def SetIsosurfaceEmit( + self, id: VolumeId, isosurf_id: IsosurfaceId, map: Literal[False], value: float + ) -> Literal[1, -1, -2]: + pass + + def SetIsosurfaceEmit( + self, id: VolumeId, isosurf_id: IsosurfaceId, map: bool, value: str | float + ) -> Literal[1, -1, -2]: """Set isosurface emission (currently unused) :param id: volume id @@ -1665,7 +1916,58 @@ def SetIsosurfaceEmit(self, id, isosurf_id, map, value): """ return self.SetIsosurfaceAttr(id, isosurf_id, ATT_EMIT, map, value) - def SetIsosurfaceAttr(self, id, isosurf_id, attr, map, value): + @overload + def SetIsosurfaceAttr( + self, + id: VolumeId, + isosurf_id: IsosurfaceId, + attr: int, + map: Literal[True], + value: str, + ) -> Literal[1, -1, -2]: + pass + + @overload + def SetIsosurfaceAttr( + self, + id: VolumeId, + isosurf_id: IsosurfaceId, + attr: int, + map: Literal[False], + value: float, + ) -> Literal[1, -1, -2]: + pass + + @overload + def SetIsosurfaceAttr( + self, + id: VolumeId, + isosurf_id: IsosurfaceId, + attr: Literal[2], + map: Literal[False], + value: str, + ) -> Literal[1, -1, -2]: + pass + + @overload + def SetIsosurfaceAttr( + self, + id: VolumeId, + isosurf_id: IsosurfaceId, + attr: int, + map: bool, + value: str | float, + ) -> Literal[1, -1, -2]: + pass + + def SetIsosurfaceAttr( + self, + id: VolumeId, + isosurf_id: IsosurfaceId, + attr: int, + map: bool, + value: str | float, + ) -> Literal[1, -1, -2]: """Set isosurface attribute :param id: volume id @@ -1688,13 +1990,14 @@ def SetIsosurfaceAttr(self, id, isosurf_id, attr, map, value): if map: ret = GVL_isosurf_set_att_map(id, isosurf_id, attr, value) else: - val = Nviz_color_from_str(value) if attr == ATT_COLOR else float(value) - ret = GVL_isosurf_set_att_const(id, isosurf_id, attr, val) + val: int | float = ( + Nviz_color_from_str(value) if attr == ATT_COLOR else float(value) + ) + ret: int = GVL_isosurf_set_att_const(id, isosurf_id, attr, val) Debug.msg( 3, - "Nviz::SetIsosurfaceAttr(): id=%d, isosurf=%d, " - "attr=%d, map=%s, value=%s", + "Nviz::SetIsosurfaceAttr(): id=%d, isosurf=%d, attr=%d, map=%s, value=%s", id, isosurf_id, attr, @@ -1707,7 +2010,9 @@ def SetIsosurfaceAttr(self, id, isosurf_id, attr, map, value): return 1 - def UnsetIsosurfaceMask(self, id, isosurf_id): + def UnsetIsosurfaceMask( + self, id: VolumeId, isosurf_id: IsosurfaceId + ) -> Literal[1, -1, -2]: """Unset isosurface mask :param id: volume id @@ -1720,7 +2025,9 @@ def UnsetIsosurfaceMask(self, id, isosurf_id): """ return self.UnsetIsosurfaceAttr(id, isosurf_id, ATT_MASK) - def UnsetIsosurfaceTransp(self, id, isosurf_id): + def UnsetIsosurfaceTransp( + self, id: VolumeId, isosurf_id: IsosurfaceId + ) -> Literal[1, -1, -2]: """Unset isosurface transparency :param id: volume id @@ -1733,7 +2040,9 @@ def UnsetIsosurfaceTransp(self, id, isosurf_id): """ return self.UnsetIsosurfaceAttr(id, isosurf_id, ATT_TRANSP) - def UnsetIsosurfaceEmit(self, id, isosurf_id): + def UnsetIsosurfaceEmit( + self, id: VolumeId, isosurf_id: IsosurfaceId + ) -> Literal[1, -1, -2]: """Unset isosurface emission (currently unused) :param id: volume id @@ -1746,10 +2055,12 @@ def UnsetIsosurfaceEmit(self, id, isosurf_id): """ return self.UnsetIsosurfaceAttr(id, isosurf_id, ATT_EMIT) - def UnsetIsosurfaceAttr(self, id, isosurf_id, attr): + def UnsetIsosurfaceAttr( + self, id: VolumeId, isosurf_id: IsosurfaceId, attr: int + ) -> Literal[1, -1, -2]: """Unset surface attribute - :param id: surface id + :param id: volume id :param isosurf_id: isosurface id (0 - MAX_ISOSURFS) :param attr: attribute descriptor @@ -1779,10 +2090,10 @@ def UnsetIsosurfaceAttr(self, id, isosurf_id, attr): return 1 - def SetIsosurfaceMode(self, id, mode): + def SetIsosurfaceMode(self, id: VolumeId, mode: int) -> Literal[1, -1, -2]: """Set draw mode for isosurfaces - :param id: isosurface id + :param id: volume set id :param mode: isosurface draw mode :return: 1 on success @@ -1799,10 +2110,10 @@ def SetIsosurfaceMode(self, id, mode): return 1 - def SetSliceMode(self, id, mode): + def SetSliceMode(self, id: VolumeId, mode: int) -> Literal[1, -1, -2]: """Set draw mode for slices - :param id: slice id + :param id: volume set id :param mode: slice draw mode :return: 1 on success @@ -1819,10 +2130,10 @@ def SetSliceMode(self, id, mode): return 1 - def SetIsosurfaceRes(self, id, res): + def SetIsosurfaceRes(self, id: VolumeId, res: int) -> Literal[1, -1, -2]: """Set draw resolution for isosurfaces - :param id: isosurface id + :param id: volume set id :param res: resolution value :return: 1 on success @@ -1839,10 +2150,10 @@ def SetIsosurfaceRes(self, id, res): return 1 - def SetSliceRes(self, id, res): + def SetSliceRes(self, id: VolumeId, res: int) -> Literal[1, -1, -2]: """Set draw resolution for slices - :param id: slice id + :param id: volume set id :param res: resolution value :return: 1 on success @@ -1859,7 +2170,18 @@ def SetSliceRes(self, id, res): return 1 - def SetSlicePosition(self, id, slice_id, x1, x2, y1, y2, z1, z2, dir): + def SetSlicePosition( + self, + id: VolumeId, + slice_id: SliceId, + x1: float, + x2: float, + y1: float, + y2: float, + z1: float, + z2: float, + dir: int, + ) -> Literal[1, -1, -2, -3]: """Set slice position :param id: volume id @@ -1881,11 +2203,13 @@ def SetSlicePosition(self, id, slice_id, x1, x2, y1, y2, z1, z2, dir): ret = GVL_slice_set_pos(id, slice_id, x1, x2, y1, y2, z1, z2, dir) if ret < 0: - return -2 + return -3 return 1 - def SetSliceTransp(self, id, slice_id, value): + def SetSliceTransp( + self, id: VolumeId, slice_id: SliceId, value: int + ) -> Literal[1, -1, -2, -3]: """Set slice transparency :param id: volume id @@ -1907,11 +2231,13 @@ def SetSliceTransp(self, id, slice_id, value): ret = GVL_slice_set_transp(id, slice_id, value) if ret < 0: - return -2 + return -3 return 1 - def SetIsosurfaceInOut(self, id, isosurf_id, inout): + def SetIsosurfaceInOut( + self, id: VolumeId, isosurf_id: IsosurfaceId, inout: bool + ) -> Literal[1, -1, -2, -3]: """Set inout mode :param id: volume id @@ -1929,14 +2255,14 @@ def SetIsosurfaceInOut(self, id, isosurf_id, inout): if isosurf_id > GVL_isosurf_num_isosurfs(id) - 1: return -2 - ret = GVL_isosurf_set_flags(id, isosurf_id, inout) + ret: int = GVL_isosurf_set_flags(id, isosurf_id, int(inout)) if ret < 0: return -3 return 1 - def GetVolumePosition(self, id): + def GetVolumePosition(self, id: VolumeId) -> tuple[()] | tuple[float, float, float]: """Get volume position :param id: volume id @@ -1945,7 +2271,7 @@ def GetVolumePosition(self, id): :return: zero-length vector on error """ if not GVL_vol_exists(id): - return [] + return () x, y, z = c_float(), c_float(), c_float() GVL_get_trans(id, byref(x), byref(y), byref(z)) @@ -1959,9 +2285,11 @@ def GetVolumePosition(self, id): z.value, ) - return [x.value, y.value, z.value] + return (x.value, y.value, z.value) - def SetVolumePosition(self, id, x, y, z): + def SetVolumePosition( + self, id: VolumeId, x: float, y: float, z: float + ) -> Literal[1, -1]: """Set volume position :param id: volume id @@ -1969,7 +2297,6 @@ def SetVolumePosition(self, id, x, y, z): :return: 1 on success :return: -1 volume not found - :return: -2 setting position failed """ if not GVL_vol_exists(id): return -1 @@ -1980,72 +2307,73 @@ def SetVolumePosition(self, id, x, y, z): return 1 - def SetVolumeDrawBox(self, id, ifBox): + def SetVolumeDrawBox(self, id: VolumeId, ifBox: bool) -> Literal[1, -1]: """Display volume wire box :param id: volume id :param ifBox: True to draw wire box, False otherwise - :type ifBox: bool :return: 1 on success :return: -1 volume not found """ if not GVL_vol_exists(id): return -1 - Debug.msg(3, "Nviz::SetVolumeDrawBox(): id=%d, ifBox=%d", id, ifBox) + Debug.msg(3, "Nviz::SetVolumeDrawBox(): id=%d, ifBox=%d", id, int(ifBox)) GVL_set_draw_wire(id, int(ifBox)) return 1 - def GetCPlaneCurrent(self): + def GetCPlaneCurrent(self) -> ClipPlaneId: return Nviz_get_current_cplane(self.data) - def GetCPlanesCount(self): + def GetCPlanesCount(self) -> int: """Returns number of cutting planes""" return Nviz_num_cplanes(self.data) - def GetCPlaneRotation(self): + def GetCPlaneRotation(self) -> tuple[float, float, float]: """Returns rotation parameters of current cutting plane""" x, y, z = c_float(), c_float(), c_float() - current = Nviz_get_current_cplane(self.data) + current: ClipPlaneId = Nviz_get_current_cplane(self.data) Nviz_get_cplane_rotation(self.data, current, byref(x), byref(y), byref(z)) return x.value, y.value, z.value - def GetCPlaneTranslation(self): + def GetCPlaneTranslation(self) -> tuple[float, float, float]: """Returns translation parameters of current cutting plane""" x, y, z = c_float(), c_float(), c_float() - current = Nviz_get_current_cplane(self.data) + current: ClipPlaneId = Nviz_get_current_cplane(self.data) Nviz_get_cplane_translation(self.data, current, byref(x), byref(y), byref(z)) return x.value, y.value, z.value - def SetCPlaneRotation(self, x, y, z): + def SetCPlaneRotation(self, x: float, y: float, z: float) -> None: """Set current clip plane rotation :param x,y,z: rotation parameters """ - current = Nviz_get_current_cplane(self.data) + current: ClipPlaneId = Nviz_get_current_cplane(self.data) Nviz_set_cplane_rotation(self.data, current, x, y, z) Nviz_draw_cplane(self.data, -1, -1) - def SetCPlaneTranslation(self, x, y, z): + def SetCPlaneTranslation(self, x: float, y: float, z: float) -> None: """Set current clip plane translation :param x,y,z: translation parameters """ - current = Nviz_get_current_cplane(self.data) + current: ClipPlaneId = Nviz_get_current_cplane(self.data) Nviz_set_cplane_translation(self.data, current, x, y, z) Nviz_draw_cplane(self.data, -1, -1) Debug.msg( 3, "Nviz::SetCPlaneTranslation(): id=%d, x=%f, y=%f, z=%f", current, x, y, z ) - def SetCPlaneInteractively(self, x, y): - current = Nviz_get_current_cplane(self.data) + def SetCPlaneInteractively( + self, x: float, y: float + ) -> tuple[float, float, float] | tuple[None, None, None]: + current: ClipPlaneId = Nviz_get_current_cplane(self.data) ret = Nviz_set_cplane_here(self.data, current, x, y) if ret: Nviz_draw_cplane(self.data, -1, -1) @@ -2053,38 +2381,44 @@ def SetCPlaneInteractively(self, x, y): return x, y, z return None, None, None - def SelectCPlane(self, index): + def SelectCPlane(self, index: ClipPlaneId) -> None: """Select cutting plane :param index: index of cutting plane """ Nviz_on_cplane(self.data, index) - def UnselectCPlane(self, index): + def UnselectCPlane(self, index: ClipPlaneId) -> None: """Unselect cutting plane :param index: index of cutting plane """ Nviz_off_cplane(self.data, index) - def SetFenceColor(self, index): - """Select current cutting plane + def SetFenceColor(self, type: int) -> None: + """Set appropriate fence color - :param index: type of fence - from 0 (off) to 4 + :param type: type of fence - from 0 (off) to 4 """ - Nviz_set_fence_color(self.data, index) + Nviz_set_fence_color(self.data, type) - def GetXYRange(self): + def GetXYRange(self) -> float: """Get xy range""" return Nviz_get_xyrange(self.data) - def GetZRange(self): + def GetZRange(self) -> tuple[float, float]: """Get z range""" min, max = c_float(), c_float() Nviz_get_zrange(self.data, byref(min), byref(max)) return min.value, max.value - def SaveToFile(self, filename, width=20, height=20, itype="ppm"): + def SaveToFile( + self, + filename: str, + width: int = 20, + height: int = 20, + itype: Literal["ppm", "tif"] = "ppm", + ) -> None: """Save current GL screen to ppm/tif file :param filename: file name @@ -2105,16 +2439,25 @@ def SaveToFile(self, filename, width=20, height=20, itype="ppm"): self.ResizeWindow(widthOrig, heightOrig) - def DrawLightingModel(self): + def DrawLightingModel(self) -> None: """Draw lighting model""" if self.showLight: Nviz_draw_model(self.data) - def DrawFringe(self): + def DrawFringe(self) -> None: """Draw fringe""" Nviz_draw_fringe(self.data) - def SetFringe(self, sid, color, elev, nw=False, ne=False, sw=False, se=False): + def SetFringe( + self, + sid: SurfaceId, + color: tuple[int, int, int] | tuple[int, int, int, int], + elev: float, + nw: bool = False, + ne: bool = False, + sw: bool = False, + se: bool = False, + ) -> None: """Set fringe :param sid: surface id @@ -2134,24 +2477,28 @@ def SetFringe(self, sid, color, elev, nw=False, ne=False, sw=False, se=False): int(se), ) - def DrawArrow(self): + def DrawArrow(self) -> Literal[1]: """Draw north arrow""" return Nviz_draw_arrow(self.data) - def SetArrow(self, sx, sy, size, color): + def SetArrow(self, sx: int, sy: int, size: float, color: str) -> Literal[1, 0]: """Set north arrow from canvas coordinates :param sx,sy: canvas coordinates :param size: arrow length :param color: arrow color + :return: 1 on success + :return: 0 on failure (no surfaces found) """ return Nviz_set_arrow(self.data, sx, sy, size, Nviz_color_from_str(color)) - def DeleteArrow(self): + def DeleteArrow(self) -> None: """Delete north arrow""" Nviz_delete_arrow(self.data) - def SetScalebar(self, id, sx, sy, size, color): + def SetScalebar( + self, id: int, sx: int, sy: int, size: float, color: str + ): # -> struct_scalebar_data | None: """Set scale bar from canvas coordinates :param sx,sy: canvas coordinates @@ -2163,15 +2510,17 @@ def SetScalebar(self, id, sx, sy, size, color): self.data, id, sx, sy, size, Nviz_color_from_str(color) ) - def DrawScalebar(self): + def DrawScalebar(self) -> None: """Draw scale bar""" - return Nviz_draw_scalebar(self.data) + Nviz_draw_scalebar(self.data) - def DeleteScalebar(self, id): + def DeleteScalebar(self, id: int) -> None: """Delete scalebar""" Nviz_delete_scalebar(self.data, id) - def GetPointOnSurface(self, sx, sy, scale=1): + def GetPointOnSurface( + self, sx, sy, scale: float = 1 + ) -> tuple[SurfaceId, float, float, float] | tuple[None, None, None, None]: """Get point on surface :param sx,sy: canvas coordinates (LL) @@ -2191,15 +2540,14 @@ def GetPointOnSurface(self, sx, sy, scale=1): return (sid.value, x.value, y.value, z.value) - def QueryMap(self, sx, sy, scale=1): + def QueryMap(self, sx, sy, scale: float = 1) -> QueryMapResult | None: """Query surface map :param sx,sy: canvas coordinates (LL) """ sid, x, y, z = self.GetPointOnSurface(sx, sy, scale) - if not sid: + if not sid or (x is None or y is None or z is None): return None - catstr = create_string_buffer(256) valstr = create_string_buffer(256) GS_get_cat_at_xy(sid, ATT_TOPO, catstr, x, y) @@ -2214,7 +2562,13 @@ def QueryMap(self, sx, sy, scale=1): "color": DecodeString(valstr.value), } - def GetDistanceAlongSurface(self, sid, p1, p2, useExag=True): + def GetDistanceAlongSurface( + self, + sid: SurfaceId, + p1: tuple[float, float], + p2: tuple[float, float], + useExag: bool = True, + ) -> float: """Get distance measured along surface""" d = c_float() @@ -2224,7 +2578,9 @@ def GetDistanceAlongSurface(self, sid, p1, p2, useExag=True): return d.value - def GetRotationParameters(self, dx, dy): + def GetRotationParameters( + self, dx: float, dy: float + ) -> tuple[float, float, float, float]: """Get rotation parameters (angle, x, y, z axes) :param dx,dy: difference from previous mouse drag event @@ -2248,7 +2604,7 @@ def GetRotationParameters(self, dx, dy): return angle, x, y, z - def Rotate(self, angle, x, y, z): + def Rotate(self, angle: float, x: float, y: float, z: float) -> None: """Set rotation parameters Rotate scene (difference from current state). @@ -2257,11 +2613,11 @@ def Rotate(self, angle, x, y, z): """ Nviz_set_rotation(angle, x, y, z) - def UnsetRotation(self): + def UnsetRotation(self) -> None: """Stop rotating the scene""" Nviz_unset_rotation() - def ResetRotation(self): + def ResetRotation(self) -> None: """Reset scene rotation""" Nviz_init_rotation() @@ -2284,7 +2640,9 @@ def SetRotationMatrix(self, matrix): def Start2D(self): Nviz_set_2D(self.width, self.height) - def FlyThrough(self, flyInfo, mode, exagInfo): + def FlyThrough( + self, flyInfo: Iterable[float], mode: int, exagInfo: Mapping[str, float | int] + ): """Fly through the scene :param flyInfo: fly parameters @@ -2303,7 +2661,9 @@ def FlyThrough(self, flyInfo, mode, exagInfo): class Texture: """Class representing OpenGL texture""" - def __init__(self, filepath, overlayId, coords): + def __init__( + self, filepath: StrPath, overlayId: int, coords: tuple[int, int] + ) -> None: """Load image to texture :param filepath: path to image file @@ -2311,11 +2671,15 @@ def __init__(self, filepath, overlayId, coords): :param coords: image coordinates """ self.path = filepath - self.image = autoCropImageFromFile(filepath) + self.image: wx.Image = autoCropImageFromFile(filepath) + self.width: int + self.orig_width: int + self.height: int + self.orig_height: int self.width = self.orig_width = self.image.GetWidth() self.height = self.orig_height = self.image.GetHeight() - self.id = overlayId - self.coords = coords + self.id: int = overlayId + self.coords: tuple[int, int] = coords self.active = True # alpha needs to be initialized @@ -2335,13 +2699,13 @@ def __init__(self, filepath, overlayId, coords): else: self.textureId = self.Load() - def __del__(self): + def __del__(self) -> None: """Delete texture""" if self.textureId: Nviz_del_texture(self.textureId) gs.try_remove(self.path) - def Resize(self): + def Resize(self) -> None: """Resize image to match 2^n""" n = m = 1 while self.width > pow(2, n): @@ -2349,11 +2713,14 @@ def Resize(self): while self.height > pow(2, m): m += 1 self.image.Resize(size=(pow(2, n), pow(2, m)), pos=(0, 0)) - self.width = self.image.GetWidth() - self.height = self.image.GetHeight() + self.width: int = self.image.GetWidth() + self.height: int = self.image.GetHeight() - def Load(self): - """Load image to texture""" + def Load(self) -> int: + """Load image to texture + + :return: The texture id + """ bytesPerPixel = 4 if self.image.HasAlpha() else 3 bytes = bytesPerPixel * self.width * self.height rev_val = self.height - 1 @@ -2388,43 +2755,45 @@ def Load(self): return Nviz_load_image(im, self.width, self.height, self.image.HasAlpha()) - def Draw(self): + def Draw(self) -> None: """Draw texture as an image""" Nviz_draw_image( self.coords[0], self.coords[1], self.width, self.height, self.textureId ) - def HitTest(self, x, y, radius): + def HitTest(self, x: int, y: int, radius: int) -> bool: copy = Rect(self.coords[0], self.coords[1], self.orig_width, self.orig_height) copy.Inflate(radius, radius) return copy.ContainsXY(x, y) - def MoveTexture(self, dx, dy): + def MoveTexture(self, dx: int, dy: int) -> None: """Move texture on the screen""" self.coords[0] += dx self.coords[1] += dy - def SetCoords(self, coords): + def SetCoords(self, coords: tuple[int, int]) -> None: """Set coordinates""" dx = coords[0] - self.coords[0] dy = coords[1] - self.coords[1] self.MoveTexture(dx, dy) - def GetId(self): + def GetId(self) -> int: """Returns image id.""" return self.id - def SetActive(self, active=True): - self.active = active + def SetActive(self, active: bool = True) -> None: + self.active: bool = active - def IsActive(self): + def IsActive(self) -> bool: return self.active class ImageTexture(Texture): """Class representing OpenGL texture as an overlay image""" - def __init__(self, filepath, overlayId, coords, cmd): + def __init__( + self, filepath: StrPath, overlayId, coords: tuple[int, int], cmd + ) -> None: """Load image to texture :param filepath: path to image file @@ -2440,11 +2809,11 @@ def GetCmd(self): """Returns overlay command.""" return self.cmd - def Corresponds(self, item): + def Corresponds(self, item) -> bool: return sorted(self.GetCmd()) == sorted(item.GetCmd()) -__all__ = [ +__all__: list[str] = [ "DM_FLAT", "DM_GOURAUD", "DM_GRID_SURF", diff --git a/gui/wxpython/photo2image/g.gui.photo2image.py b/gui/wxpython/photo2image/g.gui.photo2image.py index 443a43f2d81..e3d6ea140f2 100755 --- a/gui/wxpython/photo2image/g.gui.photo2image.py +++ b/gui/wxpython/photo2image/g.gui.photo2image.py @@ -69,6 +69,7 @@ """ Module to run GCP management tool as standalone application. """ + import os import grass.script as gs diff --git a/gui/wxpython/photo2image/ip2i_manager.py b/gui/wxpython/photo2image/ip2i_manager.py index d720e0a5adc..f7a43bbcf6b 100644 --- a/gui/wxpython/photo2image/ip2i_manager.py +++ b/gui/wxpython/photo2image/ip2i_manager.py @@ -67,22 +67,13 @@ def getSmallUpArrowImage(): - stream = open(os.path.join(globalvar.IMGDIR, "small_up_arrow.png"), "rb") - try: - img = wx.Image(stream) - finally: - stream.close() - return img + with open(os.path.join(globalvar.IMGDIR, "small_up_arrow.png"), "rb") as stream: + return wx.Image(stream) def getSmallDnArrowImage(): - stream = open(os.path.join(globalvar.IMGDIR, "small_down_arrow.png"), "rb") - try: - img = wx.Image(stream) - finally: - stream.close() - stream.close() - return img + with open(os.path.join(globalvar.IMGDIR, "small_down_arrow.png"), "rb") as stream: + return wx.Image(stream) class GCPWizard: @@ -255,8 +246,9 @@ def SetSrcEnv(self, location, mapset): try: f = open(self.source_gisrc, mode="w") - for line in self.gisrc_dict.items(): - f.write(line[0] + ": " + line[1] + "\n") + f.writelines( + line[0] + ": " + line[1] + "\n" for line in self.gisrc_dict.items() + ) finally: f.close() @@ -308,6 +300,7 @@ def __init__( lmgr=None, camera=None, ): + # pylint: disable=super-init-not-called; See InitMapDisplay() self.grwiz = grwiz # GR Wizard self._giface = giface @@ -771,9 +764,8 @@ def SetSettings(self): font = self.GetFont() font.SetPointSize(int(spx) + 2) - textProp = {} - textProp["active"] = True - textProp["font"] = font + textProp = {"active": True, "font": font} + self.pointsToDrawSrc.SetPropertyVal("text", textProp) self.pointsToDrawTgt.SetPropertyVal("text", copy(textProp)) @@ -1144,7 +1136,7 @@ def OnGeorectDone(self, **kargs): """Print final message""" global maptype if maptype == "raster": - return + pass def OnSettings(self, event): """GCP Manager settings""" @@ -1280,8 +1272,7 @@ def RMSError(self, xygroup, order): GError( parent=self, message=_( - "Could not calculate RMS Error.\n" - "Possible error with m.transform." + "Could not calculate RMS Error.\nPossible error with m.transform." ), ) return @@ -1416,8 +1407,7 @@ def GetNewExtent(self, region, map=None): GError( parent=self, message=_( - "Could not calculate new extends.\n" - "Possible error with m.transform." + "Could not calculate new extends.\nPossible error with m.transform." ), ) return @@ -1978,11 +1968,11 @@ def __init__( size=wx.DefaultSize, style=wx.DEFAULT_DIALOG_STYLE, ): - wx.Dialog.__init__(self, parent, id, title, pos, size, style) """ Dialog to set profile text options: font, title and font size, axis labels and font size """ + wx.Dialog.__init__(self, parent, id, title, pos, size, style) # # initialize variables # @@ -2354,8 +2344,7 @@ def OnSDFactor(self, event): GError( parent=self, message=_( - "RMS threshold factor is < 1\n" - "Too many points might be highlighted" + "RMS threshold factor is < 1\nToo many points might be highlighted" ), ) diff --git a/gui/wxpython/psmap/dialogs.py b/gui/wxpython/psmap/dialogs.py index 428ae248c87..41ae0bbceb7 100644 --- a/gui/wxpython/psmap/dialogs.py +++ b/gui/wxpython/psmap/dialogs.py @@ -792,11 +792,12 @@ def OnChoice(self, event): currPaper = self.paperTable[self.getCtrl("Format").GetSelection()] currUnit = self.unitConv.findUnit(self.getCtrl("Units").GetStringSelection()) currOrientIdx = self.getCtrl("Orientation").GetSelection() - newSize = {} - for item in self.cat[3:]: - newSize[item] = self.unitConv.convert( + newSize = { + item: self.unitConv.convert( float(currPaper[item]), fromUnit="inch", toUnit=currUnit ) + for item in self.cat[3:] + } enable = True if currPaper["Format"] != _("custom"): @@ -4966,8 +4967,9 @@ def _scalebarPanel(self): sbTypeText = StaticText(panel, id=wx.ID_ANY, label=_("Type:")) self.sbCombo = BitmapComboBox(panel, style=wx.CB_READONLY) # only temporary, images must be moved away - imagePath = os.path.join(globalvar.IMGDIR, "scalebar-fancy.png"), os.path.join( - globalvar.IMGDIR, "scalebar-simple.png" + imagePath = ( + os.path.join(globalvar.IMGDIR, "scalebar-fancy.png"), + os.path.join(globalvar.IMGDIR, "scalebar-simple.png"), ) for item, path in zip(["fancy", "simple"], imagePath): bitmap = EmptyBitmap(0, 0) if not os.path.exists(path) else wx.Bitmap(path) @@ -5470,10 +5472,11 @@ def _positionPanel(self, notebook): ) sizerR = wx.StaticBoxSizer(box, wx.VERTICAL) flexSizer = wx.FlexGridSizer(rows=3, cols=3, hgap=5, vgap=5) - ref = [] - for row in ["upper", "center", "lower"]: - for col in ["left", "center", "right"]: - ref.append(row + " " + col) + ref = [ + row + " " + col + for row in ["upper", "center", "lower"] + for col in ["left", "center", "right"] + ] self.radio = [ RadioButton(panel, id=wx.ID_ANY, label="", style=wx.RB_GROUP, name=ref[0]) ] diff --git a/gui/wxpython/psmap/frame.py b/gui/wxpython/psmap/frame.py index bb71fd856a5..41536cd921d 100644 --- a/gui/wxpython/psmap/frame.py +++ b/gui/wxpython/psmap/frame.py @@ -19,6 +19,7 @@ import queue as Queue import sys from math import cos, pi, sin, sqrt +from pathlib import Path import wx @@ -358,13 +359,12 @@ def OnPreview(self, event): def PSFile(self, filename=None, pdf=False): """Create temporary instructions file and run ps.map with output = filename""" instrFile = gs.tempfile() - instrFileFd = open(instrFile, mode="wb") - content = self.InstructionFile() - if not content: - return - instrFileFd.write(content) - instrFileFd.flush() - instrFileFd.close() + with open(instrFile, mode="wb") as instrFileFd: + content = self.InstructionFile() + if not content: + return + instrFileFd.write(content) + instrFileFd.flush() temp = False regOld = gs.region(env=self.env) @@ -468,8 +468,7 @@ def OnCmdDone(self, event): event.userData["pdfname"], ] message = _( - "Program {} is not available." - " Please install it to create PDF.\n\n " + "Program {} is not available. Please install it to create PDF.\n\n " ).format(pdf_rendering_prog) try: proc = gs.Popen(command) @@ -598,12 +597,10 @@ def OnInstructionFile(self, event): wildcard="*.psmap|*.psmap|Text file(*.txt)|*.txt|All files(*.*)|*.*" ) if filename: - instrFile = open(filename, "wb") content = self.InstructionFile() if not content: return - instrFile.write(content) - instrFile.close() + Path(filename).write_bytes(content) def OnLoadFile(self, event): """Launch file dialog and load selected file""" @@ -1068,13 +1065,12 @@ def getInitMap(self): """Create default map frame when no map is selected, needed for coordinates in map units""" instrFile = gs.tempfile() - instrFileFd = open(instrFile, mode="wb") - content = self.InstructionFile() - if not content: - return - instrFileFd.write(content) - instrFileFd.flush() - instrFileFd.close() + with open(instrFile, mode="wb") as instrFileFd: + content = self.InstructionFile() + if not content: + return + instrFileFd.write(content) + instrFileFd.flush() page = self.instruction.FindInstructionByType("page") mapInitRect = GetMapBounds( diff --git a/gui/wxpython/psmap/instructions.py b/gui/wxpython/psmap/instructions.py index dcdfb907383..4528e0a0d93 100644 --- a/gui/wxpython/psmap/instructions.py +++ b/gui/wxpython/psmap/instructions.py @@ -140,10 +140,7 @@ def AddInstruction(self, instruction): def FindInstructionByType(self, type, list=False): """Find instruction(s) with the given type""" - inst = [] - for each in self.instruction: - if each.type == type: - inst.append(each) + inst = [each for each in self.instruction if each.type == type] if len(inst) == 1 and not list: return inst[0] return inst @@ -1676,8 +1673,7 @@ def __str__(self): def Read(self, instruction, text, **kwargs): """Read instruction and save information""" - instr = {} - instr["rLegend"] = True + instr = {"rLegend": True} for line in text: try: if line.startswith("where"): @@ -1840,8 +1836,7 @@ def __str__(self): def Read(self, instruction, text, **kwargs): """Read instruction and save information""" - instr = {} - instr["vLegend"] = True + instr = {"vLegend": True} for line in text: try: if line.startswith("where"): @@ -1906,8 +1901,7 @@ def __str__(self): def Read(self, instruction, text): """Read instruction and save information""" - instr = {} - instr["isRaster"] = True + instr = {"isRaster": True} try: map = text.split()[1] except IndexError: diff --git a/gui/wxpython/psmap/utils.py b/gui/wxpython/psmap/utils.py index bb75c0eb7d5..10b10f96297 100644 --- a/gui/wxpython/psmap/utils.py +++ b/gui/wxpython/psmap/utils.py @@ -16,8 +16,15 @@ @author Anna Kratochvilova """ +from __future__ import annotations + +from math import ceil, cos, floor, fmod, radians, sin +from typing import overload + import wx -from math import ceil, floor, sin, cos, pi +from core.gcmd import GError, RunCommand + +import grass.script as gs try: from PIL import Image as PILImage # noqa @@ -26,9 +33,6 @@ except ImportError: havePILImage = False -import grass.script as gs -from core.gcmd import RunCommand, GError - class Rect2D(wx.Rect2D): """Class representing rectangle with floating point values. @@ -145,7 +149,17 @@ def convert(self, value, fromUnit=None, toUnit=None): return float(value) / self._units[fromUnit]["val"] * self._units[toUnit]["val"] -def convertRGB(rgb): +@overload +def convertRGB(rgb: wx.Colour) -> str: + pass + + +@overload +def convertRGB(rgb: str) -> wx.Colour | None: + pass + + +def convertRGB(rgb: wx.Colour | str) -> str | wx.Colour | None: """Converts wx.Colour(r,g,b,a) to string 'r:g:b' or named color, or named color/r:g:b string to wx.Colour, depending on input""" # transform a wx.Colour tuple into an r:g:b string @@ -159,12 +173,10 @@ def convertRGB(rgb): return name return str(rgb.Red()) + ":" + str(rgb.Green()) + ":" + str(rgb.Blue()) # transform a GRASS named color or an r:g:b string into a wx.Colour tuple - color = ( - int(gs.parse_color(rgb)[0] * 255), - int(gs.parse_color(rgb)[1] * 255), - int(gs.parse_color(rgb)[2] * 255), - ) - color = wx.Colour(*color) + parsed_color = gs.parse_color(rgb) + if parsed_color is None: + return None + color = wx.Colour(*tuple(int(x * 255) for x in parsed_color)) if color.IsOk(): return color return None @@ -396,23 +408,27 @@ def getRasterType(map): return None -def BBoxAfterRotation(w, h, angle): - """Compute bounding box or rotated rectangle +def BBoxAfterRotation(w: float, h: float, angle: float) -> tuple[int, int]: + """Compute the bounding box of a rotated rectangle :param w: rectangle width :param h: rectangle height :param angle: angle (0, 360) in degrees """ - angleRad = angle / 180.0 * pi - ct = cos(angleRad) - st = sin(angleRad) - - hct = h * ct - wct = w * ct - hst = h * st - wst = w * st + + angle = fmod(angle, 360) + angleRad: float = radians(angle) + ct: float = cos(angleRad) + st: float = sin(angleRad) + + hct: float = h * ct + wct: float = w * ct + hst: float = h * st + wst: float = w * st y = x = 0 + if angle == 0: + return (ceil(w), ceil(h)) if 0 < angle <= 90: y_min = y y_max = y + hct + wst @@ -433,7 +449,10 @@ def BBoxAfterRotation(w, h, angle): y_max = y + hct x_min = x x_max = x + wct - hst + else: + msg = "The angle argument should be between 0 and 360 degrees" + raise ValueError(msg) - width = ceil(abs(x_max) + abs(x_min)) - height = ceil(abs(y_max) + abs(y_min)) - return width, height + width: int = ceil(abs(x_max) + abs(x_min)) + height: int = ceil(abs(y_max) + abs(y_min)) + return (width, height) diff --git a/gui/wxpython/rdigit/controller.py b/gui/wxpython/rdigit/controller.py index 9b70a2cc468..9617e1d5178 100644 --- a/gui/wxpython/rdigit/controller.py +++ b/gui/wxpython/rdigit/controller.py @@ -648,9 +648,8 @@ def _rasterize(self, text, bufferDist, mapType, tempRaster): :return: output raster map name as a result of digitization """ output = "x" + str(uuid.uuid4())[:8] - asciiFile = tempfile.NamedTemporaryFile(mode="w", delete=False) - asciiFile.write("\n".join(text)) - asciiFile.close() + with tempfile.NamedTemporaryFile(mode="w", delete=False) as asciiFile: + asciiFile.write("\n".join(text)) if bufferDist: bufferDist /= 2.0 diff --git a/gui/wxpython/rdigit/dialogs.py b/gui/wxpython/rdigit/dialogs.py index fa9f57b8253..d0aa967a909 100644 --- a/gui/wxpython/rdigit/dialogs.py +++ b/gui/wxpython/rdigit/dialogs.py @@ -91,7 +91,7 @@ def OnBackgroundMap(self, event): ret = grast.raster_info(value) self._typeChoice.SetStringSelection(ret["datatype"]) except CalledModuleError: - return + pass def OnOK(self, event): mapName = self.GetMapName() diff --git a/gui/wxpython/rdigit/toolbars.py b/gui/wxpython/rdigit/toolbars.py index d3d9aed5eb9..ba7e8e7e6bc 100644 --- a/gui/wxpython/rdigit/toolbars.py +++ b/gui/wxpython/rdigit/toolbars.py @@ -207,7 +207,7 @@ def _cellValueChanged(self): value = float(value) self._controller.SetCellValue(value) except ValueError: - return + pass def _widthValueChanged(self): value = self._widthValue.GetValue() @@ -216,7 +216,6 @@ def _widthValueChanged(self): self._controller.SetWidthValue(value) except ValueError: self._controller.SetWidthValue(0) - return def _changeDrawColor(self): color = self._color.GetColour() diff --git a/gui/wxpython/rlisetup/frame.py b/gui/wxpython/rlisetup/frame.py index 3905fa9b09d..3563590d561 100644 --- a/gui/wxpython/rlisetup/frame.py +++ b/gui/wxpython/rlisetup/frame.py @@ -48,9 +48,8 @@ def __init__( parent=self.panel, id=wx.ID_ANY, style=wx.TE_MULTILINE, size=(-1, 75) ) self.textCtrl.Bind(wx.EVT_TEXT, self.OnFileText) - f = open(self.pathfile) - self.textCtrl.SetValue("".join(f.readlines())) - f.close() + with open(self.pathfile) as f: + self.textCtrl.SetValue("".join(f.readlines())) # BUTTONS #definition self.btn_close = Button(parent=self, id=wx.ID_EXIT) self.btn_ok = Button(parent=self, id=wx.ID_SAVE) @@ -107,11 +106,10 @@ def OnOk(self, event): ) if dlg.ShowModal() == wx.ID_YES: - f = codecs.open( + with codecs.open( self.pathfile, encoding=self.enc, mode="w", errors="replace" - ) - f.write(self.text + os.linesep) - f.close() + ) as f: + f.write(self.text + os.linesep) dlg.Destroy() self.Destroy() @@ -213,12 +211,13 @@ def _layout(self): def ListFiles(self): """Check the configuration files inside the path""" # list of configuration file - listfiles = [] # return all the configuration files in self.rlipath, check if there are # link or directory and doesn't add them - for rli_conf in os.listdir(self.rlipath): - if os.path.isfile(os.path.join(self.rlipath, rli_conf)): - listfiles.append(rli_conf) + listfiles = [ + rli_conf + for rli_conf in os.listdir(self.rlipath) + if os.path.isfile(os.path.join(self.rlipath, rli_conf)) + ] return sorted(listfiles) def OnClose(self, event): diff --git a/gui/wxpython/rlisetup/functions.py b/gui/wxpython/rlisetup/functions.py index 3670a504621..6a6eec30515 100644 --- a/gui/wxpython/rlisetup/functions.py +++ b/gui/wxpython/rlisetup/functions.py @@ -12,7 +12,7 @@ class SamplingType: - """ " + """ KMVWINC = samplingtype=moving, regionbox=keyboard, shape=circle KMVWINR = samplingtype moving, regionbox=keyboard, shape=rectangle MMVWINC = samplingtype=moving, regionbox=mouse, shape=circle diff --git a/gui/wxpython/rlisetup/g.gui.rlisetup.html b/gui/wxpython/rlisetup/g.gui.rlisetup.html index ae5663bb7d6..cda5d236ccd 100644 --- a/gui/wxpython/rlisetup/g.gui.rlisetup.html +++ b/gui/wxpython/rlisetup/g.gui.rlisetup.html @@ -61,14 +61,14 @@

    Usage details

    and other notes (disposition of sample areas etc). --> Configuration files are saved in the folder - C:\Users\userxy\AppData\Roaming\GRASS8\r.li\ (MS-Windows) or - $HOME/.r.li/ (GNU/Linux) (the file name can be + C:\Users\userxy\AppData\Roaming\GRASS8\r.li\ (MS-Windows) or + $HOME/.r.li/ (GNU/Linux) (the file name can be defined by the user). The output or an analysis can either be a new raster map (in case of using a "moving window" analysis) or be an ASCII text file (when not performing a "moving window" analysis) containing the result. Such text file will be saved in the folder - C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ - (MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux). + C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ + (MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux).
    All dimensions are percentages of raster rows or columns. @@ -315,7 +315,7 @@

    Whole region analysis

    r.li.shannon input=lsat7_2000_40 conf=whole_region output=lsat7_2000_40_shannon
    -The result is the new text file "forests_p_dens7" (stored in folder $HOME/.r.li/output/. +The result is the new text file "forests_p_dens7" (stored in folder $HOME/.r.li/output/.
    See the respective modules for further examples. @@ -323,7 +323,7 @@

    REFERENCES

    McGarigal, K., and B. J. Marks. 1995. FRAGSTATS: spatial pattern analysis program for quantifying landscape structure. USDA For. Serv. -Gen. Tech. Rep. PNW-351. (PDF) +Gen. Tech. Rep. PNW-351. (PDF)

    SEE ALSO

    diff --git a/gui/wxpython/rlisetup/sampling_frame.py b/gui/wxpython/rlisetup/sampling_frame.py index df2ee8f62af..e62737449e6 100644 --- a/gui/wxpython/rlisetup/sampling_frame.py +++ b/gui/wxpython/rlisetup/sampling_frame.py @@ -255,18 +255,16 @@ def nextRegion(self, next=True, area=None): ) def writeArea(self, coords, rasterName): - polyfile = tempfile.NamedTemporaryFile(delete=False) - polyfile.write("AREA\n") - for coor in coords: - east, north = coor - point = " %s %s\n" % (east, north) - polyfile.write(point) - - catbuf = "=%d a\n" % self.catId - polyfile.write(catbuf) - self.catId += 1 - - polyfile.close() + with tempfile.NamedTemporaryFile(delete=False) as polyfile: + polyfile.write("AREA\n") + for coor in coords: + east, north = coor + point = " %s %s\n" % (east, north) + polyfile.write(point) + catbuf = "=%d a\n" % self.catId + polyfile.write(catbuf) + self.catId += 1 + region_settings = grass.parse_command("g.region", flags="p", delimiter=":") pname = polyfile.name.split("/")[-1] tmpraster = "rast_" + pname @@ -431,7 +429,7 @@ def _rectangleDrawn(self): dlg.Destroy() elif self.samplingtype != SamplingType.WHOLE: - """When drawing finished, get region values""" + # When drawing finished, get region values self.sampleFrameChanged.emit(region=region) diff --git a/gui/wxpython/rlisetup/wizard.py b/gui/wxpython/rlisetup/wizard.py index 35aa3384e38..c2485c28e86 100644 --- a/gui/wxpython/rlisetup/wizard.py +++ b/gui/wxpython/rlisetup/wizard.py @@ -4,8 +4,7 @@ @brief GUI per r.li.setup module Classes: - - RLiSetupFrame (first frame to show existing conf file and choose some - operation) + - RLiSetupFrame (first frame to show existing conf file and choose some operation) - RLIWizard (the main wizard) - FirstPage (first page of wizard, choose name of conf file, raster, vector, sampling region) @@ -20,28 +19,33 @@ @author Luca Delucchi """ +from __future__ import annotations + import os +from typing import TYPE_CHECKING import wx from core.globalvar import wxPythonPhoenix -if wxPythonPhoenix: +if wxPythonPhoenix or TYPE_CHECKING: from wx import adv as wiz from wx.adv import Wizard else: from wx import wizard as wiz from wx.wizard import Wizard -import wx.lib.scrolledpanel as scrolled +import wx.lib.scrolledpanel as scrolled +from core.gcmd import GError, GMessage, RunCommand from gui_core import gselect from gui_core.wrap import Button, StaticText, TextCtrl from location_wizard.wizard import GridBagSizerTitledPage as TitledPage from rlisetup.functions import checkValue, retRLiPath from rlisetup.sampling_frame import RLiSetupMapPanel + +from grass.exceptions import CalledModuleError from grass.script import core as grass from grass.script import raster as grast from grass.script import vector as gvect -from grass.exceptions import CalledModuleError from .functions import ( SamplingType, @@ -50,7 +54,9 @@ obtainCategories, sampleAreaVector, ) -from core.gcmd import GError, GMessage, RunCommand + +if TYPE_CHECKING: + from wx.adv import WizardEvent class RLIWizard: @@ -152,11 +158,10 @@ def __init__(self, parent): def _write_confile(self): """Write the configuration file""" - f = open(os.path.join(self.rlipath, self.startpage.conf_name), "w") - self.rasterinfo = grast.raster_info(self.startpage.rast) - self._write_region(f) - self._write_area(f) - f.close() + with open(os.path.join(self.rlipath, self.startpage.conf_name), "w") as f: + self.rasterinfo = grast.raster_info(self.startpage.rast) + self._write_region(f) + self._write_area(f) def _temp_region(self): # save current settings: @@ -692,7 +697,7 @@ def OnLayer(self, event): next = wx.FindWindowById(wx.ID_FORWARD) next.Enable(self.CheckInput()) - def OnEnterPage(self, event): + def OnEnterPage(self, event: WizardEvent) -> None: """Sets the default values, for the entire map""" next = wx.FindWindowById(wx.ID_FORWARD) next.Enable(self.CheckInput()) @@ -721,9 +726,7 @@ def CheckVector(self, vector): ) return False, [] if links > 0: - layers = [] - for i in range(1, links + 1): - layers.append(str(i)) + layers = [str(i) for i in range(1, links + 1)] return True, layers return False, [] @@ -735,7 +738,7 @@ def CheckInput(self): """ return bool(self.conf_name and bool(self.rast and bool(self.VectorEnabled))) - def OnExitPage(self, event=None): + def OnExitPage(self, event: WizardEvent | None = None) -> None: """Function during exiting""" next = wx.FindWindowById(wx.ID_FORWARD) next.Enable(self.CheckInput()) @@ -749,7 +752,6 @@ def OnExitPage(self, event=None): elif self.region == "draw": self.SetNext(self.parent.drawsampleframepage) self.parent.samplingareapage.SetPrev(self.parent.drawsampleframepage) - return class KeyboardPage(TitledPage): @@ -891,7 +893,7 @@ def OnEnterPage(self, event): self.ColLentxt.SetValue(self.col_len) self.RowLentxt.SetValue(self.row_len) - def OnExitPage(self, event=None): + def OnExitPage(self, event: WizardEvent | None = None) -> None: """Function during exiting""" if ( self.row_len == "" @@ -923,7 +925,7 @@ def SampleFrameChanged(self, region): else: wx.FindWindowById(wx.ID_FORWARD).Enable(False) - def OnEnterPage(self, event): + def OnEnterPage(self, event: WizardEvent) -> None: """Function during entering""" if self.mapPanel is None: self.mapPanel = RLiSetupMapPanel(self, samplingType="drawFrame") @@ -951,7 +953,7 @@ def OnEnterPage(self, event): render=True, ) - def OnExitPage(self, event=None): + def OnExitPage(self, event: WizardEvent | None = None) -> None: """Function during exiting""" if event.GetDirection(): self.SetNext(self.parent.samplingareapage) @@ -1071,7 +1073,7 @@ def OnNumRegions(self, event): else: wx.FindWindowById(wx.ID_FORWARD).Enable(False) - def OnEnterPage(self, event): + def OnEnterPage(self, event: WizardEvent) -> None: """Insert values into text controls for summary of location creation options """ @@ -1230,7 +1232,7 @@ def afterRegionDrawn(self, marea): ) wx.FindWindowById(wx.ID_FORWARD).Enable(False) - def OnEnterPage(self, event): + def OnEnterPage(self, event: WizardEvent) -> None: """Function during entering""" if self.parent.samplingareapage.samplingtype == SamplingType.WHOLE: self.title.SetLabel(_("Draw moving windows region")) @@ -1265,12 +1267,12 @@ def OnEnterPage(self, event): render=True, ) - # def OnExitPage(self, event=None): - # Function during exiting - # print event.GetDirection() - # if event.GetDirection(): - # self.SetNext(self.parent.samplingareapage) - # self.parent.samplingareapage.SetPrev(self) + # def OnExitPage(self, event: WizardEvent | None = None) -> None: + # """Function during exiting""" + # print(event.GetDirection()) + # if event.GetDirection(): + # self.SetNext(self.parent.samplingareapage) + # self.parent.samplingareapage.SetPrev(self) class SampleUnitsKeyPage(TitledPage): @@ -1400,14 +1402,14 @@ def __init__(self, wizard, parent): # self.Bind(wiz.EVT_WIZARD_PAGE_CHANGING, self.OnExitPage) self.OnType(None) - def OnEnterPage(self, event=None): + def OnEnterPage(self, event: WizardEvent | None = None) -> None: """Function during entering""" # This is an hack to force the user to choose Rectangle or Circle - self.typeBox.SetSelection(2), + self.typeBox.SetSelection(2) self.typeBox.ShowItem(2, False) self.panelSizer.Layout() - def OnExitPage(self, event=None): + def OnExitPage(self, event: WizardEvent | None = None) -> None: """Function during exiting""" if event.GetDirection(): self.SetNext(self.parent.summarypage) @@ -1545,7 +1547,7 @@ def __init__(self, wizard, parent): self.heightTxt.Bind(wx.EVT_TEXT, self.OnHeight) wx.FindWindowById(wx.ID_FORWARD).Enable(False) - def OnEnterPage(self, event): + def OnEnterPage(self, event: WizardEvent) -> None: # This is an hack to force the user to choose Rectangle or Circle # self.typeBox.SetSelection(2), # self.typeBox.ShowItem(2, False) @@ -1606,7 +1608,7 @@ def __init__(self, wizard, parent): choices=[_("Rectangle"), _("Circle"), ("")], ) # This is an hack to force the user to choose Rectangle or Circle - self.typeBox.SetSelection(2), + self.typeBox.SetSelection(2) self.typeBox.ShowItem(2, False) self.sizer.Add(self.typeBox, flag=wx.ALIGN_LEFT, pos=(0, 0), span=(1, 2)) @@ -1634,7 +1636,7 @@ def __init__(self, wizard, parent): self.OnType(None) self.regionNumTxt.SetValue("") - def OnEnterPage(self, event): + def OnEnterPage(self, event: WizardEvent) -> None: """Function during entering""" if self.numregions: wx.FindWindowById(wx.ID_FORWARD).Enable(True) @@ -1693,7 +1695,7 @@ def OnNumRegions(self, event): else: wx.FindWindowById(wx.ID_FORWARD).Enable(False) - def OnExitPage(self, event=None): + def OnExitPage(self, event: WizardEvent | None = None) -> None: """Function during exiting""" if event.GetDirection(): self.SetNext(self.drawsampleunitspage) @@ -1730,7 +1732,7 @@ def SampleFrameChanged(self, region): ) wx.FindWindowById(wx.ID_FORWARD).Enable(False) - def OnEnterPage(self, event): + def OnEnterPage(self, event: WizardEvent) -> None: """Function during entering""" if self.parent.samplingareapage.samplingtype in { @@ -1777,9 +1779,8 @@ def OnEnterPage(self, event): render=True, ) - def OnExitPage(self, event=None): + def OnExitPage(self, event: WizardEvent | None = None) -> None: """Function during exiting""" - # if event.GetDirection(): # self.SetNext(self.parent.samplingareapage) # self.parent.samplingareapage.SetPrev(self) @@ -1889,7 +1890,7 @@ def newCat(self): self.map_.DeleteLayer(layer) self.areaText.SetLabel("Is this area (cat={n}) ok?".format(n=cat)) - def OnEnterPage(self, event): + def OnEnterPage(self, event: WizardEvent) -> None: """Function during entering: draw the raster map and the first vector feature""" if self.mapPanel is None: @@ -1929,7 +1930,7 @@ def OnEnterPage(self, event): ) self.newCat() - def OnExitPage(self, event=None): + def OnExitPage(self, event: WizardEvent | None = None) -> None: """Function during exiting""" grass.del_temp_region() @@ -2136,7 +2137,7 @@ def __init__(self, wizard, parent): flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.ALL, ) - def OnEnterPage(self, event): + def OnEnterPage(self, event: WizardEvent) -> None: """Insert values into text controls for summary of location creation options """ diff --git a/gui/wxpython/startup/guiutils.py b/gui/wxpython/startup/guiutils.py index fa4d0a8760a..bb5cbc88d0d 100644 --- a/gui/wxpython/startup/guiutils.py +++ b/gui/wxpython/startup/guiutils.py @@ -707,8 +707,7 @@ def switch_mapset_interactively( GMessage( parent=guiparent, message=_( - "Current project is <%(loc)s>.\n" - "Current mapset is <%(mapset)s>." + "Current project is <%(loc)s>.\nCurrent mapset is <%(mapset)s>." ) % {"loc": location, "mapset": mapset}, ) diff --git a/gui/wxpython/startup/locdownload.py b/gui/wxpython/startup/locdownload.py index 7e24724d862..1eb1b6f6e23 100644 --- a/gui/wxpython/startup/locdownload.py +++ b/gui/wxpython/startup/locdownload.py @@ -96,8 +96,8 @@ def write(self, string): heigth = self._get_heigth(string) wx.CallAfter(self.out.SetLabel, string) self._resize(heigth) - except wx.PyDeadObjectError: - # window closed -> PyDeadObjectError + except (RuntimeError, AttributeError): + # window closed or destroyed pass def flush(self): @@ -231,9 +231,7 @@ def __init__(self, parent, database, locations=LOCATIONS): parent=self, label=_("Select sample project to download:") ) - choices = [] - for item in self.locations: - choices.append(item["label"]) + choices = [item["label"] for item in self.locations] self.choice = wx.Choice(parent=self, choices=choices) self.choice.Bind(wx.EVT_CHOICE, self.OnChangeChoice) diff --git a/gui/wxpython/timeline/frame.py b/gui/wxpython/timeline/frame.py index c5c54982ed3..d330277dda3 100644 --- a/gui/wxpython/timeline/frame.py +++ b/gui/wxpython/timeline/frame.py @@ -264,7 +264,6 @@ def _getData(self, timeseries): def _draw3dFigure(self): """Draws 3d view (spatio-temporal extents). - Only for matplotlib versions >= 1.0.0. Earlier versions cannot draw time ticks and alpha and it has a slightly different API. @@ -272,9 +271,7 @@ def _draw3dFigure(self): self.axes3d.clear() self.axes3d.grid(False) # self.axes3d.grid(True) - convert = ( - mdates.date2num if self.temporalType == "absolute" else lambda x: x - ) # noqa: E731 + convert = mdates.date2num if self.temporalType == "absolute" else lambda x: x # noqa: E731 colors = cycle(COLORS) plots = [] @@ -320,9 +317,7 @@ def _draw2dFigure(self): """Draws 2D plot (temporal extents)""" self.axes2d.clear() self.axes2d.grid(True) - convert = ( - mdates.date2num if self.temporalType == "absolute" else lambda x: x - ) # noqa: E731 + convert = mdates.date2num if self.temporalType == "absolute" else lambda x: x # noqa: E731 colors = cycle(COLORS) @@ -640,7 +635,6 @@ class DataCursor: """A simple data cursor widget that displays the x,y location of a matplotlib artist when it is selected. - Source: https://stackoverflow.com/questions/4652439/ is-there-a-matplotlib-equivalent-of-matlabs-datacursormode/4674445 """ diff --git a/gui/wxpython/tools/build_modules_xml.py b/gui/wxpython/tools/build_modules_xml.py index a97bdeb4627..2a69ce6a696 100644 --- a/gui/wxpython/tools/build_modules_xml.py +++ b/gui/wxpython/tools/build_modules_xml.py @@ -70,7 +70,7 @@ def parse_modules(fd): def get_module_metadata(name): - """ + """Gets the module's metadata for a given module name >>> get_module_metadata("g.region") ('Manages the boundary definitions for the geographic region.', ['general', 'settings']) diff --git a/gui/wxpython/tools/update_menudata.py b/gui/wxpython/tools/update_menudata.py index 08897e6147d..2c42f618c10 100644 --- a/gui/wxpython/tools/update_menudata.py +++ b/gui/wxpython/tools/update_menudata.py @@ -22,8 +22,8 @@ import os import sys import tempfile - import xml.etree.ElementTree as ET +from subprocess import DEVNULL from grass.script import core as grass from grass.script import task as gtask @@ -71,9 +71,7 @@ def updateData(data, modules): if node.tag != "menuitem": continue - item = {} - for child in node: - item[child.tag] = child.text + item = {child.tag: child.text for child in node} if "command" not in item: continue @@ -142,9 +140,8 @@ def main(argv=None): print(sys.stderr, __doc__, file=sys.stderr) return 1 - nuldev = open(os.devnull, "w+") grass.info("Step 1: running make...") - grass.call(["make"], stderr=nuldev) + grass.call(["make"], stderr=DEVNULL) grass.info("Step 2: parsing modules...") modules = {} modules = parseModules() @@ -154,14 +151,13 @@ def main(argv=None): updateData(data, modules) if printDiff: - tempFile = tempfile.NamedTemporaryFile() - grass.info("Step 5: diff menu data...") - writeData(data, tempFile.name) - - grass.call( - ["diff", "-u", os.path.join("xml", "menudata.xml"), tempFile.name], - stderr=nuldev, - ) + with tempfile.NamedTemporaryFile() as tempFile: + grass.info("Step 5: diff menu data...") + writeData(data, tempFile.name) + grass.call( + ["diff", "-u", os.path.join("xml", "menudata.xml"), tempFile.name], + stderr=DEVNULL, + ) else: grass.info("Step 5: writing menu data (menudata.xml)...") writeData(data) diff --git a/gui/wxpython/tplot/frame.py b/gui/wxpython/tplot/frame.py index 71705cc0160..21fe822cf4a 100755 --- a/gui/wxpython/tplot/frame.py +++ b/gui/wxpython/tplot/frame.py @@ -18,6 +18,7 @@ @author Luca Delucchi @author start stvds support Matej Krejci """ + import os from itertools import cycle from pathlib import Path @@ -85,7 +86,7 @@ def check_version(*version) -> bool: versionInstalled.append(v) except ValueError: versionInstalled.append(0) - return not versionInstalled < list(version) + return versionInstalled >= list(version) def findBetween(s, first, last): @@ -474,9 +475,7 @@ def _getSTRDdata(self, timeseries): GError( parent=self, message=_( - "Datasets have different " - "time unit which is not " - "allowed." + "Datasets have different time unit which is not allowed." ), ) return @@ -670,8 +669,7 @@ def _getSTVDData(self, timeseries): parent=self, showTraceback=False, message=_( - "No connection between vector map {vmap} " - "and layer {la}" + "No connection between vector map {vmap} and layer {la}" ).format(vmap=row["name"], la=lay), ) return @@ -760,8 +758,7 @@ def _writeCSV(self, x, y): with open(self.csvpath, "w", newline="") as fi: writer = csv.writer(fi) if self.header: - head = ["Time"] - head.extend(self.yticksNames) + head = ["Time", *self.yticksNames] writer.writerow(head) writer.writerows(zipped) @@ -807,7 +804,7 @@ def _drawSimpleLinRegLine(self, xdata, ydata): x=np.array(xdata), y=np.array(ydata), returnFormula=True ) - r2 = "r\u00B2 = {:.5f}".format( + r2 = "r\u00b2 = {:.5f}".format( np.corrcoef(np.array(xdata), np.array(ydata))[0, 1] ** 2 ) self.plots.append( @@ -1337,10 +1334,11 @@ def AddDataset(self, yranges, xranges, datasetName): self.data[datasetName][xranges[i]] = yranges[i] def GetInformation(self, x): - values = {} - for key, value in self.data.items(): - if value[x]: - values[key] = [self.convert(x), value[x]] + values = { + key: [self.convert(x), value[x]] + for key, value in self.data.items() + if value[x] + } if len(values) == 0: return None @@ -1372,7 +1370,6 @@ class DataCursor: """A simple data cursor widget that displays the x,y location of a matplotlib artist when it is selected. - Source: https://stackoverflow.com/questions/4652439/ is-there-a-matplotlib-equivalent-of-matlabs-datacursormode/4674445 """ diff --git a/gui/wxpython/vdigit/dialogs.py b/gui/wxpython/vdigit/dialogs.py index 494f8f66acc..369855c94fc 100644 --- a/gui/wxpython/vdigit/dialogs.py +++ b/gui/wxpython/vdigit/dialogs.py @@ -75,9 +75,7 @@ def __init__( for layer in cats[line].keys(): self.cats[line][layer] = list(cats[line][layer]) - layers = [] - for layer in self.digit.GetLayers(): - layers.append(str(layer)) + layers = [str(layer) for layer in self.digit.GetLayers()] # make copy of cats (used for 'reload') self.cats_orig = copy.deepcopy(self.cats) @@ -114,9 +112,7 @@ def __init__( self.fidText.SetLabel(str(self.fid)) else: self.fidText.Show(False) - choices = [] - for fid in self.cats.keys(): - choices.append(str(fid)) + choices = [str(fid) for fid in self.cats.keys()] self.fidMulti.SetItems(choices) self.fidMulti.SetSelection(0) @@ -256,7 +252,7 @@ def OnEndEdit(self, event): self.cats[self.fid][layerNew] = [] self.cats[self.fid][layerNew].append(catNew) self.cats[self.fid][layerOld].remove(catOld) - except: + except (KeyError, ValueError, AttributeError): event.Veto() self.list.SetItem(itemIndex, 0, str(layerNew)) self.list.SetItem(itemIndex, 1, str(catNew)) @@ -546,9 +542,7 @@ def UpdateDialog(self, query=None, cats=None): else: self.fidText.Show(False) self.fidMulti.Show(True) - choices = [] - for fid in self.cats.keys(): - choices.append(str(fid)) + choices = [str(fid) for fid in self.cats.keys()] self.fidMulti.SetItems(choices) self.fidMulti.SetSelection(0) diff --git a/gui/wxpython/vdigit/g.gui.vdigit.html b/gui/wxpython/vdigit/g.gui.vdigit.html index d02064b997f..766c0209270 100644 --- a/gui/wxpython/vdigit/g.gui.vdigit.html +++ b/gui/wxpython/vdigit/g.gui.vdigit.html @@ -161,7 +161,7 @@

    DIGITIZER TOOLBAR

  • Break selected lines/boundaries at intersection
    Split given vector line or boundary into two lines on given position (based on v.clean, - tool=break).
  • + tool=break).
  • Connect two selected lines/boundaries
    Connect selected lines or boundaries, the first given line is connected to the diff --git a/gui/wxpython/vdigit/main.py b/gui/wxpython/vdigit/main.py index 852cb4d1ada..c98c496b177 100644 --- a/gui/wxpython/vdigit/main.py +++ b/gui/wxpython/vdigit/main.py @@ -15,7 +15,7 @@ """ try: - from vdigit.wxdigit import IVDigit, GV_LINES, CFUNCTYPE # noqa: F401 + from vdigit.wxdigit import IVDigit, GV_LINES # noqa: F401 haveVDigit = True errorMsg = "" diff --git a/gui/wxpython/vdigit/mapwindow.py b/gui/wxpython/vdigit/mapwindow.py index 824ff68fc3e..81421027feb 100644 --- a/gui/wxpython/vdigit/mapwindow.py +++ b/gui/wxpython/vdigit/mapwindow.py @@ -290,7 +290,7 @@ def OnLeftDownAddLine(self, event): """Left mouse button pressed - add new feature""" try: mapLayer = self.toolbar.GetLayer().GetName() - except: + except AttributeError: return if self.toolbar.GetAction("type") in {"point", "centroid"}: @@ -394,40 +394,37 @@ def _geomAttrbUpdate(self, fids): return dbInfo = gselect.VectorDBInfo(vectorName) - sqlfile = tempfile.NamedTemporaryFile(mode="w") - for fid in fids: - for layer, cats in self.digit.GetLineCats(fid).items(): - table = dbInfo.GetTable(layer) - for attrb, item in vdigit["geomAttr"].items(): - val = -1 - if attrb == "length": - val = self.digit.GetLineLength(fid) - type = attrb - elif attrb == "area": - val = self.digit.GetAreaSize(fid) - type = attrb - elif attrb == "perimeter": - val = self.digit.GetAreaPerimeter(fid) - type = "length" - - if val < 0: - continue - val = UnitsConvertValue(val, type, item["units"]) - - for cat in cats: - sqlfile.write( - "UPDATE %s SET %s = %f WHERE %s = %d;\n" - % ( - table, - item["column"], - val, - dbInfo.GetKeyColumn(layer), - cat, + with tempfile.NamedTemporaryFile(mode="w") as sqlfile: + for fid in fids: + for layer, cats in self.digit.GetLineCats(fid).items(): + table = dbInfo.GetTable(layer) + for attrb, item in vdigit["geomAttr"].items(): + val = -1 + if attrb == "length": + val = self.digit.GetLineLength(fid) + type = attrb + elif attrb == "area": + val = self.digit.GetAreaSize(fid) + type = attrb + elif attrb == "perimeter": + val = self.digit.GetAreaPerimeter(fid) + type = "length" + if val < 0: + continue + val = UnitsConvertValue(val, type, item["units"]) + for cat in cats: + sqlfile.write( + "UPDATE %s SET %s = %f WHERE %s = %d;\n" + % ( + table, + item["column"], + val, + dbInfo.GetKeyColumn(layer), + cat, + ) ) - ) - - sqlfile.file.flush() - RunCommand("db.execute", parent=True, quiet=True, input=sqlfile.name) + sqlfile.file.flush() + RunCommand("db.execute", parent=True, quiet=True, input=sqlfile.name) def _updateATM(self): """Update open Attribute Table Manager @@ -482,7 +479,7 @@ def OnLeftDownDisplayCA(self, event): """ try: mapLayer = self.toolbar.GetLayer().GetName() - except: + except AttributeError: return coords = self.Pixel2Cell(self.mouse["begin"]) @@ -624,8 +621,7 @@ def OnLeftDownUndo(self, event): removed, ], ) - # self.mouse['begin'] = self.Cell2Pixel(self.polycoords[-1]) - except: + except IndexError: pass if action == "editLine": @@ -690,8 +686,8 @@ def OnLeftDownUndo(self, event): def _onLeftDown(self, event): """Left mouse button donw - vector digitizer various actions""" try: - mapLayer = self.toolbar.GetLayer().GetName() - except: + self.toolbar.GetLayer().GetName() + except AttributeError: GMessage(parent=self, message=_("No vector map selected for editing.")) event.Skip() return @@ -1095,7 +1091,7 @@ def _onRightUp(self, event): # -> add new line / boundary try: mapName = self.toolbar.GetLayer().GetName() - except: + except AttributeError: mapName = None GError(parent=self, message=_("No vector map selected for editing.")) diff --git a/gui/wxpython/vdigit/preferences.py b/gui/wxpython/vdigit/preferences.py index 8f7b46644c8..6174a4d6680 100644 --- a/gui/wxpython/vdigit/preferences.py +++ b/gui/wxpython/vdigit/preferences.py @@ -199,7 +199,7 @@ def _createGeneralPage(self, notebook): self.snappingUnit.SetSelection( UserSettings.Get(group="vdigit", key="snapping", subkey="unit") ) - except: + except KeyError: self.snappingUnit.SetSelection(0) self.snappingUnit.Bind(wx.EVT_CHOICE, self.OnChangeSnappingUnits) flexSizer.Add(text, proportion=0, flag=wx.ALIGN_CENTER_VERTICAL) @@ -525,7 +525,6 @@ def _createAttributesPage(self, notebook): # settings flexSizer = wx.FlexGridSizer(cols=2, hgap=3, vgap=3) flexSizer.AddGrowableCol(0) - settings = ((_("Layer"), 1), (_("Category"), 1), (_("Mode"), _("Next to use"))) # layer text = StaticText(parent=panel, id=wx.ID_ANY, label=_("Layer")) self.layer = SpinCtrl(parent=panel, id=wx.ID_ANY, min=1, max=1e3) diff --git a/gui/wxpython/vdigit/toolbars.py b/gui/wxpython/vdigit/toolbars.py index 73bca461227..e3fc9657a51 100644 --- a/gui/wxpython/vdigit/toolbars.py +++ b/gui/wxpython/vdigit/toolbars.py @@ -705,7 +705,6 @@ def OnHelp(self, event): def OnAdditionalToolMenu(self, event): """Menu for additional tools""" - point = wx.GetMousePosition() toolMenu = Menu() for label, itype, handler, desc in ( @@ -1216,11 +1215,7 @@ def StopEditing(self): dlg.Destroy() self.parent.SetStatusText( - _( - "Please wait, " - "closing and rebuilding topology of " - "vector map <%s>..." - ) + _("Please wait, closing and rebuilding topology of vector map <%s>...") % self.mapLayer.GetName(), 0, ) diff --git a/gui/wxpython/vdigit/wxdigit.py b/gui/wxpython/vdigit/wxdigit.py index c1f777ec647..8f486e27005 100644 --- a/gui/wxpython/vdigit/wxdigit.py +++ b/gui/wxpython/vdigit/wxdigit.py @@ -26,22 +26,152 @@ @author Martin Landa """ -import grass.script.core as grass - -from grass.pydispatch.signal import Signal +import os +import sys +from ctypes import byref, c_double, c_int, create_string_buffer, pointer -from core.gcmd import GError +import grass.script.core as grass from core.debug import Debug +from core.gcmd import GError from core.settings import UserSettings -from vdigit.wxdisplay import DisplayDriver, GetLastError try: - from grass.lib.gis import * - from grass.lib.vector import * - from grass.lib.vedit import * - from grass.lib.dbmi import * + from grass.lib.dbmi import ( + DB_C_TYPE_STRING, + DB_NEXT, + DB_OK, + DB_SEQUENTIAL, + db_append_string, + db_close_database_shutdown_driver, + db_convert_column_value_to_string, + db_execute_immediate, + db_fetch, + db_get_column_name, + db_get_column_sqltype, + db_get_column_value, + db_get_cursor_table, + db_get_string, + db_get_table_column, + db_get_table_number_of_columns, + db_init_handle, + db_init_string, + db_open_database, + db_open_select_cursor, + db_set_handle, + db_set_string, + db_shutdown_driver, + db_sqltype_to_Ctype, + db_start_driver, + db_test_value_isnull, + dbCursor, + dbHandle, + dbString, + ) + from grass.lib.gis import ( + GMAPSET_MAX, + GNAME_MAX, + G_find_vector2, + G_free, + G_name_is_fully_qualified, + ) + from grass.lib.vector import ( + GV_AREA, + GV_BOUNDARY, + GV_CENTROID, + GV_LINE, + GV_LINES, + GV_POINT, + GV_POINTS, + GV_TOPO_PSEUDO, + WITHOUT_Z, + Map_info, + Vect_append_point, + Vect_area_alive, + Vect_get_area_perimeter, + Vect_break_lines_list, + Vect_cat_set, + Vect_cidx_get_cat_by_index, + Vect_cidx_get_field_number, + Vect_cidx_get_num_cats_by_index, + Vect_cidx_get_num_fields, + Vect_close, + Vect_delete_line, + Vect_destroy_boxlist, + Vect_destroy_cats_struct, + Vect_destroy_line_struct, + Vect_destroy_list, + Vect_field_cat_del, + Vect_get_area_area, + Vect_get_area_boundaries, + Vect_get_area_centroid, + Vect_get_area_points, + Vect_get_centroid_area, + Vect_get_dblink, + Vect_get_field, + Vect_get_finfo_geometry_type, + Vect_get_finfo_topology_info, + Vect_get_line_areas, + Vect_get_line_box, + Vect_get_line_type, + Vect_get_mapset, + Vect_get_name, + Vect_get_num_dblinks, + Vect_get_num_lines, + Vect_get_num_updated_lines, + Vect_get_point_in_area, + Vect_get_updated_line, + Vect_get_updated_line_offset, + Vect_is_3d, + Vect_line_alive, + Vect_line_check_intersection, + Vect_line_length, + Vect_list_append, + Vect_new_boxlist, + Vect_new_cats_struct, + Vect_new_line_struct, + Vect_new_list, + Vect_open_old, + Vect_points_distance, + Vect_read_line, + Vect_reset_cats, + Vect_reset_line, + Vect_reset_updated, + Vect_restore_line, + Vect_rewrite_line, + Vect_select_lines_by_box, + Vect_select_lines_by_polygon, + Vect_set_updated, + Vect_snap_lines_list, + Vect_write_line, + bound_box, + ) + from grass.lib.vedit import ( + NO_SNAP, + SNAP, + SNAPVERTEX, + Vedit_add_vertex, + Vedit_bulk_labeling, + Vedit_chtype_lines, + Vedit_connect_lines, + Vedit_copy_lines, + Vedit_delete_area_centroid, + Vedit_delete_lines, + Vedit_flip_lines, + Vedit_merge_lines, + Vedit_move_lines, + Vedit_move_vertex, + Vedit_remove_vertex, + Vedit_select_by_query, + Vedit_snap_line, + Vedit_split_lines, + QUERY_UNKNOWN, + QUERY_LENGTH, + QUERY_DANGLE, + ) except (ImportError, OSError, TypeError) as e: print("wxdigit.py: {}".format(e), file=sys.stderr) +from grass.pydispatch.signal import Signal +from vdigit.wxdisplay import DisplayDriver, GetLastError class VDigitError: @@ -339,9 +469,6 @@ def _breakLineAtIntersection(self, line, pointsLine): if Vect_read_line(self.poMapInfo, self.poPoints, None, line) < 0: self._error.ReadLine(line) return -1 - points = self.poPoints - else: - points = pointsLine listLine = Vect_new_boxlist(0) listRef = Vect_new_list() @@ -692,8 +819,7 @@ def _getaAreaBboxCats(self, area): :param area: area id :return: list of categories :func:`_getLineAreasCategories` and - list of bboxes :func:`_getBbox` of area boundary - features + list of bboxes :func:`_getBbox` of area boundary features """ po_b_list = Vect_new_list() Vect_get_area_boundaries(self.poMapInfo, area, po_b_list) @@ -760,7 +886,7 @@ def _getCategories(self, ln_id): :return: None feature does not exist """ if not Vect_line_alive(self.poMapInfo, ln_id): - return none + return None poCats = Vect_new_cats_struct() if Vect_read_line(self.poMapInfo, None, poCats, ln_id) < 0: @@ -826,18 +952,13 @@ def _createBbox(self, points): return bbox def _convertGeom(self, poPoints): - """Helper function convert geom from ctypes line_pts to python - list + """Helper function convert geom from ctypes line_pts to Python list :return: coords in python list [(x, y),...] """ Points = poPoints.contents - pts_geom = [] - for j in range(Points.n_points): - pts_geom.append((Points.x[j], Points.y[j])) - - return pts_geom + return [(Points.x[j], Points.y[j]) for j in range(Points.n_points)] def MoveSelectedLines(self, move): """Move selected features @@ -895,7 +1016,7 @@ def MoveSelectedLines(self, move): if nlines > 0 and self._settings["breakLines"]: for i in range(1, nlines): - self._breakLineAtIntersection(nlines + i, None, changeset) + self._breakLineAtIntersection(nlines + i, None) if nlines > 0: self._addChangeset() @@ -1095,7 +1216,7 @@ def EditLine(self, line, coords): self.poMapInfo, line, ltype, self.poPoints, self.poCats ) if newline > 0 and self.emit_signals: - new_geom = [self._getBbox(newline)] + new_bboxs = [self._getBbox(newline)] new_areas_cats = [self._getLineAreasCategories(newline)] if newline > 0 and self._settings["breakLines"]: @@ -1124,8 +1245,6 @@ def FlipLine(self): if not self._checkMap(): return -1 - nlines = Vect_get_num_lines(self.poMapInfo) - poList = self._display.GetSelectedIList() ret = Vedit_flip_lines(self.poMapInfo, poList) Vect_destroy_list(poList) @@ -1498,7 +1617,7 @@ def GetLineLength(self, line): ltype = Vect_read_line(self.poMapInfo, self.poPoints, None, line) if ltype < 0: self._error.ReadLine(line) - return ret + return -1 length = -1 if ltype & GV_LINES: # lines & boundaries @@ -1519,8 +1638,8 @@ def GetAreaSize(self, centroid): ltype = Vect_read_line(self.poMapInfo, None, None, centroid) if ltype < 0: - self._error.ReadLine(line) - return ret + self._error.ReadLine(centroid) + return -1 if ltype != GV_CENTROID: return -1 @@ -1548,8 +1667,8 @@ def GetAreaPerimeter(self, centroid): ltype = Vect_read_line(self.poMapInfo, None, None, centroid) if ltype < 0: - self._error.ReadLine(line) - return ret + self._error.ReadLine(centroid) + return -1 if ltype != GV_CENTROID: return -1 @@ -1561,7 +1680,7 @@ def GetAreaPerimeter(self, centroid): return -1 Vect_get_area_points(self.poMapInfo, area, self.poPoints) - perimeter = Vect_area_perimeter(self.poPoints) + perimeter = Vect_get_area_perimeter(self.poPoints) return perimeter @@ -1955,7 +2074,7 @@ def _addFeature(self, ftype, coords, layer, cat, snap, threshold): ) if newc < 0: self._error.WriteLine() - return len(fids, fids) + return len(fids), fids fids.append(newc) Vect_destroy_line_struct(bpoints) diff --git a/gui/wxpython/vdigit/wxdisplay.py b/gui/wxpython/vdigit/wxdisplay.py index 39662afb19f..f7381fe763f 100644 --- a/gui/wxpython/vdigit/wxdisplay.py +++ b/gui/wxpython/vdigit/wxdisplay.py @@ -18,22 +18,106 @@ """ import locale - import os import sys -import wx +from ctypes import CFUNCTYPE, byref, c_double, c_int, pointer +from grass.lib.ctypes_preamble import UNCHECKED, String +import wx from core.debug import Debug -from core.settings import UserSettings from core.gcmd import DecodeString -from gui_core.wrap import Rect +from core.settings import UserSettings try: - from grass.lib.gis import * - from grass.lib.vector import * - from grass.lib.vedit import * + from grass.lib.gis import ( + G_gisinit, + G_set_error_routine, + G_set_percent_routine, + G_unset_error_routine, + G_unset_percent_routine, + ) + from grass.lib.vector import ( # Types; Functions; Classes + GV_BOUNDARY, + GV_BUILD_NONE, + GV_CENTROID, + GV_LINE, + GV_LINES, + GV_MODE_RW, + GV_POINT, + GV_POINTS, + WITHOUT_Z, + Map_info, + Vect_append_point, + Vect_box_copy, + Vect_box_extend, + Vect_build, + Vect_build_partial, + Vect_close, + Vect_destroy_cats_struct, + Vect_destroy_line_struct, + Vect_destroy_list, + Vect_find_line_list, + Vect_get_area_box, + Vect_get_area_centroid, + Vect_get_centroid_area, + Vect_get_line_box, + Vect_get_map_box, + Vect_get_num_areas, + Vect_get_num_lines, + Vect_is_3d, + Vect_line_alive, + Vect_line_check_duplicate, + Vect_line_distance, + Vect_list_append, + Vect_new_cats_struct, + Vect_new_line_struct, + Vect_new_list, + Vect_open_old, + Vect_open_tmp_old, + Vect_open_tmp_update, + Vect_open_update, + Vect_point_in_area, + Vect_point_in_poly, + Vect_points_distance, + Vect_read_line, + Vect_select_lines_by_polygon, + Vect_set_updated, + bound_box, + PORT_DOUBLE_MAX, + ) + from grass.lib.vedit import ( # Types; Draw flags; Functions + DRAW_AREA, + DRAW_BOUNDARYNO, + DRAW_BOUNDARYONE, + DRAW_BOUNDARYTWO, + DRAW_CENTROIDDUP, + DRAW_CENTROIDIN, + DRAW_CENTROIDOUT, + DRAW_DIRECTION, + DRAW_LINE, + DRAW_NODEONE, + DRAW_NODETWO, + DRAW_POINT, + DRAW_VERTEX, + TYPE_AREA, + TYPE_BOUNDARYNO, + TYPE_BOUNDARYONE, + TYPE_BOUNDARYTWO, + TYPE_CENTROIDDUP, + TYPE_CENTROIDIN, + TYPE_CENTROIDOUT, + TYPE_DIRECTION, + TYPE_ISLE, + TYPE_LINE, + TYPE_NODEONE, + TYPE_NODETWO, + TYPE_POINT, + TYPE_VERTEX, + Vedit_render_map, + ) except (ImportError, OSError, TypeError) as e: - print("wxdigit.py: {}".format(e), file=sys.stderr) + print("wxdisplay.py: {}".format(e), file=sys.stderr) +from gui_core.wrap import Rect log = None progress = None @@ -373,6 +457,8 @@ def _definePen(self, rtype): :return: pen, brush """ + + key = None if rtype == TYPE_POINT: key = "point" elif rtype == TYPE_LINE: @@ -561,7 +647,7 @@ def _validLine(self, line) -> bool: :return: True valid feature id :return: False invalid """ - return bool(line > 0 and line <= Vect_get_num_lines(self.poMapInfo)) + return bool(0 < line <= Vect_get_num_lines(self.poMapInfo)) def SelectLinesByBox(self, bbox, ltype=None, drawSeg=False, poMapInfo=None): """Select vector objects by given bounding box @@ -712,7 +798,7 @@ def SelectLineByPoint(self, point, ltype=None, poMapInfo=None): pz = c_double() if not self._validLine(lineNearest): return {"line": -1, "point": None} - ftype = Vect_read_line(poMapInfo, self.poPoints, self.poCats, lineNearest) + Vect_read_line(poMapInfo, self.poPoints, self.poCats, lineNearest) Vect_line_distance( self.poPoints, point[0], @@ -856,7 +942,7 @@ def GetSelectedVertex(self, pos): if not self._validLine(line): return -1 - ftype = Vect_read_line(self.poMapInfo, self.poPoints, self.poCats, line) + Vect_read_line(self.poMapInfo, self.poPoints, self.poCats, line) minDist = 0.0 Gid = -1 @@ -911,7 +997,7 @@ def GetRegionSelected(self): for line in self.selected["ids"]: area = Vect_get_centroid_area(self.poMapInfo, line) - if area > 0 and area <= nareas: + if 0 < area <= nareas: if not Vect_get_area_box(self.poMapInfo, area, byref(lineBox)): continue else: # noqa: PLR5501 diff --git a/gui/wxpython/vnet/dialogs.py b/gui/wxpython/vnet/dialogs.py index 2979429a572..ca928a6b164 100644 --- a/gui/wxpython/vnet/dialogs.py +++ b/gui/wxpython/vnet/dialogs.py @@ -581,13 +581,10 @@ def _updateResultDbMgrPage(self): def OnPageChanged(self, event): """Tab switched""" if event.GetEventObject() == self.notebook: - dbMgrIndxs = [] - dbMgrIndxs.extend( - ( - self.notebook.GetPageIndexByName("inputDbMgr"), - self.notebook.GetPageIndexByName("resultDbMgr"), - ) - ) + dbMgrIndxs = [ + self.notebook.GetPageIndexByName("inputDbMgr"), + self.notebook.GetPageIndexByName("resultDbMgr"), + ] if self.notebook.GetSelection() in dbMgrIndxs: self.stBar.AddStatusItem( text=_("Loading tables..."), @@ -834,9 +831,7 @@ def OnNLayerSel(self, event): self._setInputData() def _setInputData(self): - params = {} - for k, v in self.inputData.items(): - params[k] = v.GetValue() + params = {k: v.GetValue() for k, v in self.inputData.items()} flags = {} self.vnet_mgr.SetParams(params, flags) @@ -1218,7 +1213,7 @@ def OnItemSelected(self, event): self.pts_data.SetSelected(self.selectedkey) def OnCheckItem(self, index, flag): - "flag is True if the item was checked, False if unchecked" + """flag is True if the item was checked, False if unchecked""" key = self.GetItemData(index) if self.pts_data.GetPointData(key)["use"] != flag: self.pts_data.SetPointData(key, {"use": flag}) @@ -1258,11 +1253,13 @@ def __init__( rules = RunCommand("v.colors", read=True, flags="l") - settsLabels = {} - - settsLabels["color_table"] = StaticText( - parent=self, id=wx.ID_ANY, label=_("Color table style %s:") % "(v.net.flow)" - ) + settsLabels = { + "color_table": StaticText( + parent=self, + id=wx.ID_ANY, + label=_("Color table style %s:") % "(v.net.flow)", + ) + } self.settings["color_table"] = ComboBox( parent=self, id=wx.ID_ANY, @@ -1651,11 +1648,7 @@ def InputSel(self): self._updateInputDbMgrPage(show=True) def GetData(self): - params = {} - for param, sel in self.inputData.items(): - params[param] = sel.GetValue() - - return params + return {param: sel.GetValue() for param, sel in self.inputData.items()} class OutputVectorDialog(wx.Dialog): diff --git a/gui/wxpython/vnet/toolbars.py b/gui/wxpython/vnet/toolbars.py index d31313091fe..e3e83346e1d 100644 --- a/gui/wxpython/vnet/toolbars.py +++ b/gui/wxpython/vnet/toolbars.py @@ -208,10 +208,10 @@ def __init__(self, parent, vnet_mgr): self.vnet_mgr = vnet_mgr self.InitToolbar(self._toolbarData()) - choices = [] - - for moduleName in self.vnet_mgr.GetAnalyses(): - choices.append(self.vnet_mgr.GetAnalysisProperties(moduleName)["label"]) + choices = [ + self.vnet_mgr.GetAnalysisProperties(moduleName)["label"] + for moduleName in self.vnet_mgr.GetAnalyses() + ] self.anChoice = ComboBox( parent=self, @@ -235,5 +235,4 @@ def __init__(self, parent, vnet_mgr): self.Realize() def _toolbarData(self): - return self._getToolbarData(()) diff --git a/gui/wxpython/vnet/vnet_core.py b/gui/wxpython/vnet/vnet_core.py index b0ec072b2af..cb1df07441d 100644 --- a/gui/wxpython/vnet/vnet_core.py +++ b/gui/wxpython/vnet/vnet_core.py @@ -20,6 +20,8 @@ """ import math +from pathlib import Path + from grass.script.utils import try_remove from grass.script import core as grass from grass.script.task import cmdlist_to_tuple @@ -419,9 +421,11 @@ def _vnetPathRunAn(self, analysis, output, params, flags, catPts): cats = self.data.GetAnalysisProperties()["cmdParams"]["cats"] # Creates part of cmd fro analysis - cmdParams = [analysis] - cmdParams.extend(self._setInputParams(analysis, params, flags)) - cmdParams.append("output=" + output) + cmdParams = [ + analysis, + *self._setInputParams(analysis, params, flags), + "output=" + output, + ] cmdPts = [] for cat in cats: @@ -447,9 +451,7 @@ def _vnetPathRunAn(self, analysis, output, params, flags, catPts): ) self.coordsTmpFile = grass.tempfile() - coordsTmpFileOpened = open(self.coordsTmpFile, "w") - coordsTmpFileOpened.write(inpPoints) - coordsTmpFileOpened.close() + Path(self.coordsTmpFile).write_text(inpPoints) if flags["t"]: cmdParams.append("-t") @@ -513,9 +515,11 @@ def _onDone(self, event): def _runTurnsAn(self, analysis, output, params, flags, catPts): # Creates part of cmd fro analysis - cmdParams = [analysis] - cmdParams.extend(self._setInputParams(analysis, params, flags)) - cmdParams.append("output=" + output) + cmdParams = [ + analysis, + *self._setInputParams(analysis, params, flags), + "output=" + output, + ] cats = {} for cat_name, pts_coor in catPts.items(): @@ -575,28 +579,26 @@ def _updateTtbByGlobalCosts(self, vectMapName, tlayer): driver, database = dbInfo.GetDbSettings(tlayer) sqlFile = grass.tempfile() - sqlFile_f = open(sqlFile, "w") - - for ival in intervals: - from_angle = ival[0] - to_angle = ival[1] - cost = ival[2] - - if to_angle < from_angle: - to_angle = math.pi * 2 + to_angle - # if angle < from_angle: - # angle = math.pi * 2 + angle - - where = ( - " WHERE (((angle < {0}) AND ({2} + angle >= {0} AND {2} + angle < {1}))" - " OR ((angle >= {0}) AND (angle >= {0} AND angle < {1})))" - " AND cost==0.0 " - ).format(str(from_angle), str(to_angle), str(math.pi * 2)) - - stm = ("UPDATE %s SET cost=%f " % (table, cost)) + where + ";\n" - sqlFile_f.write(stm) - - sqlFile_f.close() + with open(sqlFile, "w") as sqlFile_f: + for ival in intervals: + from_angle = ival[0] + to_angle = ival[1] + cost = ival[2] + + if to_angle < from_angle: + to_angle = math.pi * 2 + to_angle + # if angle < from_angle: + # angle = math.pi * 2 + angle + + where = ( + " WHERE" + " (((angle < {0}) AND ({2} + angle >= {0} AND {2} + angle < {1}))" + " OR ((angle >= {0}) AND (angle >= {0} AND angle < {1})))" + " AND cost==0.0 " + ).format(str(from_angle), str(to_angle), str(math.pi * 2)) + + stm = ("UPDATE %s SET cost=%f " % (table, cost)) + where + ";\n" + sqlFile_f.write(stm) # TODO improve parser and run in thread @@ -622,9 +624,11 @@ def _runAn(self, analysis, output, params, flags, catPts): """Called for all v.net.* analysis (except v.net.path)""" # Creates part of cmd fro analysis - cmdParams = [analysis] - cmdParams.extend(self._setInputParams(analysis, params, flags)) - cmdParams.append("output=" + output) + cmdParams = [ + analysis, + *self._setInputParams(analysis, params, flags), + "output=" + output, + ] cats = self.data.GetAnalysisProperties()["cmdParams"]["cats"] @@ -665,9 +669,7 @@ def _runAn(self, analysis, output, params, flags, catPts): # TODO better tmp files cleanup (make class for managing tmp files) self.tmpPtsAsciiFile = grass.tempfile() - tmpPtsAsciiFileOpened = open(self.tmpPtsAsciiFile, "w") - tmpPtsAsciiFileOpened.write(pt_ascii) - tmpPtsAsciiFileOpened.close() + Path(self.tmpPtsAsciiFile).write_text(pt_ascii) self.tmpInPts = AddTmpMapAnalysisMsg("vnet_tmp_in_pts", self.tmp_maps) if not self.tmpInPts: @@ -765,9 +767,7 @@ def _getPtByCat(self, analysis): anProps = self.data.GetAnalysisProperties() cats = anProps["cmdParams"]["cats"] - ptByCats = {} - for cat in anProps["cmdParams"]["cats"]: - ptByCats[cat[0]] = [] + ptByCats = {cat[0]: [] for cat in anProps["cmdParams"]["cats"]} for i in range(self.pts_data.GetPointsCount()): pt_data = self.pts_data.GetPointData(i) @@ -893,13 +893,13 @@ def _updateHistStepData(self, histStepData): ptDataHist = histStepData["points"]["pt" + str(iPt)] e, n = ptDataHist["coords"] - pt_data = {"e": e, "n": n} - - pt_data["type"] = int(ptDataHist["catIdx"]) - - pt_data["topology"] = ptDataHist["topology"] - - pt_data["use"] = ptDataHist["checked"] + pt_data = { + "e": e, + "n": n, + "type": int(ptDataHist["catIdx"]), + "topology": ptDataHist["topology"], + "use": ptDataHist["checked"], + } pts.append(pt_data) diff --git a/gui/wxpython/vnet/vnet_data.py b/gui/wxpython/vnet/vnet_data.py index 47ffdb9a371..e8e05839e70 100644 --- a/gui/wxpython/vnet/vnet_data.py +++ b/gui/wxpython/vnet/vnet_data.py @@ -427,11 +427,7 @@ def _ptDataToList(self, pt_data): return pt_list_data def _ptListDataToPtData(self, pt_list_data): - pt_data = {} - for i, val in enumerate(pt_list_data): - pt_data[self.cols["name"][i]] = val - - return pt_data + return {self.cols["name"][i]: val for i, val in enumerate(pt_list_data)} def _usePoint(self, pt_id, use): """Item is checked/unchecked""" @@ -557,8 +553,7 @@ def OnMapClickHandler(self, event): def GetColumns(self, only_relevant=True): cols_data = deepcopy(self.cols) - hidden_cols = [] - hidden_cols.extend((self.cols["name"].index("e"), self.cols["name"].index("n"))) + hidden_cols = [self.cols["name"].index("e"), self.cols["name"].index("n")] analysis, valid = self.an_params.GetParam("analysis") if only_relevant and len(self.an_data[analysis]["cmdParams"]["cats"]) <= 1: @@ -1149,15 +1144,11 @@ def SaveHistStep(self): self.currHistStep = 0 newHistFile = grass.tempfile() - newHist = open(newHistFile, "w") - - self._saveNewHistStep(newHist) - - oldHist = open(self.histFile) - removedHistData = self._savePreviousHist(newHist, oldHist) + with open(newHistFile, "w") as newHist: + self._saveNewHistStep(newHist) + with open(self.histFile) as oldHist: + removedHistData = self._savePreviousHist(newHist, oldHist) - oldHist.close() - newHist.close() try_remove(self.histFile) self.histFile = newHistFile @@ -1280,27 +1271,25 @@ def _castValue(self, value): def _getHistStepData(self, histStep): """Load data saved in history step""" - hist = open(self.histFile) histStepData = {} - newHistStep = False isSearchedHistStep = False - for line in hist: - if not line.strip() and isSearchedHistStep: - break - if not line.strip(): - newHistStep = True - continue - if isSearchedHistStep: - self._parseLine(line, histStepData) + with open(self.histFile) as hist: + for line in hist: + if not line.strip() and isSearchedHistStep: + break + if not line.strip(): + newHistStep = True + continue + if isSearchedHistStep: + self._parseLine(line, histStepData) - if newHistStep: - line = line.split("=") - if int(line[1]) == histStep: - isSearchedHistStep = True - newHistStep = False + if newHistStep: + line = line.split("=") + if int(line[1]) == histStep: + isSearchedHistStep = True + newHistStep = False - hist.close() return histStepData def _parseLine(self, line, histStepData): @@ -1347,11 +1336,7 @@ def __init__(self): ] def GetData(self): - data = [] - for ival in self.turn_data: - data.append(ival[1:]) - - return data + return [ival[1:] for ival in self.turn_data] def GetValue(self, line, col): return self.turn_data[line][col] @@ -1450,4 +1435,4 @@ def IsInInterval(self, from_angle, to_angle, angle) -> bool: if angle < from_angle: angle = math.pi * 2 + angle - return bool(angle > from_angle and angle < to_angle) + return bool(from_angle < angle < to_angle) diff --git a/gui/wxpython/vnet/widgets.py b/gui/wxpython/vnet/widgets.py index d997b9c2434..d5a0964a5ac 100644 --- a/gui/wxpython/vnet/widgets.py +++ b/gui/wxpython/vnet/widgets.py @@ -439,21 +439,15 @@ def OnItemSelected(self, event): def getSmallUpArrowImage(self): """Get arrow up symbol for indication of sorting""" - stream = open(os.path.join(globalvar.IMGDIR, "small_up_arrow.png"), "rb") - try: - img = wx.Image(stream) - finally: - stream.close() - return img + with open(os.path.join(globalvar.IMGDIR, "small_up_arrow.png"), "rb") as stream: + return wx.Image(stream) def getSmallDnArrowImage(self): """Get arrow down symbol for indication of sorting""" - stream = open(os.path.join(globalvar.IMGDIR, "small_down_arrow.png"), "rb") - try: - img = wx.Image(stream) - finally: - stream.close() - return img + with open( + os.path.join(globalvar.IMGDIR, "small_down_arrow.png"), "rb" + ) as stream: + return wx.Image(stream) def _getColumnNum(self, colName): """Get position of column among showed columns @@ -511,7 +505,7 @@ def ShowColumn(self, colName, pos): :return: True if column was shown :return: False if position is not valid or column is not hidden """ - if pos < 0 and pos >= self.self.GetColumnCount(): + if pos < 0 or pos >= self.GetColumnCount(): return False if colName in self.hiddenCols: col = self.hiddenCols[colName] diff --git a/gui/wxpython/web_services/cap_interface.py b/gui/wxpython/web_services/cap_interface.py index 38cc7c3c72f..91b2b396dfd 100644 --- a/gui/wxpython/web_services/cap_interface.py +++ b/gui/wxpython/web_services/cap_interface.py @@ -109,11 +109,7 @@ def GetFormats(self): get_map_node = request_node.find(self.xml_ns.Ns("GetMap")) format_nodes = get_map_node.findall(self.xml_ns.Ns("Format")) - formats = [] - for node in format_nodes: - formats.append(node.text) - - return formats + return [node.text for node in format_nodes] class WMSLayer(LayerBase): @@ -265,10 +261,10 @@ def GetLayerData(self, param): return styles if param == "format": - formats = [] - for frmt in self.layer_node.findall(self.xml_ns.NsWmts("Format")): - formats.append(frmt.text.strip()) - return formats + return [ + frmt.text.strip() + for frmt in self.layer_node.findall(self.xml_ns.NsWmts("Format")) + ] if param == "srs": return self.projs diff --git a/gui/wxpython/web_services/dialogs.py b/gui/wxpython/web_services/dialogs.py index bed4f57c70b..309d4b8387c 100644 --- a/gui/wxpython/web_services/dialogs.py +++ b/gui/wxpython/web_services/dialogs.py @@ -336,11 +336,10 @@ def OnClose(self, event): event.Skip() def _getCapFiles(self): - ws_cap_files = {} - for v in self.ws_panels.values(): - ws_cap_files[v["panel"].GetWebService()] = v["panel"].GetCapFile() - - return ws_cap_files + return { + v["panel"].GetWebService(): v["panel"].GetCapFile() + for v in self.ws_panels.values() + } def OnServer(self, event): """Server settings edited""" @@ -416,12 +415,9 @@ def _getConnectedWS(self): :return: list of found web services on server (identified as keys in self.ws_panels) """ - conn_ws = [] - for ws, data in self.ws_panels.items(): - if data["panel"].IsConnected(): - conn_ws.append(ws) - - return conn_ws + return [ + ws for ws, data in self.ws_panels.items() if data["panel"].IsConnected() + ] def UpdateDialogAfterConnection(self): """Update dialog after all web service panels downloaded and parsed @@ -750,10 +746,7 @@ def LoadCapFiles(self, ws_cap_files, cmd): def _getServerConnFromCmd(self, cmd): """Get url/server/password from cmd tuple""" conn = {"url": "", "username": "", "password": ""} - - for k in conn.keys(): - if k in cmd[1]: - conn[k] = cmd[1][k] + conn |= {k: cmd[1][k] for k in conn.keys() if k in cmd[1]} return conn def _apply(self): diff --git a/gui/wxpython/web_services/widgets.py b/gui/wxpython/web_services/widgets.py index db057313831..29a1c99014a 100644 --- a/gui/wxpython/web_services/widgets.py +++ b/gui/wxpython/web_services/widgets.py @@ -514,10 +514,7 @@ def _prepareForNewConn(self, url, username, password): self.conn = {"url": url, "password": password, "username": username} - conn_cmd = [] - for k, v in self.conn.items(): - if v: - conn_cmd.append("%s=%s" % (k, v)) + conn_cmd = ["%s=%s" % (k, v) for k, v in self.conn.items() if v] self.ws_cmdl = self.ws_drvs[self.ws]["cmd"] + conn_cmd diff --git a/gui/wxpython/wxplot/dialogs.py b/gui/wxpython/wxplot/dialogs.py index bae305a1817..d1564d4fc8a 100755 --- a/gui/wxpython/wxplot/dialogs.py +++ b/gui/wxpython/wxplot/dialogs.py @@ -547,8 +547,6 @@ def OnHistMap(self, event): self.gselection.Enable() self.rselection.Disable() self.rselection.SetValue("") - else: - pass def OnRasterSelection(self, event): """Handler for selecting a single raster map""" @@ -986,9 +984,7 @@ def _do_layout(self): gridSizer = wx.GridBagSizer(vgap=5, hgap=5) row = 0 - choicelist = [] - for i in self.rasterList: - choicelist.append(str(i)) + choicelist = [str(i) for i in self.rasterList] self.mapchoice = Choice( parent=self, id=wx.ID_ANY, size=(300, -1), choices=choicelist diff --git a/gui/wxpython/wxplot/profile.py b/gui/wxpython/wxplot/profile.py index e0994f1a8a5..5b63f3cabab 100644 --- a/gui/wxpython/wxplot/profile.py +++ b/gui/wxpython/wxplot/profile.py @@ -446,8 +446,10 @@ def SaveProfileToFile(self, event): dlg.Destroy() return - for datapair in self.raster[r]["datalist"]: - fd.write("%.6f,%.6f\n" % (float(datapair[0]), float(datapair[1]))) + fd.writelines( + "%.6f,%.6f\n" % (float(datapair[0]), float(datapair[1])) + for datapair in self.raster[r]["datalist"] + ) fd.close() diff --git a/imagery/i.aster.toar/i.aster.toar.html b/imagery/i.aster.toar/i.aster.toar.html index 793d0834447..aaaf287e200 100644 --- a/imagery/i.aster.toar/i.aster.toar.html +++ b/imagery/i.aster.toar/i.aster.toar.html @@ -38,7 +38,7 @@

    SEE ALSO

    ASTER sensor data download: -ASTER: Advanced Spaceborne Thermal Emission and Reflection Radiometer +ASTER: Advanced Spaceborne Thermal Emission and Reflection Radiometer

    AUTHOR

    diff --git a/imagery/i.atcorr/create_iwave.py b/imagery/i.atcorr/create_iwave.py index 827b8be54c4..27c1e8e1e42 100644 --- a/imagery/i.atcorr/create_iwave.py +++ b/imagery/i.atcorr/create_iwave.py @@ -26,6 +26,7 @@ filter function short. """ + import os import sys import numpy as np @@ -87,9 +88,9 @@ def interpolate_band(values, step=2.5): wavelengths = values_clean[:, 0] # 1st column of input array responses = values_clean[:, 1] # 2nd column - assert len(wavelengths) == len( - responses - ), "Number of wavelength slots and spectral responses are not equal!" + assert len(wavelengths) == len(responses), ( + "Number of wavelength slots and spectral responses are not equal!" + ) # spectral responses are written out with .4f in pretty_print() # anything smaller than 0.0001 will become 0.0000 -> discard with ... diff --git a/imagery/i.atcorr/i.atcorr.html b/imagery/i.atcorr/i.atcorr.html index bf122963aba..8365c6294cf 100644 --- a/imagery/i.atcorr/i.atcorr.html +++ b/imagery/i.atcorr/i.atcorr.html @@ -849,14 +849,14 @@

    Atmospheric correction of a Sentinel-2 band

    1. Geometrical conditions -

      For Sentinel-2A, the geometrical conditions take the value 25 and for -Sentinel-2B, the geometrical conditions value is 26 (See table A). +

      For Sentinel-2A, the geometrical conditions take the value 25 and for +Sentinel-2B, the geometrical conditions value is 26 (See table A). Our scene comes from the Sentinel-2A mission (the file name begins with S2A_...).

    2. Day, time, longitude and latitude of measurement

      Day and time of the measurement are hidden in the filename (i.e., the -second datum in the file name with format YYYYMMDDTHHMMSS), +second datum in the file name with format YYYYMMDDTHHMMSS), and are also noted in the metadata file, which is included in the downloaded scene (file with .xml extension). Our sample scene was taken on October 28th (20161028) at 15:54:02 (155402). Note @@ -873,20 +873,20 @@

      Atmospheric correction of a Sentinel-2 band

      The longitude and latitude of the centre are stored in ll_clon -and ll_clat. In our case, ll_clon=-78.691 and -ll_clat=35.749. +and ll_clat. In our case, ll_clon=-78.691 and +ll_clat=35.749.

    3. Atmospheric model

      We can choose between various atmospheric models as defined at the -beginning of this manual. For North Carolina, we can choose 2 - -midlatitude summer. +beginning of this manual. For North Carolina, we can choose 2 - +midlatitude summer.

    4. Aerosol model

      We can also choose between various aerosol models as defined at the -beginning of this manual. For North Carolina, we can choose 1 - -continental model. +beginning of this manual. For North Carolina, we can choose 1 - +continental model.

    5. Visibility or Aerosol Optical Depth @@ -909,13 +909,13 @@

      Atmospheric correction of a Sentinel-2 band

      The mean elevation is stored in mean. In our case, -mean=110. In the 6S file it will be displayed in [-km], -i.e., -0.110. +mean=110. In the 6S file it will be displayed in [-km], +i.e., -0.110.

    6. Sensor height

      Since the sensor is on board a satellite, the sensor height will be -set to -1000. +set to -1000.

    7. Sensor band @@ -926,7 +926,7 @@

      Atmospheric correction of a Sentinel-2 band

      Finally, here is what the 6S file would look like for Band 02 of our scene. In order to use it in the i.atcorr module, we can save -it in a text file, for example params_B02.txt. +it in a text file, for example params_B02.txt.

       25
       10 28 15.901 -78.691 35.749
      @@ -947,7 +947,7 @@ 

      Atmospheric correction of a Sentinel-2 band

    8. input = raster band to be processed,
    9. parameters = path to 6S file created in the previous step (we could also enter the values directly),
    10. output = name for the output corrected raster band,
    11. -
    12. range = from 1 to the QUANTIFICATION_VALUE stored in the metadata file. It is 10000 for both Sentinel-2A and Sentinel-2B.
    13. +
    14. range = from 1 to the QUANTIFICATION_VALUE stored in the metadata file. It is 10000 for both Sentinel-2A and Sentinel-2B.
    15. rescale = the output range of values for the corrected bands. This is up to the user to choose, for example: 0-255, 0-1, 1-10000.
    16. If the data is available, the following parameters can be specified @@ -1007,7 +1007,7 @@

      Atmospheric correction of a Landsat-7 band

      If the overpass time is unknown, use the -NASA LaRC Satellite Overpass Predictor. +NASA LaRC Satellite Overpass Predictor.

      Convert digital numbers (DN) to radiance at top-of-atmosphere (TOA)

      @@ -1039,7 +1039,7 @@

      Convert digital numbers (DN) to radiance at top-of-atmosphere (TOA)

      and maximal DN value, and they are reported in the metadata file of each image. High gain or low gain is also reported in the metadata file of each satellite image. For Landsat ETM+, the minimal DN value (QCALMIN) is 1 -(see Landsat handbook, chapter 11), +(see Landsat handbook, chapter 11), and the maximal DN value (QCALMAX) is 255. QCAL is the DN value for every separate pixel in the Landsat image.

      We extract the coefficients and apply them in order to obtain the @@ -1072,7 +1072,7 @@

      Create the parameters file for i.atcorr

      defining geometrical and atmospherical conditions of the satellite overpass. -Here we create a control file icnd_lsat4.txt for band 4 (NIR), +Here we create a control file icnd_lsat4.txt for band 4 (NIR), based on metadata. For the overpass time, we need to define decimal hours: 10:42:07 NC local time = 10.70 decimal hours (decimal minutes: 42 * 100 / 60) which is 15.70 GMT. @@ -1098,7 +1098,7 @@

      Create the parameters file for i.atcorr

      Note that the altitude value from 'icnd_lsat4.txt' file is read at the beginning to compute the initial transform. Therefore, it is necessary to provide a value that might be the mean value of the elevation model -(r.univar elevation). For the atmospheric correction per se, the +(r.univar elevation). For the atmospheric correction per se, the elevation values from the raster map are used.

      Note that the process is computationally intensive. Note also, that i.atcorr reports solar elevation angle above horizon rather @@ -1125,7 +1125,7 @@

      REFERENCES

    17. 6S Manual: PDF1, PDF2, and PDF3
    18. -
    19. RapidEye sensors have been provided by RapidEye AG, Germany
    20. +
    21. RapidEye sensors have been provided by RapidEye AG, Germany
    22. Barsi, J.A., Markham, B.L. and Pedelty, J.A., 2011, The operational land imager: spectral response and spectral uniformity., Proc. SPIE 8153, 81530G; doi:10.1117/12.895438
    23. @@ -1164,10 +1164,10 @@

      AUTHORS

      RapidEye sensors addition 11/2010:
      Peter Löwe, Anne Ghisla -

      VGT1 and VGT2 sensors addition from 6SV-1.1 sources, addition 07/2011: +

      VGT1 and VGT2 sensors addition from 6SV-1.1 sources, addition 07/2011:
      Alfredo Alessandrini, Anne Ghisla -

      Added Landsat 8 from NASA sources, addition 05/2014: +

      Added Landsat 8 from NASA sources, addition 05/2014:
      Nikolaos Ves

      Geoeye1 addition 7/2015: diff --git a/imagery/i.eb.netrad/i.eb.netrad.html b/imagery/i.eb.netrad/i.eb.netrad.html index c6cba3ad484..d4d96b7d966 100644 --- a/imagery/i.eb.netrad/i.eb.netrad.html +++ b/imagery/i.eb.netrad/i.eb.netrad.html @@ -14,7 +14,7 @@

      NOTES

      In the old methods, dT was taken as flat images (dT=5.0), if you don't have a dT map from ground data, you would want to try something in this line, this is to calculate atmospherical energy balance. In the same way, a standard tsw is used -in those equations. Refer to r_net.c for that and for other non-used equations, +in those equations. Refer to r_net.c for that and for other non-used equations, but stored in there for further research convenience.

      TODO

      diff --git a/imagery/i.fft/testsuite/test_i_fft.py b/imagery/i.fft/testsuite/test_i_fft.py new file mode 100644 index 00000000000..b5c86a3c621 --- /dev/null +++ b/imagery/i.fft/testsuite/test_i_fft.py @@ -0,0 +1,216 @@ +import numpy as np +from grass.script import array +from grass.gunittest.case import TestCase +from grass.gunittest.main import test + + +class TestIFFT(TestCase): + """Regression tests for the i.fft GRASS GIS module.""" + + input_raster = "test_input" + real_output = "fft_real" + imag_output = "fft_imag" + + @classmethod + def setUpClass(cls): + """Set up an input raster and configure test environment.""" + cls.use_temp_region() + cls.runModule("g.region", n=10, s=0, e=10, w=0, rows=10, cols=10) + cls.runModule( + "r.mapcalc", expression=f"{cls.input_raster} = col()", overwrite=True + ) + + @classmethod + def tearDownClass(cls): + """Clean up generated data and reset the region.""" + rasters_to_remove = [ + cls.input_raster, + cls.real_output, + cls.imag_output, + "reconstructed", + "input_2", + "combined", + "real_1", + "imag_1", + "real_2", + "imag_2", + "combined_real", + "combined_imag", + ] + cls.runModule( + "g.remove", + type="raster", + name=",".join(rasters_to_remove), + flags="f", + quiet=True, + ) + cls.del_temp_region() + + def test_linearity_property(self): + """Test linearity of the FFT by combining two rasters.""" + self.runModule("r.mapcalc", expression="input_2 = row()", overwrite=True) + self.runModule( + "r.mapcalc", expression="combined = test_input + input_2", overwrite=True + ) + + self.assertModule( + "i.fft", + input=self.input_raster, + real="real_1", + imaginary="imag_1", + overwrite=True, + ) + self.assertRasterExists("real_1") + self.assertRasterExists("imag_1") + + self.assertModule( + "i.fft", input="input_2", real="real_2", imaginary="imag_2", overwrite=True + ) + self.assertRasterExists("real_2") + self.assertRasterExists("imag_2") + + self.assertModule( + "i.fft", + input="combined", + real="combined_real", + imaginary="combined_imag", + overwrite=True, + ) + + self.assertRasterExists("combined_real") + self.assertRasterExists("combined_imag") + + real_combined = array.array("combined_real") + imag_combined = array.array("combined_imag") + real_sum = array.array("real_1") + array.array("real_2") + imag_sum = array.array("imag_1") + array.array("imag_2") + + self.assertTrue( + np.allclose(real_combined, real_sum, atol=1e-5), + "Linearity failed for real component", + ) + self.assertTrue( + np.allclose(imag_combined, imag_sum, atol=1e-5), + "Linearity failed for imaginary component", + ) + + def test_energy_conservation_theorem(self): + """Validate energy conservation theorem.""" + self.assertModule( + "i.fft", + input=self.input_raster, + real=self.real_output, + imaginary=self.imag_output, + overwrite=True, + ) + + self.assertRasterExists(self.real_output) + self.assertRasterExists(self.imag_output) + + spatial_energy = np.sum(array.array(self.input_raster) ** 2) + freq_energy = np.sum( + array.array(self.real_output) ** 2 + array.array(self.imag_output) ** 2 + ) + self.assertAlmostEqual( + spatial_energy, freq_energy, places=5, msg="Energy conservation failed" + ) + + def test_inverse_fft(self): + """Check that inverse FFT reconstructs the original raster.""" + self.assertModule( + "i.fft", + input=self.input_raster, + real=self.real_output, + imaginary=self.imag_output, + overwrite=True, + ) + + self.assertRasterExists(self.real_output) + self.assertRasterExists(self.imag_output) + + self.assertModule( + "i.ifft", + real=self.real_output, + imaginary=self.imag_output, + output="reconstructed", + overwrite=True, + ) + + original_values = array.array(self.input_raster) + reconstructed_values = array.array("reconstructed") + + self.assertTrue( + np.allclose(original_values, reconstructed_values, atol=1e-5), + "Reconstructed raster does not match the original", + ) + + def test_all_zero_raster(self): + """Test FFT behavior with an all-zero raster.""" + self.runModule( + "r.mapcalc", expression=f"{self.input_raster} = 0", overwrite=True + ) + self.assertModule( + "i.fft", + input=self.input_raster, + real=self.real_output, + imaginary=self.imag_output, + overwrite=True, + ) + self.assertRasterExists(self.real_output) + self.assertRasterExists(self.imag_output) + + real_values = array.array(self.real_output) + imag_values = array.array(self.imag_output) + self.assertTrue( + np.allclose(real_values, 0), "Real component should be all zeros" + ) + self.assertTrue( + np.allclose(imag_values, 0), "Imaginary component should be all zeros" + ) + + def test_all_one_raster(self): + """Test FFT behavior with an all-one raster.""" + self.runModule( + "r.mapcalc", expression=f"{self.input_raster} = 1", overwrite=True + ) + self.assertModule( + "i.fft", + input=self.input_raster, + real=self.real_output, + imaginary=self.imag_output, + overwrite=True, + ) + self.assertRasterExists(self.real_output) + self.assertRasterExists(self.imag_output) + + real_values = array.array(self.real_output) + imag_values = array.array(self.imag_output) + self.assertTrue( + np.all(np.isfinite(real_values)), "Real component should have valid values" + ) + self.assertTrue( + np.all(np.isfinite(imag_values)), + "Imaginary component should have valid values", + ) + + def test_large_raster_performance(self): + """Assess performance with a larger raster.""" + self.runModule("g.region", n=90, s=-90, e=180, w=-180, rows=1000, cols=1000) + self.runModule( + "r.mapcalc", expression=f"{self.input_raster} = col()", overwrite=True + ) + + self.assertModule( + "i.fft", + input=self.input_raster, + real=self.real_output, + imaginary=self.imag_output, + overwrite=True, + ) + + self.assertRasterExists(self.real_output) + self.assertRasterExists(self.imag_output) + + +if __name__ == "__main__": + test() diff --git a/imagery/i.group/main.c b/imagery/i.group/main.c index d9a51e01073..84c08cc83e7 100644 --- a/imagery/i.group/main.c +++ b/imagery/i.group/main.c @@ -448,6 +448,7 @@ static void print_subgroups(const char *group, const char *mapset, int simple) if (subgs_num <= 0) { fprintf(stdout, _("Group <%s> does not contain any subgroup.\n"), group); + G_free(subgs); return; } max = 0; diff --git a/imagery/i.landsat.acca/i.landsat.acca.html b/imagery/i.landsat.acca/i.landsat.acca.html index 5e594389762..41f69905706 100644 --- a/imagery/i.landsat.acca/i.landsat.acca.html +++ b/imagery/i.landsat.acca/i.landsat.acca.html @@ -22,9 +22,9 @@

      EXAMPLES

      Run the standard ACCA algorithm with filling of small cloud holes (the -f flag): With per-band reflectance raster maps -named 226_62.toar.1, 226_62.toar.2, [...] and LANDSAT-7 -thermal band 226_62.toar.61, outputting to a new raster map -named 226_62.acca: +named 226_62.toar.1, 226_62.toar.2, ... and LANDSAT-7 +thermal band 226_62.toar.61, outputting to a new raster map +named 226_62.acca:
       i.landsat.toar sensor=7 gain=HHHLHLHHL date=2003-04-07 \
      @@ -35,7 +35,7 @@ 

      EXAMPLES

      REFERENCES

      -
        +
        • Irish R.R., Barker J.L., Goward S.N., and Arvidson T., 2006. Characterization of the Landsat-7 ETM+ Automated Cloud-Cover Assessment (ACCA) Algorithm. Photogrammetric Engineering and Remote @@ -45,7 +45,7 @@

          REFERENCES

          S.S. Shen and M.R. Descour (Eds.): Algorithms for Multispectral, Hyperspectral, and Ultraspectral Imagery VI. Proceedings of SPIE, 4049: 348-355.
        • -
      +

      SEE ALSO

      diff --git a/imagery/i.landsat.toar/i.landsat.toar.html b/imagery/i.landsat.toar/i.landsat.toar.html index 4a792c26274..3305c80b7de 100644 --- a/imagery/i.landsat.toar/i.landsat.toar.html +++ b/imagery/i.landsat.toar/i.landsat.toar.html @@ -243,7 +243,7 @@

      DOS1 example

      product_date=2004-02-12 gain=HHHLHLHHL
      -The resulting Landsat channels are named lsat7_2002_toar.1 .. lsat7_2002_toar.8. +The resulting Landsat channels are named lsat7_2002_toar.1 .. lsat7_2002_toar.8.

      REFERENCES

      diff --git a/imagery/i.modis.qc/i.modis.qc.html b/imagery/i.modis.qc/i.modis.qc.html index 3147a006bd1..8f96364a937 100644 --- a/imagery/i.modis.qc/i.modis.qc.html +++ b/imagery/i.modis.qc/i.modis.qc.html @@ -498,12 +498,12 @@

      NOTES

      MOD11A2 quality control (QC) bands do not have a FillValue (No-data) according to MODIS Land Products site. -However, the metadata of the QC bands (i.e.: gdalinfo QC_band) shows No-data=0. +However, the metadata of the QC bands (i.e.: gdalinfo QC_band) shows No-data=0. This value is then transformed into GRASS NULLs when data is imported through r.in.gdal. Applying i.modis.qc on those QC bands will not give the expected range of values in the different QC bits. Therefore, before using i.modis.qc, the user needs to set the NULL value in QC bands -back to zero (i.e.: r.null map=QC_band null=0) or just edit the metadata with GDAL +back to zero (i.e.: r.null map=QC_band null=0) or just edit the metadata with GDAL utilities before importing into GRASS GIS. This is a known issue for MOD11A2 (8-day LST product), but other MODIS products might be affected as well. diff --git a/imagery/i.ortho.photo/i.ortho.elev/i.ortho.elev.html b/imagery/i.ortho.photo/i.ortho.elev/i.ortho.elev.html index ab5b66eaa38..21fedea7e7f 100644 --- a/imagery/i.ortho.photo/i.ortho.elev/i.ortho.elev.html +++ b/imagery/i.ortho.photo/i.ortho.elev/i.ortho.elev.html @@ -1,6 +1,14 @@

      DESCRIPTION

      -i.ortho.elev is used to select or modify the target elevation model. +i.ortho.elev is used to select or modify the target elevation +model for orthorectification of imagery. +This elevation model is essential for both the computation of photo-to-target +parameters and for the actual orthorectification of imagery group files. +The elevation model selected should cover the entire area of the image +group to be orthorectified. + +Optionally, scaled elevation data can be converted to real elevation values +specifying a mathematical expression.

      SEE ALSO

      diff --git a/imagery/i.ortho.photo/i.ortho.transform/i.ortho.transform.html b/imagery/i.ortho.photo/i.ortho.transform/i.ortho.transform.html index 4bb60a9c633..9eb8b293b33 100644 --- a/imagery/i.ortho.photo/i.ortho.transform/i.ortho.transform.html +++ b/imagery/i.ortho.photo/i.ortho.transform/i.ortho.transform.html @@ -5,7 +5,7 @@

      DESCRIPTION

      If coordinates are given with the input file option or fed from -stdin, both the input and the output format is "x y z" with one +stdin, both the input and the output format is "x y z" with one coordinate pair per line. Reverse transform is performed with the -r flag. @@ -28,7 +28,7 @@

      SEE ALSO

      TODO

      -Update this document with x,y,z<->E,N,H information +Update this document with x,y,z<->E,N,H information

      AUTHORS

      diff --git a/imagery/i.pca/main.c b/imagery/i.pca/main.c index 8cc6c4f58fe..3ef5b5501da 100644 --- a/imagery/i.pca/main.c +++ b/imagery/i.pca/main.c @@ -4,7 +4,7 @@ * * AUTHOR(S): Original author Center for Space Research (Uni. of TX) * Rewritten by Brad Douglas - * NULL value/MASK handling and speed up by Markus Metz + * NULL value/mask handling and speed up by Markus Metz * * PURPOSE: Principal Component Analysis transform of raster data. * diff --git a/imagery/i.rectify/i.rectify.html b/imagery/i.rectify/i.rectify.html index b8c21b0b7ef..6bfc49dd4c1 100644 --- a/imagery/i.rectify/i.rectify.html +++ b/imagery/i.rectify/i.rectify.html @@ -11,8 +11,8 @@

      DESCRIPTION

      are first, second, and third order polynomial and thin plate spline. Thin plate spline is recommended for ungeoreferenced satellite imagery where ground control points (GCPs) are included. Examples are -NOAA/AVHRR -and ENVISAT +NOAA/AVHRR +and ENVISAT imagery which include throusands of GCPs.

      @@ -161,9 +161,9 @@

      Resampling method

      NOTES

      If i.rectify starts normally but after some time the following text is seen: -
      +
      ERROR: Error writing segment file -

      +

      the user may try the -c flag or the module needs more free space on the hard drive. diff --git a/imagery/i.segment/i.segment.html b/imagery/i.segment/i.segment.html index 43d5e5fde68..74de8f9177a 100644 --- a/imagery/i.segment/i.segment.html +++ b/imagery/i.segment/i.segment.html @@ -1,5 +1,8 @@

      DESCRIPTION

      +i.segment identifies segments (objects) from +imagery data. +

      Image segmentation or object recognition is the process of grouping similar pixels into unique segments, also referred to as objects. Boundary and region based algorithms are described in the literature, @@ -64,10 +67,10 @@

      Calculation Formulas

      In future, the distance calculation will also take into account the shape characteristics of the segments. The normal distances are then multiplied by the input radiometric weight. Next an additional -contribution is added: (1-radioweight) * {smoothness * smoothness -weight + compactness * (1-smoothness weight)}, -where compactness = Perimeter Length / sqrt( Area ) -and smoothness = Perimeter Length / Bounding Box. The +contribution is added: (1-radioweight) * {smoothness * smoothness +weight + compactness * (1-smoothness weight)}, +where compactness = Perimeter Length / sqrt( Area ) +and smoothness = Perimeter Length / Bounding Box. The perimeter length is estimated as the number of pixel sides the segment has. @@ -175,7 +178,7 @@

      Segmentation of RGB orthophoto

      -The output ortho_segs_l4 with threshold=0.2 still has +The output ortho_segs_l4 with threshold=0.2 still has too many segments, but the output with threshold=0.3 has too few segments. A threshold value of 0.25 seems to be a good choice. There is also some noise in the image, lets next force all segments smaller diff --git a/imagery/i.smap/i.smap.html b/imagery/i.smap/i.smap.html index 4a57e900378..fedf0bb3560 100644 --- a/imagery/i.smap/i.smap.html +++ b/imagery/i.smap/i.smap.html @@ -124,12 +124,23 @@

      NOTES

      15% of the goodness values may need some closer inspection.

      -The module i.smap does not support MASKed or NULL cells. Therefore -it might be necessary to create a copy of the classification results -using e.g. r.mapcalc: +The module i.smap does not support NULL cells (in the +raster image or from raster mask). Therefore, if the input image has NULL +cells, it might be necessary to create a masked classification results +as part of post-processing using e.g. r.mapcalc: + +

      +r.mapcalc "masked_results = if(isnull(input_image), null(), classification_results)"
      +
      +

      +Similarly, if the raster mask is active, +it might be necessary to post-process the classification results +using r.mapcalc which will automatically mask the classification +results: +

      -r.mapcalc "MASKed_map = classification_results"
      +r.mapcalc "masked_results = classification_results"
       

      EXAMPLE

      @@ -217,7 +228,7 @@

      SEE ALSO

      i.group
      for creating groups and subgroups,
      r.mapcalc -to copy classification result in order to cut out MASKed subareas, +to copy classification result in order to cut out masked subareas,
      i.gensigset to generate the signature file required by this program diff --git a/imagery/i.svm.predict/i.svm.predict.html b/imagery/i.svm.predict/i.svm.predict.html index 3ddf4f33879..f0dbcf511cb 100644 --- a/imagery/i.svm.predict/i.svm.predict.html +++ b/imagery/i.svm.predict/i.svm.predict.html @@ -72,11 +72,6 @@

      REFERENCES

      -

      AUTHORS

      +

      AUTHOR

      Maris Nartiss, University of Latvia. - - diff --git a/imagery/i.svm.train/i.svm.train.html b/imagery/i.svm.train/i.svm.train.html index e25397d7ca6..d0ea9b8ba83 100644 --- a/imagery/i.svm.train/i.svm.train.html +++ b/imagery/i.svm.train/i.svm.train.html @@ -101,11 +101,6 @@

      REFERENCES

      -

      AUTHORS

      +

      AUTHOR

      Maris Nartiss, University of Latvia. - - diff --git a/imagery/i.vi/i.vi.html b/imagery/i.vi/i.vi.html index 38786a22abc..0ed45b41d2d 100644 --- a/imagery/i.vi/i.vi.html +++ b/imagery/i.vi/i.vi.html @@ -517,7 +517,7 @@

      Preparation: DN to reflectance

      product_date=2004-02-12 gain=HHHLHLHHL -The resulting Landsat channels are names lsat7_2002_toar.1 .. lsat7_2002_toar.8. +The resulting Landsat channels are names lsat7_2002_toar.1 .. lsat7_2002_toar.8.

      Calculation of NDVI

      @@ -578,7 +578,7 @@

      Calculation of GARI

      NOTES

      -Originally from kepler.gps.caltech.edu (FAQ): +Originally from kepler.gps.caltech.edu (FAQ):

      A FAQ on Vegetation in Remote Sensing
      Written by Terrill W. Ray, Div. of Geological and Planetary Sciences, diff --git a/imagery/imageryintro.html b/imagery/imageryintro.html index a9c93696632..b9258352b86 100644 --- a/imagery/imageryintro.html +++ b/imagery/imageryintro.html @@ -30,9 +30,9 @@

      Image processing in general

      by the sensor of the satellite platform is encoded in 8 or more bits. This energy is called radiance-at-sensor. To obtain physical values from DNs, satellite image providers use a linear transform -equation (y = a * x + b) to encode the radiance-at-sensor +equation (y = a * x + b) to encode the radiance-at-sensor in 8 to 16 bits. DNs can be turned back into physical values by -applying the reverse formula (x = (y - b) / a). +applying the reverse formula (x = (y - b) / a).

      The GRASS GIS module i.landsat.toar easily transforms Landsat DN to radiance-at-sensor (top of atmosphere, @@ -48,7 +48,7 @@

      Image processing in general

      interaction with the sun energy reflected back into space by ground/vegetation/soil needs to be corrected. The need of removing atmospheric artifacts stems from the fact that the -atmosphericic conditions are changing over time. Hence, to gain +atmospheric conditions are changing over time. Hence, to gain comparability between Earth surface images taken at different times, atmospheric need to be removed converting at-sensor values which are top of atmosphere to surface reflectance values. @@ -114,7 +114,7 @@

      Semantic label information

      a different group with identical semantic labels.
      -
      +GRASS GIS band references scheme
      New enhanced classification workflow involving semantic labels. @@ -171,7 +171,7 @@

      Calculation of vegetation indices

      status with NDVI, the Red and the Near Infrared channels (NIR) are taken as used as input for simple map algebra in the GRASS command r.mapcalc -(ndvi = 1.0 * (nir - red)/(nir + red)). With +(ndvi = 1.0 * (nir - red)/(nir + red)). With r.colors an optimized "ndvi" color table can be assigned afterward. Also other vegetation indices can be generated likewise. @@ -204,16 +204,19 @@

      Image classification

      or g.gui.iclass, i.maxlik) using the Maximum Likelihood classification method +
    24. Combined radiometric/geometric (segmentation based) classification: +
    25. Object-oriented classification:
      • Unsupervised classification (segmentation based: i.segment)
      +
    26. Kappa statistic can be calculated to validate the results diff --git a/include/Make/Grass.make b/include/Make/Grass.make index 17ce25fcb71..dfcbd974b6a 100644 --- a/include/Make/Grass.make +++ b/include/Make/Grass.make @@ -213,7 +213,7 @@ NVIZDEPS = $(OGSFLIB) $(GISLIB) $(OPENGLLIB) OGSFDEPS = $(BITMAPLIB) $(RASTER3DLIB) $(VECTORLIB) $(DBMILIB) $(RASTERLIB) $(GISLIB) $(TIFFLIBPATH) $(TIFFLIB) $(OPENGLLIB) $(OPENGLULIB) $(MATHLIB) PNGDRIVERDEPS = $(DRIVERLIB) $(GISLIB) $(PNGLIB) $(MATHLIB) PSDRIVERDEPS = $(DRIVERLIB) $(GISLIB) $(MATHLIB) -RASTERDEPS = $(GISLIB) $(GPROJLIB) $(MATHLIB) +RASTERDEPS = $(GISLIB) $(GPROJLIB) $(MATHLIB) $(PARSONLIB) RLIDEPS = $(RASTERLIB) $(GISLIB) $(MATHLIB) ROWIODEPS = $(GISLIB) RTREEDEPS = $(GISLIB) $(MATHLIB) diff --git a/include/VERSION b/include/VERSION index c4ace75dbf6..97aca63225c 100644 --- a/include/VERSION +++ b/include/VERSION @@ -1,4 +1,4 @@ 8 5 0dev -2024 +2025 diff --git a/include/grass/defs/raster.h b/include/grass/defs/raster.h index bbdc8bfeaa1..f81445a374c 100644 --- a/include/grass/defs/raster.h +++ b/include/grass/defs/raster.h @@ -178,6 +178,10 @@ void Rast__organize_colors(struct Colors *); /* color_out.c */ void Rast_print_colors(struct Colors *, DCELL, DCELL, FILE *, int); +/* json_color_out.c */ +void Rast_print_json_colors(struct Colors *, DCELL, DCELL, FILE *, int, + ColorFormat); + /* color_rand.c */ void Rast_make_random_colors(struct Colors *, CELL, CELL); diff --git a/include/grass/gis.h b/include/grass/gis.h index fcc53535f47..e6b818135e4 100644 --- a/include/grass/gis.h +++ b/include/grass/gis.h @@ -6,7 +6,7 @@ * PURPOSE: This file contains definitions of variables and data types * for use with most, if not all, Grass programs. This file is * usually included in every Grass program. - * COPYRIGHT: (C) 2000-2024 by the GRASS Development Team + * COPYRIGHT: (C) 2000-2025 by the GRASS Development Team * * This program is free software under the GNU General Public * License (>=v2). Read the file COPYING that comes with GRASS diff --git a/include/grass/raster.h b/include/grass/raster.h index 138fb21c3d1..2fed5d88157 100644 --- a/include/grass/raster.h +++ b/include/grass/raster.h @@ -168,6 +168,15 @@ enum History_field { HIST_NUM_FIELDS }; +/*! + \typedef ColorFormat + \brief Color format identifiers (enum) + + Identifiers of all recognized color formats. + + */ +typedef enum { RGB, HEX, HSV, TRIPLET } ColorFormat; + /*! \brief Raster history info (metadata) */ struct History { /*! \brief Array of fields (see \ref History_field for details) */ diff --git a/lib/Makefile b/lib/Makefile index 91c38f5c73b..96a23af2069 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -31,6 +31,7 @@ SUBDIRS = \ lidar \ raster3d \ raster3d/test \ + external/parson/test \ gpde \ dspf \ symbol \ diff --git a/lib/cairodriver/cairodriver.html b/lib/cairodriver/cairodriver.html index 22f8e1264c8..2e294af5edd 100644 --- a/lib/cairodriver/cairodriver.html +++ b/lib/cairodriver/cairodriver.html @@ -7,7 +7,7 @@

      DESCRIPTION

      The Cairo driver generates PNG, BMP, PPM, PS, PDF or SVG images by GRASS display commands, using the -Cairo graphics library. +Cairo graphics library. The image format is selected from the extension of the output file. The Cairo driver is used for GRASS display commands by default if @@ -44,11 +44,11 @@

      Environment variables

    27. GRASS_RENDER_ANTIALIAS
      can be default, none, gray, or subpixel, corresponding to - cairo_antialias_t + cairo_antialias_t
    28. GRASS_RENDER_FILE=filename
      the name and format of the resulting image file, default is - map.png.
      + map.png.
      The image format is determined from the file extension.
      Supported bitmap formats:
        @@ -67,12 +67,12 @@

        Environment variables

        the Cairo library that GRASS was built with.)
      • GRASS_RENDER_FILE_READ
        - if TRUE, the Cairo driver will initialize the image from + if TRUE, the Cairo driver will initialize the image from the contents of GRASS_RENDER_FILE.
        (Note: This is only supported for bitmap formats)
      • GRASS_RENDER_FILE_MAPPED
        - if TRUE, the Cairo driver will map GRASS_RENDER_FILE as its framebuffer, + if TRUE, the Cairo driver will map GRASS_RENDER_FILE as its framebuffer, rather than using memory.
        (Note: This only works with BMP files.)
      • @@ -152,13 +152,13 @@

        NOTES

        The driver is still in development. Enable it by specifying --with-cairo when configuring GRASS. This requires a reasonably recent version of the Cairo libraries -and a working pkg-config. +and a working pkg-config.

        Antialiasing is enabled by default for bitmap formats. There is currently no way of disabling this.

        The resolution of the output images is defined by current region -extents. Use g.region -p to get the number of rows and cols +extents. Use g.region -p to get the number of rows and cols and use the environment variables to set the image size. If you would like a larger image, multiply both rows and cols by the same whole number to preserve the aspect ratio. diff --git a/lib/db/sqlp/sql.html b/lib/db/sqlp/sql.html index a2dc32da2a5..1fd070b7d3c 100644 --- a/lib/db/sqlp/sql.html +++ b/lib/db/sqlp/sql.html @@ -38,10 +38,10 @@

        Database drivers

        https://postgresql.org/ mysqlData storage in MySQL RDBMS -https://www.mysql.org/ +https://www.mysql.com/ odbcData storage via UnixODBC (PostgreSQL, Oracle, etc.) https://www.unixodbc.org/ @@ -69,8 +69,8 @@

        Database table name restrictions

    29. Attempts to use a reserved SQL word (depends on database backend) as column or table name will cause a "SQL syntax error".
    30. -
    31. An error message such as "dbmi: Protocol - error" either indicates an invalid column name or an +
    32. An error message such as "dbmi: Protocol + error" either indicates an invalid column name or an unsupported column type (then the GRASS SQL parser needs to be extended).
    33. DBF column names are limited to 10 characters (DBF API definition).
    34. @@ -151,7 +151,7 @@

      Example of null handling

      Update of attributes

      -Examples of complex expressions in updates (using v.db.* +Examples of complex expressions in updates (using v.db.* modules):
      @@ -164,7 +164,7 @@ 

      Update of attributes

      -Examples of more complex expressions in updates (using db.* +Examples of more complex expressions in updates (using db.* modules):

      diff --git a/lib/display/tran_colr.c b/lib/display/tran_colr.c
      index 63975f18799..2c416abaecc 100644
      --- a/lib/display/tran_colr.c
      +++ b/lib/display/tran_colr.c
      @@ -4,10 +4,12 @@
       
       #include 
       
      -#include 
       #include 
      -#include 
      +#include 
      +#include 
       #include 
      +#include 
      +
       #include "driver.h"
       
       static struct color_rgb *colors;
      @@ -38,7 +40,9 @@ static int translate_or_add_color(const char *str)
           char lowerstr[MAX_COLOR_LEN];
       
           /* Make the color string lowercase for display colors */
      -    strcpy(lowerstr, str);
      +    if (G_strlcpy(lowerstr, str, sizeof(lowerstr)) >= sizeof(lowerstr)) {
      +        G_fatal_error(_("String <%s> is too long"), str);
      +    }
           G_chop(lowerstr);
           G_tolcase(lowerstr);
       
      diff --git a/lib/external/parson/Makefile b/lib/external/parson/Makefile
      index 43aca3bc351..fa6cda3efa1 100644
      --- a/lib/external/parson/Makefile
      +++ b/lib/external/parson/Makefile
      @@ -7,7 +7,7 @@ include $(MODULE_TOPDIR)/include/Make/Lib.make
       default: headers
       	$(MAKE) lib
       
      -headers: $(ARCH_INCDIR)/parson.h
      +headers: $(ARCH_INCDIR)/parson.h $(ARCH_INCDIR)/gjson.h
       
       $(ARCH_INCDIR)/%.h: %.h
       	$(INSTALL_DATA) $< $@
      diff --git a/lib/external/parson/gjson.c b/lib/external/parson/gjson.c
      new file mode 100644
      index 00000000000..54740fa6df8
      --- /dev/null
      +++ b/lib/external/parson/gjson.c
      @@ -0,0 +1,155 @@
      +/*****************************************************************************
      + *
      + * MODULE:       GRASS json output interface
      + *
      + * AUTHOR:       Nishant Bansal (nishant.bansal.282003@gmail.com)
      + *
      + * PURPOSE:      parson library function wrapper
      + *               part of the gjson library
      + *
      + * COPYRIGHT:    (C) 2024 by 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 "gjson.h"
      +
      +/* *************************************************************** */
      +/* ***** WRAPPER FOR PARSON FUNCTIONS USED IN GRASS ************** */
      +/* *************************************************************** */
      +
      +JSON_Value *G_json_value_init_object(void)
      +{
      +    return json_value_init_object();
      +}
      +
      +JSON_Value *G_json_value_init_array(void)
      +{
      +    return json_value_init_array();
      +}
      +
      +JSON_Object *G_json_value_get_object(const JSON_Value *value)
      +{
      +    return json_value_get_object(value);
      +}
      +
      +JSON_Object *G_json_object(const JSON_Value *value)
      +{
      +    return json_object(value);
      +}
      +JSON_Object *G_json_object_get_object(const JSON_Object *object,
      +                                      const char *name)
      +{
      +    return json_object_get_object(object, name);
      +}
      +JSON_Array *G_json_object_get_array(const JSON_Object *object, const char *name)
      +{
      +    return json_object_get_array(object, name);
      +}
      +JSON_Value *G_json_object_get_value(const JSON_Object *object, const char *name)
      +{
      +    return json_object_get_value(object, name);
      +}
      +const char *G_json_object_get_string(const JSON_Object *object,
      +                                     const char *name)
      +{
      +    return json_object_get_string(object, name);
      +}
      +double G_json_object_get_number(const JSON_Object *object, const char *name)
      +{
      +    return json_object_get_number(object, name);
      +}
      +int G_json_object_get_boolean(const JSON_Object *object, const char *name)
      +{
      +    return json_object_get_boolean(object, name);
      +}
      +JSON_Value *G_json_object_get_wrapping_value(const JSON_Object *object)
      +{
      +    return json_object_get_wrapping_value(object);
      +}
      +JSON_Status G_json_object_set_value(JSON_Object *object, const char *name,
      +                                    JSON_Value *value)
      +{
      +    return json_object_set_value(object, name, value);
      +}
      +JSON_Status G_json_object_set_string(JSON_Object *object, const char *name,
      +                                     const char *string)
      +{
      +    return json_object_set_string(object, name, string);
      +}
      +JSON_Status G_json_object_set_number(JSON_Object *object, const char *name,
      +                                     double number)
      +{
      +    return json_object_set_number(object, name, number);
      +}
      +JSON_Status G_json_object_set_boolean(JSON_Object *object, const char *name,
      +                                      int boolean)
      +{
      +    return json_object_set_boolean(object, name, boolean);
      +}
      +JSON_Status G_json_object_set_null(JSON_Object *object, const char *name)
      +{
      +    return json_object_set_null(object, name);
      +}
      +JSON_Array *G_json_array(const JSON_Value *value)
      +{
      +    return json_array(value);
      +}
      +JSON_Value *G_json_array_get_value(const JSON_Array *array, size_t index)
      +{
      +    return json_array_get_value(array, index);
      +}
      +const char *G_json_array_get_string(const JSON_Array *array, size_t index)
      +{
      +    return json_array_get_string(array, index);
      +}
      +double G_json_array_get_number(const JSON_Array *array, size_t index)
      +{
      +    return json_array_get_number(array, index);
      +}
      +int G_json_array_get_boolean(const JSON_Array *array, size_t index)
      +{
      +    return json_array_get_boolean(array, index);
      +}
      +
      +JSON_Status G_json_array_append_value(JSON_Array *array, JSON_Value *value)
      +{
      +    return json_array_append_value(array, value);
      +}
      +
      +JSON_Status G_json_array_append_string(JSON_Array *array, const char *string)
      +{
      +    return json_array_append_string(array, string);
      +}
      +
      +JSON_Status G_json_array_append_number(JSON_Array *array, double number)
      +{
      +    return json_array_append_number(array, number);
      +}
      +
      +JSON_Status G_json_array_append_boolean(JSON_Array *array, int boolean)
      +{
      +    return json_array_append_boolean(array, boolean);
      +}
      +
      +JSON_Status G_json_array_append_null(JSON_Array *array)
      +{
      +    return json_array_append_null(array);
      +}
      +
      +char *G_json_serialize_to_string_pretty(const JSON_Value *value)
      +{
      +    return json_serialize_to_string_pretty(value);
      +}
      +
      +void G_json_free_serialized_string(char *string)
      +{
      +    json_free_serialized_string(string);
      +}
      +void G_json_value_free(JSON_Value *value)
      +{
      +    json_value_free(value);
      +}
      diff --git a/lib/external/parson/gjson.h b/lib/external/parson/gjson.h
      new file mode 100644
      index 00000000000..0e9cb345123
      --- /dev/null
      +++ b/lib/external/parson/gjson.h
      @@ -0,0 +1,48 @@
      +#ifndef GRASS_GJSON_H
      +#define GRASS_GJSON_H
      +
      +#include "parson.h"
      +
      +/* *************************************************************** */
      +/* ***** WRAPPER FOR PARSON FUNCTIONS USED IN GRASS ************** */
      +/* *************************************************************** */
      +
      +extern JSON_Value *G_json_value_init_object(void);
      +extern JSON_Value *G_json_value_init_array(void);
      +
      +extern JSON_Object *G_json_value_get_object(const JSON_Value *);
      +extern JSON_Object *G_json_object(const JSON_Value *);
      +extern JSON_Object *G_json_object_get_object(const JSON_Object *, const char *);
      +extern JSON_Array *G_json_object_get_array(const JSON_Object *, const char *);
      +extern JSON_Value *G_json_object_get_value(const JSON_Object *, const char *);
      +extern const char *G_json_object_get_string(const JSON_Object *, const char *);
      +extern double G_json_object_get_number(const JSON_Object *, const char *);
      +extern int G_json_object_get_boolean(const JSON_Object *, const char *);
      +extern JSON_Value *G_json_object_get_wrapping_value(const JSON_Object *);
      +
      +extern JSON_Status G_json_object_set_value(JSON_Object *, const char *,
      +                                           JSON_Value *);
      +extern JSON_Status G_json_object_set_string(JSON_Object *, const char *,
      +                                            const char *);
      +extern JSON_Status G_json_object_set_number(JSON_Object *, const char *,
      +                                            double);
      +extern JSON_Status G_json_object_set_boolean(JSON_Object *, const char *, int);
      +extern JSON_Status G_json_object_set_null(JSON_Object *, const char *);
      +
      +extern JSON_Array *G_json_array(const JSON_Value *);
      +extern JSON_Value *G_json_array_get_value(const JSON_Array *, size_t);
      +extern const char *G_json_array_get_string(const JSON_Array *, size_t);
      +extern double G_json_array_get_number(const JSON_Array *, size_t);
      +extern int G_json_array_get_boolean(const JSON_Array *, size_t);
      +
      +extern JSON_Status G_json_array_append_value(JSON_Array *, JSON_Value *);
      +extern JSON_Status G_json_array_append_string(JSON_Array *, const char *);
      +extern JSON_Status G_json_array_append_number(JSON_Array *, double);
      +extern JSON_Status G_json_array_append_boolean(JSON_Array *, int);
      +extern JSON_Status G_json_array_append_null(JSON_Array *);
      +
      +extern char *G_json_serialize_to_string_pretty(const JSON_Value *);
      +extern void G_json_free_serialized_string(char *);
      +extern void G_json_value_free(JSON_Value *);
      +
      +#endif /* GRASS_GJSON_H */
      diff --git a/lib/external/parson/test/Makefile b/lib/external/parson/test/Makefile
      new file mode 100644
      index 00000000000..893b3e59bc3
      --- /dev/null
      +++ b/lib/external/parson/test/Makefile
      @@ -0,0 +1,10 @@
      +MODULE_TOPDIR = ../../../..
      +
      +PGM=test.gjson.lib
      +
      +LIBES = $(PARSONLIB) $(GISLIB)
      +DEPENDENCIES = $(PARSONDEP) $(GISDEP)
      +
      +include $(MODULE_TOPDIR)/include/Make/Module.make
      +
      +default: cmd
      diff --git a/lib/external/parson/test/test.gjson.lib.html b/lib/external/parson/test/test.gjson.lib.html
      new file mode 100644
      index 00000000000..1d399f9eafa
      --- /dev/null
      +++ b/lib/external/parson/test/test.gjson.lib.html
      @@ -0,0 +1,9 @@
      +

      DESCRIPTION

      + +test.gjson.lib +is a module dedicated for testing the gjson library. +This module is used by the testing framework to perform library tests. + +

      AUTHOR

      + +Nishant Bansal diff --git a/lib/external/parson/test/test_gjson_lib.h b/lib/external/parson/test/test_gjson_lib.h new file mode 100644 index 00000000000..0cf9bde0ee6 --- /dev/null +++ b/lib/external/parson/test/test_gjson_lib.h @@ -0,0 +1,31 @@ +/***************************************************************************** + * + * MODULE: GRASS gjson Library + * + * AUTHOR: Nishant Bansal (nishant.bansal.282003@gmail.com) + * + * PURPOSE: Unit tests + * + * COPYRIGHT: (C) 2024 by 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. + * + *****************************************************************************/ + +#ifndef _TEST_GJSON_LIB_H_ +#define _TEST_GJSON_LIB_H_ + +#include + +#define TEST_OBJECT_KEY "key" +#define TEST_OBJECT_VALUE "value" +#define TEST_ARRAY_STRING "array" +#define TEST_NUMBER 123.45 +#define TEST_BOOLEAN 1 + +/* parson wrapper tests */ +int unit_test_parson_wrapper(void); + +#endif diff --git a/lib/external/parson/test/test_main.c b/lib/external/parson/test/test_main.c new file mode 100644 index 00000000000..2a2a28b7169 --- /dev/null +++ b/lib/external/parson/test/test_main.c @@ -0,0 +1,78 @@ +/**************************************************************************** + * + * MODULE: test.gjson.lib + * + * AUTHOR: Nishant Bansal (nishant.bansal.282003@gmail.com) + * + * PURPOSE: Unit tests for the gjson library + * + * COPYRIGHT: (C) 2024 by 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 "test_gjson_lib.h" + +/*- Parameters and global variables -----------------------------------------*/ +typedef struct { + struct Flag *testunit; +} paramType; + +paramType param; /*Parameters */ + +/*- prototypes --------------------------------------------------------------*/ +static void set_params(void); /*Fill the paramType structure */ + +/* ************************************************************************* */ +/* Set up the arguments we are expecting ********************************** */ + +/* ************************************************************************* */ +void set_params(void) +{ + param.testunit = G_define_flag(); + param.testunit->key = 'u'; + param.testunit->description = "Run all unit tests"; +} +/* ************************************************************************* */ +/* ************************************************************************* */ + +/* ************************************************************************* */ +int main(int argc, char *argv[]) +{ + struct GModule *module; + int returnstat = 0; + + /* Initialize GRASS */ + G_gisinit(argv[0]); + + module = G_define_module(); + G_add_keyword(_("gjson")); + G_add_keyword(_("unit test")); + module->description = _("Performs unit tests " + "for the gjson library"); + + /* Get parameters from user */ + set_params(); + + if (G_parser(argc, argv)) + exit(EXIT_FAILURE); + + /*Run the unit tests */ + if (param.testunit->answer) { + returnstat += unit_test_parson_wrapper(); + } + + if (returnstat != 0) + G_warning("Errors detected while testing the gjson lib"); + else + G_message("\n-- gjson lib tests finished successfully --"); + + return (returnstat); +} diff --git a/lib/external/parson/test/test_parson_wrapper.c b/lib/external/parson/test/test_parson_wrapper.c new file mode 100644 index 00000000000..ed4a229d7cc --- /dev/null +++ b/lib/external/parson/test/test_parson_wrapper.c @@ -0,0 +1,265 @@ +/***************************************************************************** + * + * MODULE: GRASS Parson Output Library + * + * AUTHOR: Nishant Bansal (nishant.bansal.282003@gmail.com) + * + * PURPOSE: Unit tests for parson wrapper + * + * COPYRIGHT: (C) 2024 by 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 "test_gjson_lib.h" + +static int test_parson_wrapper(void); + +/* ************************************************************************* */ +/* Perform the JSON function unit tests *************************** */ +/* ************************************************************************* */ +int unit_test_parson_wrapper(void) +{ + int sum = 0; + + G_message(_("\n++ Running gjson wrapper unit tests ++")); + + sum += test_parson_wrapper(); + + if (sum > 0) + G_warning(_("\n-- gjson wrapper unit tests failure --")); + else + G_message(_("\n-- gjson wrapper unit tests finished successfully --")); + + return sum; +} + +/* *************************************************************** */ +/* Test all implemented parson wrapper **************** */ +/* *************************************************************** */ +int test_parson_wrapper(void) +{ + int sum = 0; + JSON_Value *value = NULL; + JSON_Object *object = NULL; + JSON_Array *array = NULL; + char *serialized_string; + + G_message("\t * testing JSON object initialization\n"); + value = G_json_value_init_object(); + if (value == NULL) { + G_warning("Error in G_json_value_init_object"); + sum++; + } + else { + G_json_value_free(value); + } + + G_message("\t * testing JSON array initialization\n"); + value = G_json_value_init_array(); + if (value == NULL) { + G_warning("Error in G_json_value_init_array"); + sum++; + } + else { + G_json_value_free(value); + } + + G_message("\t * testing JSON object set and get string\n"); + value = G_json_value_init_object(); + object = G_json_value_get_object(value); + if (object == NULL) { + G_warning("Error in G_json_value_get_object"); + sum++; + } + if (G_json_object_set_string(object, TEST_OBJECT_KEY, TEST_OBJECT_VALUE) != + JSONSuccess) { + G_warning("Error in G_json_object_set_string"); + sum++; + } + const char *retrieved = G_json_object_get_string(object, TEST_OBJECT_KEY); + if (strcmp(retrieved, TEST_OBJECT_VALUE) != 0) { + G_warning("Error in G_json_object_get_string %s != %s", + TEST_OBJECT_VALUE, retrieved); + sum++; + } + G_json_value_free(value); + + G_message("\t * testing JSON object get wrapping value"); + value = G_json_value_init_object(); + object = G_json_value_get_object(value); + if (G_json_object_get_wrapping_value(object) != value) { + G_warning("Error in G_json_object_get_wrapping_value"); + sum++; + } + G_json_value_free(value); + + G_message("\t * testing JSON object set null\n"); + value = G_json_value_init_object(); + object = G_json_value_get_object(value); + if (G_json_object_set_null(object, TEST_OBJECT_KEY) != JSONSuccess) { + G_warning("Error in G_json_object_set_null"); + sum++; + } + if (json_value_get_type(G_json_object_get_value(object, TEST_OBJECT_KEY)) != + JSONNull) { + G_warning("Error: G_json_object_set_null failed, the value type is not " + "null."); + sum++; + } + G_json_value_free(value); + + G_message("\t * testing JSON object set and get array\n"); + value = G_json_value_init_object(); + object = G_json_value_get_object(value); + if (G_json_object_set_value(object, TEST_OBJECT_KEY, + G_json_value_init_array()) != JSONSuccess) { + G_warning("Error in G_json_object_set_value for array"); + sum++; + } + array = G_json_object_get_array(object, TEST_OBJECT_KEY); + if (!array) { + G_warning("Error in G_json_object_get_array"); + sum++; + } + G_json_value_free(value); + + G_message("\t * testing JSON object get object\n"); + value = G_json_value_init_object(); + object = G_json_value_get_object(value); + if (G_json_object_set_value(object, TEST_OBJECT_KEY, + G_json_value_init_object()) != JSONSuccess) { + G_warning("Error in G_json_object_set_value for nested object"); + sum++; + } + JSON_Object *nested_object = + G_json_object_get_object(object, TEST_OBJECT_KEY); + if (!nested_object) { + G_warning("Error in G_json_object_get_object"); + sum++; + } + G_json_value_free(value); + + G_message("\t * testing JSON array append value\n"); + value = G_json_value_init_array(); + array = G_json_array(value); + if (G_json_array_append_value(array, G_json_value_init_object()) != + JSONSuccess) { + G_warning("Error in G_json_array_append_value"); + sum++; + } + G_json_value_free(value); + + G_message("\t * testing JSON array append number\n"); + value = G_json_value_init_array(); + array = G_json_array(value); + if (G_json_array_append_number(array, TEST_NUMBER) != JSONSuccess) { + G_warning("Error in G_json_array_append_number"); + sum++; + } + if (G_json_array_get_number(array, 0) != TEST_NUMBER) { + G_warning("Error in G_json_array_append_number %f != %f", TEST_NUMBER, + G_json_array_get_number(array, 0)); + sum++; + } + G_json_value_free(value); + + G_message("\t * testing JSON array append boolean\n"); + value = G_json_value_init_array(); + array = G_json_array(value); + if (G_json_array_append_boolean(array, TEST_BOOLEAN) != JSONSuccess) { + G_warning("Error in G_json_array_append_boolean"); + sum++; + } + if (G_json_array_get_boolean(array, 0) != TEST_BOOLEAN) { + G_warning("Error in G_json_array_append_boolean %i != %i", TEST_BOOLEAN, + G_json_array_get_boolean(array, 0)); + sum++; + } + G_json_value_free(value); + + G_message("\t * testing JSON array append null\n"); + value = G_json_value_init_array(); + array = G_json_array(value); + if (G_json_array_append_null(array) != JSONSuccess) { + G_warning("Error in G_json_array_append_null"); + sum++; + } + if (json_value_get_type(G_json_array_get_value(array, 0)) != JSONNull) { + G_warning("Error in G_json_array_append_null, the value type is not " + "null."); + sum++; + } + G_json_value_free(value); + + G_message("\t * testing JSON array append string\n"); + value = G_json_value_init_array(); + array = G_json_array(value); + if (G_json_array_append_string(array, TEST_ARRAY_STRING) != JSONSuccess) { + G_warning("Error in G_json_array_append_string"); + sum++; + } + if (strcmp(G_json_array_get_string(array, 0), TEST_ARRAY_STRING) != 0) { + G_warning("Error in G_json_array_append_string %s != %s", + TEST_ARRAY_STRING, G_json_array_get_string(array, 0)); + sum++; + } + G_json_value_free(value); + + G_message("\t * testing JSON object set and get number\n"); + value = G_json_value_init_object(); + object = G_json_value_get_object(value); + if (G_json_object_set_number(object, TEST_OBJECT_KEY, TEST_NUMBER) != + JSONSuccess) { + G_warning("Error in G_json_object_set_number"); + sum++; + } + double number = G_json_object_get_number(object, TEST_OBJECT_KEY); + if (number != TEST_NUMBER) { + G_warning("Error in G_json_object_get_number %f != %f", TEST_NUMBER, + number); + sum++; + } + G_json_value_free(value); + + G_message("\t * testing JSON object set and get boolean\n"); + value = G_json_value_init_object(); + object = G_json_value_get_object(value); + if (G_json_object_set_boolean(object, TEST_OBJECT_KEY, TEST_BOOLEAN) != + JSONSuccess) { + G_warning("Error in G_json_object_set_boolean"); + sum++; + } + int boolean_value = G_json_object_get_boolean(object, TEST_OBJECT_KEY); + if (boolean_value != TEST_BOOLEAN) { + G_warning("Error in G_json_object_get_boolean %i != %i", TEST_BOOLEAN, + boolean_value); + sum++; + } + G_json_value_free(value); + + G_message("\t * testing JSON serialization\n"); + value = G_json_value_init_object(); + object = G_json_value_get_object(value); + G_json_object_set_string(object, TEST_OBJECT_KEY, TEST_OBJECT_VALUE); + serialized_string = G_json_serialize_to_string_pretty(value); + if (!serialized_string || + strstr(serialized_string, TEST_OBJECT_VALUE) == NULL) { + G_warning("Error in G_json_serialize_to_string_pretty"); + sum++; + } + G_json_free_serialized_string(serialized_string); + G_json_value_free(value); + + return sum; +} diff --git a/lib/external/parson/testsuite/gjson_lib_test.py b/lib/external/parson/testsuite/gjson_lib_test.py new file mode 100644 index 00000000000..6c2eb168266 --- /dev/null +++ b/lib/external/parson/testsuite/gjson_lib_test.py @@ -0,0 +1,17 @@ +"""Test of gjson library + +@author Nishant Bansal +""" + +from grass.gunittest.case import TestCase + + +class GjsonLibraryTest(TestCase): + def test_wrapper(self): + self.assertModule("test.gjson.lib", flags="u") + + +if __name__ == "__main__": + from grass.gunittest.main import test + + test() diff --git a/lib/gis/env.c b/lib/gis/env.c index e35da07b999..fdb52b5d592 100644 --- a/lib/gis/env.c +++ b/lib/gis/env.c @@ -3,7 +3,7 @@ \brief GIS library - environment routines - (C) 2001-2024 by the GRASS Development Team + (C) 2001-2025 by 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. diff --git a/lib/gis/gislib_cmdline_parsing.dox b/lib/gis/gislib_cmdline_parsing.dox index 2c21811ea03..60be9c3a392 100644 --- a/lib/gis/gislib_cmdline_parsing.dox +++ b/lib/gis/gislib_cmdline_parsing.dox @@ -4,7 +4,7 @@ diff --git a/lib/gis/parser_html.c b/lib/gis/parser_html.c index 581eaab2625..313b1e00748 100644 --- a/lib/gis/parser_html.c +++ b/lib/gis/parser_html.c @@ -3,7 +3,7 @@ \brief GIS Library - Argument parsing functions (HTML output) - (C) 2001-2024 by the GRASS Development Team + (C) 2001-2025 by 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. diff --git a/lib/gis/testsuite/test_gis_lib_getl.py b/lib/gis/testsuite/test_gis_lib_getl.py index 72e8992ad39..dec5c0b40a2 100644 --- a/lib/gis/testsuite/test_gis_lib_getl.py +++ b/lib/gis/testsuite/test_gis_lib_getl.py @@ -29,14 +29,14 @@ def read_lines_and_assert(self, get_line_function, newline): """Write and read lines and then assert they are as expected""" lines = ["Line 1", "Line 2", "Line 3"] with open(self.file_path, mode="w", newline=newline) as stream: - for line in lines: - # Python text newline here. - # The specific newline is added by the stream. - stream.write(f"{line}\n") + # Python text newline here. + # The specific newline is added by the stream. + stream.writelines(f"{line}\n" for line in lines) file_ptr = self.libc.fopen(str(self.file_path).encode("utf-8"), b"r") if not file_ptr: - raise FileNotFoundError(f"Could not open file: {self.file_path}") + msg = f"Could not open file: {self.file_path}" + raise FileNotFoundError(msg) try: buffer_size = 50 diff --git a/lib/gis/testsuite/test_parser_json.py b/lib/gis/testsuite/test_parser_json.py index 74ff9673c7f..472a4075156 100644 --- a/lib/gis/testsuite/test_parser_json.py +++ b/lib/gis/testsuite/test_parser_json.py @@ -16,7 +16,6 @@ class TestParserJson(TestCase): - @xfail_windows def test_r_slope_aspect_json(self): args = [ diff --git a/lib/gmath/solvers_classic_iter.c b/lib/gmath/solvers_classic_iter.c index 2240d860349..8ad535f8b6f 100644 --- a/lib/gmath/solvers_classic_iter.c +++ b/lib/gmath/solvers_classic_iter.c @@ -228,6 +228,8 @@ int G_math_solver_jacobi(double **A, double *x, double *b, int rows, int maxit, break; } + G_free(Enew); + return 1; } @@ -285,5 +287,7 @@ int G_math_solver_gs(double **A, double *x, double *b, int rows, int maxit, break; } + G_free(Enew); + return 1; } diff --git a/lib/htmldriver/htmldriver.html b/lib/htmldriver/htmldriver.html index 81036978dc2..a8ff2f07b43 100644 --- a/lib/htmldriver/htmldriver.html +++ b/lib/htmldriver/htmldriver.html @@ -53,11 +53,11 @@

      Environment variables

    35. GRASS_RENDER_HTMLTYPE=type
      type of image map to create (default is CLIENT):
      -
      CLIENT    Netscape/IE client-side +
      CLIENT    Netscape/IE client-side image map (NAME="map").
      -
      APACHE    Apache/NCSA server-side image +
      APACHE    Apache/NCSA server-side image map.
      -
      RAW +
      RAW         Raw url and polygon vertices (url  x1  y1  x2  y2  .....), suitable for conversion to CERN server format, or @@ -66,7 +66,7 @@

      Environment variables

    36. GRASS_RENDER_FILE=filename
      specifies the resulting file to store the html image map, default - is htmlmap. Files without absolute path names are written + is htmlmap. Files without absolute path names are written in the current directory where the driver was started.
      Any existing file of the same name is overwritten without @@ -75,21 +75,21 @@

      Environment variables

    37. GRASS_RENDER_HTMLMINDIST=n
      specifies the minimum distance in pixels that a point must change from the previous point to keep in the list of vertices for a - polygon. The default is 2, which means that a point's x + polygon. The default is 2, which means that a point's x and y difference from the previous point must change by a number of pixels greater than this value. This parameter helps to eliminate closely spaced points.
    38. GRASS_RENDER_HTMLMINBBOX=n
      specifies the minimum bounding box dimensions to record a polygon - as a clickable area. The default is 2, which means that a + as a clickable area. The default is 2, which means that a polygon with a bounding box of less than this value is not included. This parameter helps to eliminate polygons than are a point or line.
    39. GRASS_RENDER_HTMLMAXPOINTS=n
      specifies the maximum number of vertices included in a polygon's - clickable area. The default is 99. Some browsers can only + clickable area. The default is 99. Some browsers can only accommodate polygons of 100 vertices or less. The HTMLMAP driver automatically ensures that a polygon is closed by making the last point the same as the first point.
    40. @@ -172,7 +172,7 @@

      NOTES

      If you create an HTML file with two or more images and image maps, you will need to edit the map names. The HTML driver creates its map with the name -map. A small sed script can easily change the map name: +map. A small sed script can easily change the map name:

       sed -e 's/NAME="map"/NAME="foomap"/' < htmlmap > foomap.html
      diff --git a/lib/init/grass.html b/lib/init/grass.html
      index 736be547716..18747b196ae 100644
      --- a/lib/init/grass.html
      +++ b/lib/init/grass.html
      @@ -72,7 +72,7 @@ 

      Parameters:

      GISDBASE
      Initial database directory which should be a fully qualified path - (e.g., /usr/local/share/grassdata) + (e.g., /usr/local/share/grassdata)
      PROJECT
      Initial project directory which is a subdirectory of GISDBASE @@ -128,15 +128,15 @@

      Config flag

      version parameters, with the options:
        -
      • arch: system architecture (e.g., x86_64-pc-linux-gnu)
      • -
      • build: (e.g., ./configure --with-cxx --enable-largefile --with-proj [...])
      • -
      • compiler: (e.g., gcc)
      • -
      • date: (e.g., 2024-04-10T11:44:54+00:00)
      • -
      • path: (e.g., /usr/lib64/grass)
      • -
      • python_path: (e.g., /usr/lib64/grass/etc/python)
      • -
      • revision: (e.g., 745ee7ec9)
      • -
      • svn_revision: (e.g., 062bffc8)
      • -
      • version: (e.g., 8.4.0)
      • +
      • arch: system architecture (e.g., x86_64-pc-linux-gnu)
      • +
      • build: (e.g., ./configure --with-cxx --enable-largefile --with-proj [...])
      • +
      • compiler: (e.g., gcc)
      • +
      • date: (e.g., 2024-04-10T11:44:54+00:00)
      • +
      • path: (e.g., /usr/lib64/grass)
      • +
      • python_path: (e.g., /usr/lib64/grass/etc/python)
      • +
      • revision: (e.g., 745ee7ec9)
      • +
      • svn_revision: (e.g., 062bffc8)
      • +
      • version: (e.g., 8.4.0)

      SAMPLE DATA

      @@ -157,7 +157,7 @@

      ENVIRONMENT VARIABLES

      bother with these.

      In addition to these shell environment variables GRASS maintains a -number of GIS environment variables in the $HOME/.grass8/rc +number of GIS environment variables in the $HOME/.grass8/rc file. User changes to this file will be read during the next startup of GRASS. If this file becomes corrupted the user may edit it by hand or remove it to start afresh. See the list @@ -167,7 +167,7 @@

      ENVIRONMENT VARIABLES

      Note that you will need to set these variables using the appropriate method required for the UNIX shell that you use (e.g. in a -Bash shell you must export the variables for them to +Bash shell you must export the variables for them to propagate).

      User Interface Environment Variable

      @@ -176,8 +176,8 @@

      User Interface Environment Variable

      environment variable called GRASS_GUI which indicates the type of user interface for GRASS to use. If this variable is not set when grass is run, then it will be created and then saved -in the $HOME/.grass8/rc file for the next time GRASS is -run. It can be set to text, gtext or gui. +in the $HOME/.grass8/rc file for the next time GRASS is +run. It can be set to text, gtext or gui.

      There is an order of precedence in the way grass determines the user interface to use. The following is the hierarchy @@ -185,19 +185,19 @@

      User Interface Environment Variable

      1. Command line argument
      2. Environment variable GRASS_GUI
      3. -
      4. Value set in $HOME/.grass8/rc (GUI)
      5. -
      6. Default value - gui
      7. +
      8. Value set in $HOME/.grass8/rc (GUI)
      9. +
      10. Default value - gui

      Python Environment Variables

      If you choose to use wxGUI interface, then the GRASS_PYTHON environment variable can be used to -override your system default python command. +override your system default python command.

      Suppose for example your system has Python 3.6 installed and you install a personal version of the Python 3.8 binaries -under $HOME/bin. You can use the above variables to have +under $HOME/bin. You can use the above variables to have GRASS use the Python 3.8 binaries instead.

      @@ -233,8 +233,8 @@ 

      Addon Base for Extra Local GRASS Addon Modules

      path environment.

      If not defined by user, this variable is set by GRASS startup program -to $HOME/.grass8/addons on GNU/Linux -and %APPDATA%\Roaming\GRASS8\addons on MS Windows. +to $HOME/.grass8/addons on GNU/Linux +and %APPDATA%\Roaming\GRASS8\addons on MS Windows.

      HTML Browser Variable

      @@ -455,20 +455,20 @@

      Using temporary mapset

      Troubleshooting

      -Importantly, to avoid an "[Errno 8] Exec format error" there must be a +Importantly, to avoid an "[Errno 8] Exec format error" there must be a shebang line at the top of -the script (like #!/bin/sh, #!/bin/bash, or #!/usr/bin/env python3) +the script (like #!/bin/sh, #!/bin/bash, or #!/usr/bin/env python3) indicating which interpreter to be used for the script. The script file must have its executable bit set.

      CAVEAT

      If you start GRASS using the wxGUI -interface you must have a python command in your $PATH +interface you must have a python command in your $PATH variable. That is, the command must be named -python and not something like python3.6. Rarely some -Python installations do not create a python command. In these -cases you can override python by GRASS_PYTHON environmental +python and not something like python3.6. Rarely some +Python installations do not create a python command. In these +cases you can override python by GRASS_PYTHON environmental variable.

      Furthermore, if you have more than one version of Python installed, diff --git a/lib/init/grass.py b/lib/init/grass.py index 6e9dcbf7a58..9d3bb2bb8a6 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -18,7 +18,7 @@ # command line options for setting the GISDBASE, LOCATION, # and/or MAPSET. Finally it starts GRASS with the appropriate # user interface and cleans up after it is finished. -# COPYRIGHT: (C) 2000-2024 by the GRASS Development Team +# COPYRIGHT: (C) 2000-2025 by the GRASS Development Team # # This program is free software under the GNU General # Public License (>=v2). Read the file COPYING that @@ -231,7 +231,8 @@ def f(fmt, *args): matches.append(m) if len(matches) != len(args): - raise Exception("The numbers of format specifiers and arguments do not match") + msg = "The numbers of format specifiers and arguments do not match" + raise Exception(msg) i = len(args) - 1 for m in reversed(matches): @@ -524,8 +525,7 @@ def write_gisrcrc(gisrcrc, gisrc, skip_variable=None): del lines[number] number += 1 with open(gisrcrc, "w") as f: - for line in lines: - f.write(line) + f.writelines(lines) def read_env_file(path): @@ -542,8 +542,7 @@ def write_gisrc(kv, filename, append=False): # use append=True to avoid a race condition between write_gisrc() and # grass_prompt() on startup (PR #548) f = open(filename, "a" if append else "w") - for k, v in kv.items(): - f.write("%s: %s\n" % (k, v)) + f.writelines("%s: %s\n" % (k, v) for k, v in kv.items()) f.close() @@ -604,13 +603,10 @@ def read_gui(gisrc, default_gui): def create_initial_gisrc(filename): # for convenience, define GISDBASE as pwd: - s = ( - r"""GISDBASE: %s + s = r"""GISDBASE: %s LOCATION_NAME: MAPSET: -""" - % Path.cwd() - ) +""" % Path.cwd() writefile(filename, s) @@ -737,9 +733,7 @@ def cannot_create_location_reason(gisdbase, location): " already exists." ).format(**locals()) return _( - "Unable to create new project in" - " the directory <{path}>" - " for an unknown reason." + "Unable to create new project in the directory <{path}> for an unknown reason." ).format(**locals()) @@ -1610,9 +1604,8 @@ def sh_like_startup(location, location_name, grass_env_file, sh): shrc = ".zshrc" grass_shrc = ".grass.zshrc" else: - raise ValueError( - "Only bash-like and zsh shells are supported by sh_like_startup()" - ) + msg = "Only bash-like and zsh shells are supported by sh_like_startup()" + raise ValueError(msg) # save command history in mapset dir and remember more # bash history file handled in specific_addition @@ -1666,9 +1659,7 @@ def sh_like_startup(location, location_name, grass_env_file, sh): fc -R _grass_old_mapset="$MAPSET_PATH" fi - """.format( - sh_history=sh_history - ) + """.format(sh_history=sh_history) elif sh == "bash": # Append existing history to file ("flush"). # Clear the (in-memory) history. @@ -1682,9 +1673,7 @@ def sh_like_startup(location, location_name, grass_env_file, sh): history -r _grass_old_mapset="$MAPSET_PATH" fi - """.format( - sh_history=sh_history - ) + """.format(sh_history=sh_history) # Ubuntu sudo creates a file .sudo_as_admin_successful and bash checks # for this file in the home directory from /etc/bash.bashrc and prints a # message if it's not detected. This can be suppressed with either @@ -1717,9 +1706,9 @@ def sh_like_startup(location, location_name, grass_env_file, sh): fi }} PROMPT_COMMAND=grass_prompt\n""".format( - both_masks=_("2D and 3D raster MASKs present"), - mask2d=_("Raster MASK present"), - mask3d=_("3D raster MASK present"), + both_masks=_("2D and 3D raster masks present"), + mask2d=_("Raster mask present"), + mask3d=_("3D raster mask present"), mask2d_test=mask2d_test, mask3d_test=mask3d_test, specific_addition=specific_addition, @@ -2106,10 +2095,11 @@ def find_grass_python_package(): # now we can import stuff from grass package else: # Not translatable because we don't have translations loaded. - raise RuntimeError( + msg = ( "The grass Python package is missing. " "Is the installation of GRASS GIS complete?" ) + raise RuntimeError(msg) def main(): diff --git a/lib/init/grass.sh b/lib/init/grass.sh index 2a83e7448c3..33630b14362 100755 --- a/lib/init/grass.sh +++ b/lib/init/grass.sh @@ -13,7 +13,7 @@ # setting the GISDBASE, LOCATION, and/or MAPSET. # Finally it starts GRASS with the appropriate user # interface and cleans up after it is finished. -# COPYRIGHT: (C) 2000-2024 by the GRASS Development Team +# COPYRIGHT: (C) 2000-2025 by the GRASS Development Team # # This program is free software under the GNU General # Public License (>=v2). Read the file COPYING that diff --git a/lib/init/variables.html b/lib/init/variables.html index b7704c5537b..4bd369abf4b 100644 --- a/lib/init/variables.html +++ b/lib/init/variables.html @@ -1,5 +1,11 @@ -There are two types of variables: + +A variable in scripting is a symbolic name that holds data which can be +used and modified during script execution. Variables allow scripts to +store and manipulate values dynamically, making them more flexible and +reusable. + +In GRASS GIS, there are two types of variables:

      • shell environment variables,
      • @@ -42,18 +48,18 @@

        Setting shell environment variables

      • To get personal BASH shell definitions (aliases, color listing option, ...) into GRASS, store them in:
        -$HOME/.grass8/bashrc
      • +$HOME/.grass8/bashrc
      • To get personal CSH shell definitions (aliases, color listing option, ...) into GRASS, store them in:
        -$HOME/.grass8/cshrc
      • +$HOME/.grass8/cshrc

      Setting GRASS gisenv variables

      Use g.gisenv within GRASS. This permanently -predefines GRASS variables in the $HOME/.grass8/rc file (Linux, Mac, BSD, ...) -or in the %APPDATA%\Roaming\GRASS8\rc file (Windows) after the +predefines GRASS variables in the $HOME/.grass8/rc file (Linux, Mac, BSD, ...) +or in the %APPDATA%\Roaming\GRASS8\rc file (Windows) after the current GRASS session is closed.

      Usage: @@ -83,7 +89,7 @@

      List of selected (GRASS related) shell environment variables

      startup script.
      GISRC
      -
      name of $HOME/.grass8/rc file. Defines the system wide value +
      name of $HOME/.grass8/rc file. Defines the system wide value when starting a GRASS session. Within a GRASS session, a temporary copy of this file will be used.
      @@ -95,10 +101,10 @@

      List of selected (GRASS related) shell environment variables

      GRASS_ADDON_BASE
      [grass startup script]
      allows specifying additional GISBASE for local GRASS modules (normally installed as GRASS Addons - by g.extension module) extra to standard + by g.extension module) extra to standard distribution. The default on GNU/Linux - is $HOME/.grass8/addons, on MS - Windows %APPDATA%\Roaming\GRASS8\addons.
      + is $HOME/.grass8/addons, on MS + Windows %APPDATA%\Roaming\GRASS8\addons.
  • GRASS_ADDON_ETC
    [libgis, g.findetc]
    @@ -108,9 +114,9 @@

    List of selected (GRASS related) shell environment variables

    GRASS_COMPATIBILITY_TEST
    [libgis]
    By default it is not possible to run C modules with a libgis that has a - different GIS_H_VERSION, the compatibility test will exit with a + different GIS_H_VERSION, the compatibility test will exit with a fatal error. Setting this variable to 0 (zero) with - GRASS_COMPATIBILITY_TEST=0 allows the test to be passed with a + GRASS_COMPATIBILITY_TEST=0 allows the test to be passed with a warning.
    GRASS_COMPRESSOR
    @@ -119,7 +125,7 @@

    List of selected (GRASS related) shell environment variables

    environment variable GRASS_COMPRESSOR. Supported methods are RLE, ZLIB, LZ4, BZIP2, and ZSTD. The default is ZSTD if available, otherwise ZLIB, which can be changed with e.g. - GRASS_COMPRESSOR=ZSTD, granted that GRASS has been + GRASS_COMPRESSOR=ZSTD, granted that GRASS has been compiled with the requested compressor. Compressors that are always available are RLE, ZLIB, and LZ4. The compressors BZIP2 and ZSTD must be enabled when configuring GRASS for compilation.
    @@ -128,8 +134,8 @@

    List of selected (GRASS related) shell environment variables

    [grass startup script]
    specifies root path for GRASS configuration directory. If not specified, the default placement of the - configuration directory is used: $HOME on GNU/Linux, - $HOME/Library on Mac OS X, and %APPDATA% on MS Windows.
    + configuration directory is used: $HOME on GNU/Linux, + $HOME/Library on Mac OS X, and %APPDATA% on MS Windows.
    GRASS_DB_ENCODING
    [various modules, wxGUI]
    @@ -139,7 +145,7 @@

    List of selected (GRASS related) shell environment variables

    If set, GIS_ERROR_LOG should be the absolute path to the log file (a relative path will be interpreted relative to the process' cwd, not the cwd at the point the user set the variable). If not - set, $HOME/GIS_ERROR_LOG is used instead. The file will + set, $HOME/GIS_ERROR_LOG is used instead. The file will only be used if it already exists.
    GRASS_ERROR_MAIL
    @@ -149,7 +155,7 @@

    List of selected (GRASS related) shell environment variables

    GRASS_FONT
    [display drivers]
    specifies the font as either the name of a font from - $GISBASE/etc/fontcap (or alternative fontcap file + $GISBASE/etc/fontcap (or alternative fontcap file specified by GRASS_FONT_CAP), or alternatively the full path to a FreeType font file.
    @@ -160,7 +166,7 @@

    List of selected (GRASS related) shell environment variables

    GRASS_FONT_CAP
    [g.mkfontcap, d.font, display drivers]
    - specifies an alternative location (to $GISBASE/etc/fontcap) for + specifies an alternative location (to $GISBASE/etc/fontcap) for the font configuration file.
    GRASS_FULL_OPTION_NAMES
    @@ -169,10 +175,10 @@

    List of selected (GRASS related) shell environment variables

    a found string is not an exact match for the given string.
    GRASS_GUI
    -
    either text (text user interface), gtext (text - user interface with GUI welcome screen), or gui (graphical +
    either text (text user interface), gtext (text + user interface with GUI welcome screen), or gui (graphical user interface) to define non-/graphical startup. Can also specify - the name of the GUI to use, e.g. wxpython + the name of the GUI to use, e.g. wxpython (wxGUI). Also exists as a GRASS gisenv variable (see below). If this shell variable exists at GRASS startup, it will determine the GUI used. If it is not defined @@ -198,11 +204,11 @@

    List of selected (GRASS related) shell environment variables

    using RLE compression.

    If the variable doesn't exist, or the value is non-zero, zlib compression - will be used instead. Such rasters will have a compressed + will be used instead. Such rasters will have a compressed value of 2 in the cellhd file.

    Obviously, decompression is controlled by the - raster's compressed value, not the environment variable.
    + raster's compressed value, not the environment variable.
    GRASS_ZLIB_LEVEL
    [libgis]
    if the environment variable GRASS_ZLIB_LEVEL exists and its value can @@ -210,9 +216,9 @@

    List of selected (GRASS related) shell environment variables

    raster maps are compressed using zlib compression. This applies to all raster map types (CELL, FCELL, DCELL).

    - Valid zlib compression levels are -1 to 9. The GRASS_ZLIB_LEVEL=-1 corresponds - to the zlib default value (equivalent to GRASS_ZLIB_LEVEL=6). Often - GRASS_ZLIB_LEVEL=1 gives the best compromise between speed and compression. + Valid zlib compression levels are -1 to 9. The GRASS_ZLIB_LEVEL=-1 corresponds + to the zlib default value (equivalent to GRASS_ZLIB_LEVEL=6). Often + GRASS_ZLIB_LEVEL=1 gives the best compromise between speed and compression.

    If the variable doesn't exist, or the value cannot be parsed as an integer, zlib's default compression level 6 will be used.
    @@ -221,13 +227,13 @@

    List of selected (GRASS related) shell environment variables

    [various modules, wxGUI]
    it may be set to either
      -
    • standard - sets percentage output and message +
    • standard - sets percentage output and message formatting style to standard formatting,
    • -
    • gui - sets percentage output and message formatting +
    • gui - sets percentage output and message formatting style to GUI formatting,
    • -
    • silent - disables percentage output and error +
    • silent - disables percentage output and error messages,
    • -
    • plain - sets percentage output and message +
    • plain - sets percentage output and message formatting style to ASCII output without rewinding control characters.
    @@ -236,14 +242,14 @@

    List of selected (GRASS related) shell environment variables

    [various modules]
    swaps mouse buttons for two-button or left-handed mice. Its value has three digits 1, 2, and 3, which represent default left, middle, and right buttons - respectively. Setting to 132 will swap middle and right + respectively. Setting to 132 will swap middle and right buttons. Note that this variable should be set before a display driver is initialized (e.g., - d.mon x0).
    + d.mon x0).
    GRASS_PAGER
    [various modules]
    - it may be set to either less, more, or cat.
    + it may be set to either less, more, or cat.
    GRASS_PERL
    [used during install process for generating man pages]
    @@ -252,7 +258,7 @@

    List of selected (GRASS related) shell environment variables

    GRASS_PROXY
    [used during addon install/reinstall process for generating man pages (download commit from GitHub API server and remote modules.xml file)]
    - set the proxy with: GRASS_PROXY="http=<value>,ftp=<value>".
    + set the proxy with: GRASS_PROXY="http=<value>,ftp=<value>".
    GRASS_SKIP_MAPSET_OWNER_CHECK
    By default it is not possible to work with MAPSETs that are @@ -272,7 +278,7 @@

    List of selected (GRASS related) shell environment variables

    GRASS_PYTHON
    [wxGUI, Python Ctypes]
    set to override Python executable.
    - On Mac OS X this should be the pythonw executable for the + On Mac OS X this should be the pythonw executable for the wxGUI to work.
    GRASS_VECTOR_LOWMEM
    @@ -315,14 +321,14 @@

    List of selected (GRASS related) shell environment variables

    vector maps will be read (if found) also from this directory. It may be set to either:
      -
    • keep - the temporary vector map is not deleted when +
    • keep - the temporary vector map is not deleted when closing the map.
    • -
    • move - the temporary vector map is moved to the +
    • move - the temporary vector map is moved to the current mapset when closing the map.
    • -
    • delete - the temporary vector map is deleted when +
    • delete - the temporary vector map is deleted when closing the map.
    - Default value is keep. + Default value is keep. Note that temporary vector maps are not visible to the user via g.list @@ -332,7 +338,7 @@

    List of selected (GRASS related) shell environment variables

    GRASS_VECTOR_TMPDIR_MAPSET
    [vectorlib]
    By default GRASS temporary directory is located in - $LOCATION/$MAPSET/.tmp/$HOSTNAME. If GRASS_VECTOR_TMPDIR_MAPSET is + $LOCATION/$MAPSET/.tmp/$HOSTNAME. If GRASS_VECTOR_TMPDIR_MAPSET is set to '0', the temporary directory is located in TMPDIR (environmental variable defined by the user or GRASS initialization script if not given).
    @@ -473,7 +479,7 @@

    List of selected internal GRASS environment variables

    This variable is automatically created by g.parser so that the - --overwrite option will + --overwrite option will be inherited by dependent modules as the script runs. Setting either the GRASS_OVERWRITE environment variable or the OVERWRITE gisenv variable detailed below will cause maps with identical names to be overwritten.
    @@ -489,7 +495,7 @@

    List of selected internal GRASS environment variables

  • 3 - additional verbose messages are printed
  • This variable is automatically created by g.parser - so that the --verbose or --quiet flags will be inherited + so that the --verbose or --quiet flags will be inherited by dependent modules as the script runs.
    GRASS_REGION
    @@ -501,7 +507,7 @@

    List of selected internal GRASS environment variables

    WIND_OVERRIDE
    [libgis]
    it causes programs to use the specified named region (created with - e.g. g.region save=...) to be used as the current region, instead of + e.g. g.region save=...) to be used as the current region, instead of the region from the WIND file.

    This allows programs such as the GUI to run external commands on an alternate region without having to modify the WIND file then change it @@ -535,7 +541,7 @@

    List of selected GRASS gisenv variables


    process id of the start-up shell script
    GUI
    -
    See GRASS_GUI environmental variable for details.
    +
    See GRASS_GUI environmental variable for details.
    LOCATION
    full path to project (previously called location) directory
    @@ -570,7 +576,7 @@

    List of selected GRASS gisenv variables

    This variable is automatically created by g.parser so that the - --overwrite option will + --overwrite option will be inherited by dependent modules as the script runs. Setting either the GRASS_OVERWRITE environment variable or the OVERWRITE gisenv variable detailed below will cause maps with identical names to be overwritten. @@ -579,27 +585,27 @@

    List of selected GRASS gisenv variables

    GRASS-related Files

    -
    $HOME/.grass8/rc
    +
    $HOME/.grass8/rc
    stores the GRASS gisenv variables (not shell environment variables)
    -
    $HOME/.grass8/bashrc
    +
    $HOME/.grass8/bashrc
    stores the shell environment variables (Bash only)
    -
    $HOME/.grass8/env.bat
    +
    $HOME/.grass8/env.bat
    stores the shell environment variables (MS Windows only)
    -
    $HOME/.grass8/login
    +
    $HOME/.grass8/login
    stores the DBMI passwords in this hidden file. Only the file owner can access this file.
    -
    $HOME/GIS_ERROR_LOG
    +
    $HOME/GIS_ERROR_LOG
    if this file exists then all GRASS error and warning messages are logged here. Applies to current user. To generate the file, use: - touch $HOME/GIS_ERROR_LOG
    + touch $HOME/GIS_ERROR_LOG
    See also GIS_ERROR_LOG variable.
    -Note: On MS Windows the files are stored in %APPDATA%. +Note: On MS Windows the files are stored in %APPDATA%.

    SEE ALSO

    diff --git a/lib/pngdriver/pngdriver.html b/lib/pngdriver/pngdriver.html index 1b1f6b2a2cb..f22834c0c2e 100644 --- a/lib/pngdriver/pngdriver.html +++ b/lib/pngdriver/pngdriver.html @@ -41,7 +41,7 @@

    Environment variables

    sets true-color support. Default is TRUE.
  • GRASS_RENDER_FILE=filename
    - the filename to put the resulting image in, default is map.png. + the filename to put the resulting image in, default is map.png. If you set GRASS_RENDER_FILE to a filename which ends in ".ppm", a PPM file will be created (with alpha channel stored in a PGM image, if applicable). If you set GRASS_RENDER_FILE to a filename which ends in ".bmp", a 32-bpp @@ -51,11 +51,11 @@

    Environment variables

    compression level of PNG files (0 = none, 1 = fastest, 9 = best, default is 6)
  • GRASS_RENDER_FILE_READ
    - if TRUE, the PNG driver will initialize the image from + if TRUE, the PNG driver will initialize the image from the contents of GRASS_RENDER_FILE.
  • GRASS_RENDER_FILE_MAPPED
    - if TRUE, the PNG driver + if TRUE, the PNG driver will map GRASS_RENDER_FILE as its framebuffer, rather than using memory. This only works with BMP files.
  • @@ -72,7 +72,7 @@

    Example

    d.vect roadsmajor color=red -This writes a file named map.png in your current directory. +This writes a file named map.png in your current directory.

    NOTES

    @@ -83,14 +83,14 @@

    NOTES

    be installed for the PNG driver to work (it's worth it).

    The resolution of the output images is defined by current region -extents. Use g.region -p to get the number of rows and cols +extents. Use g.region -p to get the number of rows and cols and use the environment variables to set the image size. If you would like a larger image, multiply both rows and cols by the same whole number to preserve the aspect ratio.

    Further PNG file processing (e.g. quantization to 1 bit for monochrome -images) can be done with pnmquant of -the netpbm tools. +images) can be done with pnmquant of +the netpbm tools.

    SEE ALSO

    diff --git a/lib/psdriver/psdriver.html b/lib/psdriver/psdriver.html index 31af82fae88..da593702444 100644 --- a/lib/psdriver/psdriver.html +++ b/lib/psdriver/psdriver.html @@ -38,16 +38,16 @@

    Environment variables

    fit a standard paper size, see also GRASS_RENDER_WIDTH, GRASS_RENDER_HEIGHT.
  • GRASS_RENDER_PS_LANDSCAPE
    - if TRUE, the screen is rotated 90 degrees + if TRUE, the screen is rotated 90 degrees counter-clockwise so that a "landscape" screen fits better on "portrait" paper.
  • GRASS_RENDER_PS_HEADER
    - if FALSE, the output is appended to any existing file, + if FALSE, the output is appended to any existing file, and no prolog or setup sections are generated.
  • GRASS_RENDER_PS_TRAILER
    - if FALSE, no trailer section is generated. + if FALSE, no trailer section is generated.
  • @@ -61,12 +61,12 @@

    Example

    d.rast elevation d.vect roadsmajor color=red -This writes a file named map.ps in your current directory. +This writes a file named map.ps in your current directory.

    NOTES

    The resolution of the output files is defined by current region -extents. Use g.region -p to get the number of rows and cols +extents. Use g.region -p to get the number of rows and cols and use the environment variables to set the image size. If you would like a larger image, multiply both rows and cols by the same whole number to preserve the aspect ratio. @@ -75,7 +75,7 @@

    NOTES

    colorimage and setrgbcolor operators (this is the case for colour printers which pre-date level 2 PostScript). -

    Masked images (d.rast, d.rgb, d.his -n) +

    Masked images (d.rast, d.rgb, d.his -n) require PostScript level 3.

    SEE ALSO

    diff --git a/raster/r.colors.out/prt_json.c b/lib/raster/json_color_out.c similarity index 75% rename from raster/r.colors.out/prt_json.c rename to lib/raster/json_color_out.c index 816a63e1b0e..3f9e69e6f2d 100644 --- a/raster/r.colors.out/prt_json.c +++ b/lib/raster/json_color_out.c @@ -1,15 +1,27 @@ +/*! + * \file lib/raster/json_color_out.c + * + * \brief Raster Library - Print color table in json format + * + * (C) 2010-2024 by 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. + * + * \author Nishant Bansal + */ + #include #include #include #include #include +#include #include -#include #include -#include "local_proto.h" - #define COLOR_STRING_LENGTH 30 /*! @@ -77,7 +89,7 @@ static void rgb_to_hsv(int r, int g, int b, float *h, float *s, float *v) \param clr_frmt color format to be used (RGB, HEX, HSV, TRIPLET). \param color_object pointer to the JSON object */ -static void set_color(int r, int g, int b, enum ColorFormat clr_frmt, +static void set_color(int r, int g, int b, ColorFormat clr_frmt, JSON_Object *color_object) { char color_string[COLOR_STRING_LENGTH]; @@ -87,24 +99,24 @@ static void set_color(int r, int g, int b, enum ColorFormat clr_frmt, case RGB: snprintf(color_string, sizeof(color_string), "rgb(%d, %d, %d)", r, g, b); - json_object_set_string(color_object, "rgb", color_string); + G_json_object_set_string(color_object, "color", color_string); break; case HEX: snprintf(color_string, sizeof(color_string), "#%02X%02X%02X", r, g, b); - json_object_set_string(color_object, "hex", color_string); + G_json_object_set_string(color_object, "color", color_string); break; case HSV: rgb_to_hsv(r, g, b, &h, &s, &v); snprintf(color_string, sizeof(color_string), "hsv(%d, %d, %d)", (int)h, (int)s, (int)v); - json_object_set_string(color_object, "hsv", color_string); + G_json_object_set_string(color_object, "color", color_string); break; case TRIPLET: snprintf(color_string, sizeof(color_string), "%d:%d:%d", r, g, b); - json_object_set_string(color_object, "triplet", color_string); + G_json_object_set_string(color_object, "color", color_string); break; } } @@ -126,7 +138,7 @@ static void set_color(int r, int g, int b, enum ColorFormat clr_frmt, */ static void write_json_rule(DCELL *val, DCELL *min, DCELL *max, int r, int g, int b, JSON_Array *root_array, int perc, - enum ColorFormat clr_frmt, FILE *fp, + ColorFormat clr_frmt, FILE *fp, JSON_Value *root_value) { static DCELL v0; @@ -138,24 +150,24 @@ static void write_json_rule(DCELL *val, DCELL *min, DCELL *max, int r, int g, // Update last processed values v0 = *val, r0 = r, g0 = g, b0 = b; - JSON_Value *color_value = json_value_init_object(); + JSON_Value *color_value = G_json_value_init_object(); if (color_value == NULL) { - json_value_free(root_value); + G_json_value_free(root_value); close_file(fp); G_fatal_error(_("Failed to initialize JSON object. Out of memory?")); } - JSON_Object *color_object = json_object(color_value); + JSON_Object *color_object = G_json_object(color_value); // Set the value as a percentage if requested, otherwise set it as-is if (perc) - json_object_set_number(color_object, "value", - 100 * (*val - *min) / (*max - *min)); + G_json_object_set_number(color_object, "value", + 100 * (*val - *min) / (*max - *min)); else - json_object_set_number(color_object, "value", *val); + G_json_object_set_number(color_object, "value", *val); set_color(r, g, b, clr_frmt, color_object); - json_array_append_value(root_array, color_value); + G_json_array_append_value(root_array, color_value); } /*! @@ -168,15 +180,15 @@ static void write_json_rule(DCELL *val, DCELL *min, DCELL *max, int r, int g, \param perc TRUE for percentage output \param clr_frmt color format to be used (RBG, HEX, HSV, TRIPLET). */ -void print_json_colors(struct Colors *colors, DCELL min, DCELL max, FILE *fp, - int perc, enum ColorFormat clr_frmt) +void Rast_print_json_colors(struct Colors *colors, DCELL min, DCELL max, + FILE *fp, int perc, ColorFormat clr_frmt) { - JSON_Value *root_value = json_value_init_array(); + JSON_Value *root_value = G_json_value_init_array(); if (root_value == NULL) { close_file(fp); G_fatal_error(_("Failed to initialize JSON array. Out of memory?")); } - JSON_Array *root_array = json_array(root_value); + JSON_Array *root_array = G_json_array(root_value); if (colors->version < 0) { /* 3.0 format */ @@ -221,45 +233,45 @@ void print_json_colors(struct Colors *colors, DCELL min, DCELL max, FILE *fp, // Get RGB color for null values and create JSON entry Rast_get_null_value_color(&r, &g, &b, colors); - JSON_Value *nv_value = json_value_init_object(); + JSON_Value *nv_value = G_json_value_init_object(); if (nv_value == NULL) { - json_value_free(root_value); + G_json_value_free(root_value); close_file(fp); G_fatal_error( _("Failed to initialize JSON object. Out of memory?")); } - JSON_Object *nv_object = json_object(nv_value); - json_object_set_string(nv_object, "value", "nv"); + JSON_Object *nv_object = G_json_object(nv_value); + G_json_object_set_string(nv_object, "value", "nv"); set_color(r, g, b, clr_frmt, nv_object); - json_array_append_value(root_array, nv_value); + G_json_array_append_value(root_array, nv_value); // Get RGB color for default values and create JSON entry Rast_get_default_color(&r, &g, &b, colors); - JSON_Value *default_value = json_value_init_object(); + JSON_Value *default_value = G_json_value_init_object(); if (default_value == NULL) { - json_value_free(root_value); + G_json_value_free(root_value); close_file(fp); G_fatal_error( _("Failed to initialize JSON object. Out of memory?")); } - JSON_Object *default_object = json_object(default_value); - json_object_set_string(default_object, "value", "default"); + JSON_Object *default_object = G_json_object(default_value); + G_json_object_set_string(default_object, "value", "default"); set_color(r, g, b, clr_frmt, default_object); - json_array_append_value(root_array, default_value); + G_json_array_append_value(root_array, default_value); } // Serialize JSON array to a string and print to the file - char *json_string = json_serialize_to_string_pretty(root_value); + char *json_string = G_json_serialize_to_string_pretty(root_value); if (!json_string) { - json_value_free(root_value); + G_json_value_free(root_value); close_file(fp); G_fatal_error(_("Failed to serialize JSON to pretty format.")); } fputs(json_string, fp); - json_free_serialized_string(json_string); - json_value_free(root_value); + G_json_free_serialized_string(json_string); + G_json_value_free(root_value); close_file(fp); } diff --git a/lib/rst/interp_float/segmen2d.c b/lib/rst/interp_float/segmen2d.c index f2b13951691..1430bf58320 100644 --- a/lib/rst/interp_float/segmen2d.c +++ b/lib/rst/interp_float/segmen2d.c @@ -70,7 +70,7 @@ int IL_interp_segments_2d( static double smseg; int MINPTS; double pr; - struct triple *point; + struct triple *point = NULL; struct triple skip_point; int m_skip, skip_index, j, k, segtest; double xx, yy /*, zz */; @@ -283,8 +283,11 @@ int IL_interp_segments_2d( } else if (segtest == 1) { if (params->matrix_create(params, data->points, - data->n_points - 1, matrix, indx) < 0) + data->n_points - 1, matrix, + indx) < 0) { + G_free(point); return -1; + } } if (!params->cv) { for (i = 0; i < data->n_points; i++) @@ -332,6 +335,7 @@ int IL_interp_segments_2d( G_free(data->points); G_free(data); } + G_free(point); return 1; } diff --git a/lib/vector/Vlib/build_nat.c b/lib/vector/Vlib/build_nat.c index 3461432dda1..33cf5041e89 100644 --- a/lib/vector/Vlib/build_nat.c +++ b/lib/vector/Vlib/build_nat.c @@ -169,8 +169,10 @@ int Vect_build_nat(struct Map_info *Map, int build) plus->built = GV_BUILD_AREAS; } - if (build < GV_BUILD_ATTACH_ISLES) + if (build < GV_BUILD_ATTACH_ISLES) { + Vect_destroy_cats_struct(Cats); return 1; + } /* Attach isles to areas */ if (plus->built < GV_BUILD_ATTACH_ISLES) { @@ -186,8 +188,10 @@ int Vect_build_nat(struct Map_info *Map, int build) plus->built = GV_BUILD_ATTACH_ISLES; } - if (build < GV_BUILD_CENTROIDS) + if (build < GV_BUILD_CENTROIDS) { + Vect_destroy_cats_struct(Cats); return 1; + } /* Attach centroids to areas */ if (plus->built < GV_BUILD_CENTROIDS) { diff --git a/lib/vector/rtree/docs/sources.htm b/lib/vector/rtree/docs/sources.htm index 31e076775ff..7e597c4dba7 100644 --- a/lib/vector/rtree/docs/sources.htm +++ b/lib/vector/rtree/docs/sources.htm @@ -103,7 +103,7 @@

    Implementation of a simple C++ quaternion class called "Squat". Popularized by a seminal paper by Ken Shoemake, a quaternion represents a rotation about an axis.  Squats can be concatenated together via -the * and *= +the * and *= operators and converted back and forth between transformation matrices. Implementation also includes a wonderful 3D vector macro library by Don Hatch. diff --git a/lib/vector/vectorascii.html b/lib/vector/vectorascii.html index cae0ecef5a6..ed22e4e2200 100644 --- a/lib/vector/vectorascii.html +++ b/lib/vector/vectorascii.html @@ -43,7 +43,7 @@ [ LAYER CATEGORY] -Everything above in [ ] is optional. +Everything above in [ ] is optional.

    The primitive codes are as follows:

      @@ -76,18 +76,18 @@

      Latitude/Longitude data may be given in a number of ways. Decimal degrees must be positive or negative instead of using a hemisphere letter. Mixed coordinates must use a hemisphere letter. Whole minutes and -seconds must always contain two digits (example: use 167:03:04.567; -and not 167:3:4.567). +seconds must always contain two digits (example: use 167:03:04.567; +and not 167:3:4.567).

      Acceptable formats:
      key: D=Degrees; M=Minutes; S=Seconds; h=Hemisphere (N,S,E,W)

        -
      • (+/-)DDD.DDDDD
      • -
      • DDDh
      • -
      • DDD:MMh
      • -
      • DDD:MM.MMMMMh
      • -
      • DDD:MM:SSh
      • -
      • DDD:MM:SS.SSSSSh
      • +
      • (+/-)DDD.DDDDD
      • +
      • DDDh
      • +
      • DDD:MMh
      • +
      • DDD:MM.MMMMMh
      • +
      • DDD:MM:SSh
      • +
      • DDD:MM:SS.SSSSSh

      EXAMPLES

      @@ -132,5 +132,6 @@

      SEE ALSO

      v.in.ascii, v.out.ascii, - v.edit + v.edit, + v.support diff --git a/locale/po/grassmods_ar.po b/locale/po/grassmods_ar.po index 3c4c68d45f8..412f7783428 100644 --- a/locale/po/grassmods_ar.po +++ b/locale/po/grassmods_ar.po @@ -261,7 +261,7 @@ msgid "" "database: %s" msgstr "" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -553,20 +553,20 @@ msgstr "" msgid "raster" msgstr "" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 #, fuzzy msgid "My first raster module" msgstr "اسم الخريطة الراسترية الموجودة" -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -620,7 +620,7 @@ msgstr "اسم الخريطة الراسترية الموجودة" msgid "Raster map <%s> not found" msgstr "غير موجودة <%s> الخريطة الراسترية " -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -805,12 +805,12 @@ msgstr "غير موجودة <%s> الخريطة الراسترية " msgid "vector" msgstr "" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 #, fuzzy msgid "My first vector module" msgstr "اسم الخريطة الراسترية الموجودة" -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -832,13 +832,13 @@ msgstr "اسم الخريطة الراسترية الموجودة" msgid "Vector map <%s> not found" msgstr "لم توجد<%s>الخريطة الفيكتورية" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 #, fuzzy msgid "Unable to set predetermined vector open level" msgstr "اسم خريطة النقط الفيكتورية المخرجة" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -910,7 +910,7 @@ msgstr "اسم خريطة النقط الفيكتورية المخرجة" msgid "Unable to open vector map <%s>" msgstr "[%s]لم يتم فتح ملف الخلية ل " -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -965,7 +965,7 @@ msgstr "[%s]لم يتم فتح ملف الخلية ل " msgid "Unable to create vector map <%s>" msgstr "اسم الخريطة الراسترية الناتجة" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -981,7 +981,7 @@ msgstr "اسم الخريطة الراسترية الناتجة" msgid "Database connection not defined for layer %d" msgstr "لم يتم الإتصال بقاعدة البيانات\n" -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -995,7 +995,7 @@ msgstr "لم يتم الإتصال بقاعدة البيانات\n" msgid "Unable to start driver <%s>" msgstr "'%s'لم يمكن بدء المشغل" -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1046,7 +1046,7 @@ msgstr "'%s'لم يمكن بدء المشغل" msgid "Unable to open database <%s> by driver <%s>" msgstr "%s بالمشغل %s لم يمكن فتح قاعدة البيانات" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1061,17 +1061,17 @@ msgstr "%s بالمشغل %s لم يمكن فتح قاعدة البيانات" msgid "Unable to describe table <%s>" msgstr "لم يمكن وصف الجدول" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, fuzzy, c-format msgid "Unable to get attribute data for cat %d" msgstr "لم يمكن اختيار البيانات من الجدول" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, fuzzy, c-format msgid "Error while retrieving database record for cat %d" msgstr "cat = %d لا يوجد تسجيل للخط" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, fuzzy, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "[%s]لم يتم فتح ملف الخلية ل " @@ -73052,11 +73052,11 @@ msgstr "اسم خريطة الراستر المخرجة" #~ msgstr "ملف المدخل" #, fuzzy -#~ msgid "Numpy array with vector cats." +#~ msgid "NumPy array with vector cats." #~ msgstr "%d نقطة كتبت للمخرج\n" #, fuzzy -#~ msgid "Numpy array with columns names." +#~ msgid "NumPy array with columns names." #~ msgstr "لاتشمل اسماء الأعمدة في المخرج" #, fuzzy diff --git a/locale/po/grassmods_bn.po b/locale/po/grassmods_bn.po index 53b06dd272f..9e4c7bb8280 100644 --- a/locale/po/grassmods_bn.po +++ b/locale/po/grassmods_bn.po @@ -251,7 +251,7 @@ msgid "" "database: %s" msgstr "" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -543,19 +543,19 @@ msgstr "" msgid "raster" msgstr "" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 msgid "My first raster module" msgstr "" -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -609,7 +609,7 @@ msgstr "" msgid "Raster map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -794,11 +794,11 @@ msgstr "" msgid "vector" msgstr "" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 msgid "My first vector module" msgstr "" -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -820,12 +820,12 @@ msgstr "" msgid "Vector map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 msgid "Unable to set predetermined vector open level" msgstr "" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -897,7 +897,7 @@ msgstr "" msgid "Unable to open vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -952,7 +952,7 @@ msgstr "" msgid "Unable to create vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -968,7 +968,7 @@ msgstr "" msgid "Database connection not defined for layer %d" msgstr "" -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -982,7 +982,7 @@ msgstr "" msgid "Unable to start driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1033,7 +1033,7 @@ msgstr "" msgid "Unable to open database <%s> by driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1048,17 +1048,17 @@ msgstr "" msgid "Unable to describe table <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, c-format msgid "Unable to get attribute data for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, c-format msgid "Error while retrieving database record for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "" diff --git a/locale/po/grassmods_cs.po b/locale/po/grassmods_cs.po index 0b4d04a2008..8968a2270cf 100644 --- a/locale/po/grassmods_cs.po +++ b/locale/po/grassmods_cs.po @@ -264,7 +264,7 @@ msgstr "" "ovladač: %s\n" "databáze: %s" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -556,19 +556,19 @@ msgstr "" msgid "raster" msgstr "rastr" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 msgid "My first raster module" msgstr "Můj první modul pro zpracování rastrových dat" -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -622,7 +622,7 @@ msgstr "Můj první modul pro zpracování rastrových dat" msgid "Raster map <%s> not found" msgstr "Rastrová mapa <%s> nenalezena" -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -807,11 +807,11 @@ msgstr "Rastrová mapa <%s> nenalezena" msgid "vector" msgstr "vektor" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 msgid "My first vector module" msgstr "Můj první modul pro zpracování vektorových dat" -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -833,12 +833,12 @@ msgstr "Můj první modul pro zpracování vektorových dat" msgid "Vector map <%s> not found" msgstr "Vektorová mapa <%s> nebyla nalezena" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 msgid "Unable to set predetermined vector open level" msgstr "Nelze nastavit předvybranou vektorovou mapu na úrovni otevření" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -910,7 +910,7 @@ msgstr "Nelze nastavit předvybranou vektorovou mapu na úrovni otevření" msgid "Unable to open vector map <%s>" msgstr "Nelze otevřít vektorovou mapu <%s>" -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -965,7 +965,7 @@ msgstr "Nelze otevřít vektorovou mapu <%s>" msgid "Unable to create vector map <%s>" msgstr "Nelze vytvořit vektorovou mapu <%s>" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -981,7 +981,7 @@ msgstr "Nelze vytvořit vektorovou mapu <%s>" msgid "Database connection not defined for layer %d" msgstr "Spojení s databází nebylo definováno pro vrstvu %d " -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -995,7 +995,7 @@ msgstr "Spojení s databází nebylo definováno pro vrstvu %d " msgid "Unable to start driver <%s>" msgstr "Nelze spustit ovladač <%s>" -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1046,7 +1046,7 @@ msgstr "Nelze spustit ovladač <%s>" msgid "Unable to open database <%s> by driver <%s>" msgstr "Nelze otevřít databázi <%s> ovladačem <%s>" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1061,17 +1061,17 @@ msgstr "Nelze otevřít databázi <%s> ovladačem <%s>" msgid "Unable to describe table <%s>" msgstr "Nelze popsat tabulku <%s>" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, c-format msgid "Unable to get attribute data for cat %d" msgstr "Nelze získat atributová data pro cat= %d" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, c-format msgid "Error while retrieving database record for cat %d" msgstr "Chyba během získání záznamu databáze pro kategorii %d" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "Nelze kopírovat atributovou tabulku do vektorové mapy <%s>" @@ -69845,7 +69845,7 @@ msgstr "" #~ msgstr "Číslo vrstvy (to)" #, fuzzy -#~ msgid "Numpy array with columns names." +#~ msgid "NumPy array with columns names." #~ msgstr "Příliš mnoho názvů sloupců" #, fuzzy diff --git a/locale/po/grassmods_de.po b/locale/po/grassmods_de.po index 2a168b388ca..48ffaefbcb6 100644 --- a/locale/po/grassmods_de.po +++ b/locale/po/grassmods_de.po @@ -259,7 +259,7 @@ msgstr "" "Treiber: %s\n" "Datenbank: %s" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -551,19 +551,19 @@ msgstr "" msgid "raster" msgstr "Raster" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "Schlagwort2" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "Schlagwort3" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 msgid "My first raster module" msgstr "Mein erstes Rastermodul." -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -617,7 +617,7 @@ msgstr "Mein erstes Rastermodul." msgid "Raster map <%s> not found" msgstr "Rasterkarte <%s> konnte nicht gefunden werden." -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -802,11 +802,11 @@ msgstr "Rasterkarte <%s> konnte nicht gefunden werden." msgid "vector" msgstr "Vektor" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 msgid "My first vector module" msgstr "Mein erstes Vektormodul." -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -828,12 +828,12 @@ msgstr "Mein erstes Vektormodul." msgid "Vector map <%s> not found" msgstr "Vektorkarte <%s> nicht gefunden." -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 msgid "Unable to set predetermined vector open level" msgstr "Kann das vorherbestimmte Vektoröffnungslevel nicht setzen." -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -905,7 +905,7 @@ msgstr "Kann das vorherbestimmte Vektoröffnungslevel nicht setzen." msgid "Unable to open vector map <%s>" msgstr "Kann die Vektorkarte <%s> nicht öffnen." -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -960,7 +960,7 @@ msgstr "Kann die Vektorkarte <%s> nicht öffnen." msgid "Unable to create vector map <%s>" msgstr "Kann die Vektorkarte <%s> nicht erzeugen." -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -976,7 +976,7 @@ msgstr "Kann die Vektorkarte <%s> nicht erzeugen." msgid "Database connection not defined for layer %d" msgstr "Die Datenbankverbindung für den Layer<%d> ist nicht definiert." -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -990,7 +990,7 @@ msgstr "Die Datenbankverbindung für den Layer<%d> ist nicht definiert." msgid "Unable to start driver <%s>" msgstr "Kann den Treiber <%s> nicht starten." -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1041,7 +1041,7 @@ msgstr "Kann den Treiber <%s> nicht starten." msgid "Unable to open database <%s> by driver <%s>" msgstr "Kann Datenbank <%s> nicht mit dem Treiber <%s> öffnen." -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1056,17 +1056,17 @@ msgstr "Kann Datenbank <%s> nicht mit dem Treiber <%s> öffnen." msgid "Unable to describe table <%s>" msgstr "Kann Tabelle <%s> nicht beschreiben." -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, c-format msgid "Unable to get attribute data for cat %d" msgstr "Kann die Attribute für cat %d nicht bekommen." -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, c-format msgid "Error while retrieving database record for cat %d" msgstr "Fehler beim Abfragen des Datenbankeintrags für cat %d" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "Kann die Attributtabelle nicht in die Vektorkarte <%s> kopieren." @@ -70489,11 +70489,11 @@ msgstr "" #~ msgstr "Layer-Nummer für Flächen-Karte" #, fuzzy -#~ msgid "Numpy array with vector cats." +#~ msgid "NumPy array with vector cats." #~ msgstr "Keine Flächen in Vektorkarte <%s>" #, fuzzy -#~ msgid "Numpy array with columns names." +#~ msgid "NumPy array with columns names." #~ msgstr "Zu viele Spaltennamen." #, fuzzy diff --git a/locale/po/grassmods_el.po b/locale/po/grassmods_el.po index c285d2d7d10..45b4e6e66e5 100644 --- a/locale/po/grassmods_el.po +++ b/locale/po/grassmods_el.po @@ -258,7 +258,7 @@ msgstr "" "driver: %s\n" "database: %s" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -550,19 +550,19 @@ msgstr "" msgid "raster" msgstr "" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 msgid "My first raster module" msgstr "" -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -616,7 +616,7 @@ msgstr "" msgid "Raster map <%s> not found" msgstr "Ο χάρτης raster <%s> δεν βρέθηκε." -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -801,11 +801,11 @@ msgstr "Ο χάρτης raster <%s> δεν βρέθηκε." msgid "vector" msgstr "" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 msgid "My first vector module" msgstr "" -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -827,12 +827,12 @@ msgstr "" msgid "Vector map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 msgid "Unable to set predetermined vector open level" msgstr "" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -904,7 +904,7 @@ msgstr "" msgid "Unable to open vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -959,7 +959,7 @@ msgstr "" msgid "Unable to create vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -975,7 +975,7 @@ msgstr "" msgid "Database connection not defined for layer %d" msgstr "" -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -989,7 +989,7 @@ msgstr "" msgid "Unable to start driver <%s>" msgstr "Αδυναμία εκκίνησης του οδηγού <%s>" -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1040,7 +1040,7 @@ msgstr "Αδυναμία εκκίνησης του οδηγού <%s>" msgid "Unable to open database <%s> by driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1055,17 +1055,17 @@ msgstr "" msgid "Unable to describe table <%s>" msgstr "Αδυναμία περιγραφής πίνακα <%s>" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, c-format msgid "Unable to get attribute data for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, c-format msgid "Error while retrieving database record for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "" diff --git a/locale/po/grassmods_es.po b/locale/po/grassmods_es.po index 8a0188b6448..dcd927332fa 100644 --- a/locale/po/grassmods_es.po +++ b/locale/po/grassmods_es.po @@ -271,7 +271,7 @@ msgstr "" "driver: %s\n" "base de datos: %s" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -563,19 +563,19 @@ msgstr "" msgid "raster" msgstr "ráster" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "palabra clave 2 " -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "palabra clave 3" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 msgid "My first raster module" msgstr "Mi primer módulo ráster" -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -629,7 +629,7 @@ msgstr "Mi primer módulo ráster" msgid "Raster map <%s> not found" msgstr "Mapa ráster <%s> no encontrado" -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -814,11 +814,11 @@ msgstr "Mapa ráster <%s> no encontrado" msgid "vector" msgstr "vectorial" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 msgid "My first vector module" msgstr "Mi primer módulo vectorial" -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -840,13 +840,13 @@ msgstr "Mi primer módulo vectorial" msgid "Vector map <%s> not found" msgstr "Mapa vectorial <%s> no encontrado" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 msgid "Unable to set predetermined vector open level" msgstr "" "No ha sido posible establecer nivel predeterminado de apertura de vectorial" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -918,7 +918,7 @@ msgstr "" msgid "Unable to open vector map <%s>" msgstr "No se puede abrir el mapa vectorial <%s>" -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -973,7 +973,7 @@ msgstr "No se puede abrir el mapa vectorial <%s>" msgid "Unable to create vector map <%s>" msgstr "No se puede crear el mapa vectorial <%s>" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -989,7 +989,7 @@ msgstr "No se puede crear el mapa vectorial <%s>" msgid "Database connection not defined for layer %d" msgstr "La conexión a la base de datos no ha sido definida para la capa %d" -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -1003,7 +1003,7 @@ msgstr "La conexión a la base de datos no ha sido definida para la capa %d" msgid "Unable to start driver <%s>" msgstr "No se puede iniciar el controlador <%s>" -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1054,7 +1054,7 @@ msgstr "No se puede iniciar el controlador <%s>" msgid "Unable to open database <%s> by driver <%s>" msgstr "No se puede abrir la base de datos <%s> por el controlador <%s>" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1069,17 +1069,17 @@ msgstr "No se puede abrir la base de datos <%s> por el controlador <%s>" msgid "Unable to describe table <%s>" msgstr "No se puede describir la tabla<%s>" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, c-format msgid "Unable to get attribute data for cat %d" msgstr "No ha sido posible obtener datos para categoría %d" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, c-format msgid "Error while retrieving database record for cat %d" msgstr "Error al recuperar registro de base de datos para categoría %d" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "No es posible copiar tabla de atributos al mapa vectorial <%s>" @@ -57136,7 +57136,7 @@ msgstr "categoría del elemento más cercano" #: ../vector/v.distance/main.c:202 msgid "minimum distance to nearest feature" -msgstr "distancia máxima al punto más cercano" +msgstr "distancia mínima al punto más cercano" #: ../vector/v.distance/main.c:203 msgid "x coordinate of the nearest point on the 'to' feature" @@ -72203,11 +72203,11 @@ msgstr "" #~ msgstr "Número o nombre de capa (write to)" #, fuzzy -#~ msgid "Numpy array with vector cats." +#~ msgid "NumPy array with vector cats." #~ msgstr "No hay áreas en mapa vectorial <%s>" #, fuzzy -#~ msgid "Numpy array with columns names." +#~ msgid "NumPy array with columns names." #~ msgstr "Demasiados nombres de columna" #, fuzzy diff --git a/locale/po/grassmods_fi.po b/locale/po/grassmods_fi.po index be1a87cd456..dba5dfc58ea 100644 --- a/locale/po/grassmods_fi.po +++ b/locale/po/grassmods_fi.po @@ -251,7 +251,7 @@ msgid "" "database: %s" msgstr "" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -543,19 +543,19 @@ msgstr "" msgid "raster" msgstr "" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 msgid "My first raster module" msgstr "" -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -609,7 +609,7 @@ msgstr "" msgid "Raster map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -794,11 +794,11 @@ msgstr "" msgid "vector" msgstr "" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 msgid "My first vector module" msgstr "" -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -820,12 +820,12 @@ msgstr "" msgid "Vector map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 msgid "Unable to set predetermined vector open level" msgstr "" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -897,7 +897,7 @@ msgstr "" msgid "Unable to open vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -952,7 +952,7 @@ msgstr "" msgid "Unable to create vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -968,7 +968,7 @@ msgstr "" msgid "Database connection not defined for layer %d" msgstr "" -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -982,7 +982,7 @@ msgstr "" msgid "Unable to start driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1033,7 +1033,7 @@ msgstr "" msgid "Unable to open database <%s> by driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1048,17 +1048,17 @@ msgstr "" msgid "Unable to describe table <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, c-format msgid "Unable to get attribute data for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, c-format msgid "Error while retrieving database record for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "" diff --git a/locale/po/grassmods_fr.po b/locale/po/grassmods_fr.po index 3225a9d712b..ba09f1d912c 100644 --- a/locale/po/grassmods_fr.po +++ b/locale/po/grassmods_fr.po @@ -267,7 +267,7 @@ msgstr "" "pilote : %s\n" "base : %s" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -559,19 +559,19 @@ msgstr "" msgid "raster" msgstr "raster" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 msgid "My first raster module" msgstr "Mon module raster de départ" -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -625,7 +625,7 @@ msgstr "Mon module raster de départ" msgid "Raster map <%s> not found" msgstr "Carte raster <%s> non trouvée" -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -810,11 +810,11 @@ msgstr "Carte raster <%s> non trouvée" msgid "vector" msgstr "vecteur" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 msgid "My first vector module" msgstr "Mon module vecteur de départ." -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -836,12 +836,12 @@ msgstr "Mon module vecteur de départ." msgid "Vector map <%s> not found" msgstr "Carte vecteur <%s> non trouvée" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 msgid "Unable to set predetermined vector open level" msgstr "" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -913,7 +913,7 @@ msgstr "" msgid "Unable to open vector map <%s>" msgstr "Impossible d'ouvrir la carte vecteur <%s>" -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -968,7 +968,7 @@ msgstr "Impossible d'ouvrir la carte vecteur <%s>" msgid "Unable to create vector map <%s>" msgstr "Impossible de créer la carte vecteur <%s>" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -985,7 +985,7 @@ msgid "Database connection not defined for layer %d" msgstr "" "La connexion à la base de données n'a pas été définie pour la couche %d" -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -999,7 +999,7 @@ msgstr "" msgid "Unable to start driver <%s>" msgstr "Impossible de lancer le pilote <%s>" -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1050,7 +1050,7 @@ msgstr "Impossible de lancer le pilote <%s>" msgid "Unable to open database <%s> by driver <%s>" msgstr "Impossible d'ouvrir la base de données <%s> avec le pilote <%s>" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1065,17 +1065,17 @@ msgstr "Impossible d'ouvrir la base de données <%s> avec le pilote <%s>" msgid "Unable to describe table <%s>" msgstr "Impossible de décrire la table <%s>" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, c-format msgid "Unable to get attribute data for cat %d" msgstr "Impossible de sélectionner les attributs pour cat = %d" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, c-format msgid "Error while retrieving database record for cat %d" msgstr "Aucun enregistrement pour la catégorie cat = %d" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "Impossible de copier la table d'attributs vers la carte vecteur <%s>" diff --git a/locale/po/grassmods_hu.po b/locale/po/grassmods_hu.po index 0cad5773fea..1833a1717a2 100644 --- a/locale/po/grassmods_hu.po +++ b/locale/po/grassmods_hu.po @@ -251,7 +251,7 @@ msgid "" "database: %s" msgstr "" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -543,19 +543,19 @@ msgstr "" msgid "raster" msgstr "raszter" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 msgid "My first raster module" msgstr "" -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -609,7 +609,7 @@ msgstr "" msgid "Raster map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -794,11 +794,11 @@ msgstr "" msgid "vector" msgstr "" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 msgid "My first vector module" msgstr "" -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -820,12 +820,12 @@ msgstr "" msgid "Vector map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 msgid "Unable to set predetermined vector open level" msgstr "" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -897,7 +897,7 @@ msgstr "" msgid "Unable to open vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -952,7 +952,7 @@ msgstr "" msgid "Unable to create vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -968,7 +968,7 @@ msgstr "" msgid "Database connection not defined for layer %d" msgstr "" -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -982,7 +982,7 @@ msgstr "" msgid "Unable to start driver <%s>" msgstr "Nem tudom elindítani a <%s> meghajtót" -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1033,7 +1033,7 @@ msgstr "Nem tudom elindítani a <%s> meghajtót" msgid "Unable to open database <%s> by driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1048,17 +1048,17 @@ msgstr "" msgid "Unable to describe table <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, c-format msgid "Unable to get attribute data for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, c-format msgid "Error while retrieving database record for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "" diff --git a/locale/po/grassmods_id_ID.po b/locale/po/grassmods_id_ID.po index df950915b12..ab373e47cd5 100644 --- a/locale/po/grassmods_id_ID.po +++ b/locale/po/grassmods_id_ID.po @@ -251,7 +251,7 @@ msgid "" "database: %s" msgstr "" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -543,19 +543,19 @@ msgstr "" msgid "raster" msgstr "" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 msgid "My first raster module" msgstr "" -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -609,7 +609,7 @@ msgstr "" msgid "Raster map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -794,11 +794,11 @@ msgstr "" msgid "vector" msgstr "" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 msgid "My first vector module" msgstr "" -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -820,12 +820,12 @@ msgstr "" msgid "Vector map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 msgid "Unable to set predetermined vector open level" msgstr "" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -897,7 +897,7 @@ msgstr "" msgid "Unable to open vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -952,7 +952,7 @@ msgstr "" msgid "Unable to create vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -968,7 +968,7 @@ msgstr "" msgid "Database connection not defined for layer %d" msgstr "" -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -982,7 +982,7 @@ msgstr "" msgid "Unable to start driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1033,7 +1033,7 @@ msgstr "" msgid "Unable to open database <%s> by driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1048,17 +1048,17 @@ msgstr "" msgid "Unable to describe table <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, c-format msgid "Unable to get attribute data for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, c-format msgid "Error while retrieving database record for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "" diff --git a/locale/po/grassmods_it.po b/locale/po/grassmods_it.po index 53af5452a54..2ee80f9be6d 100644 --- a/locale/po/grassmods_it.po +++ b/locale/po/grassmods_it.po @@ -265,7 +265,7 @@ msgstr "" "driver: %s\n" "database: %s" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -557,19 +557,19 @@ msgstr "" msgid "raster" msgstr "raster" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 msgid "My first raster module" msgstr "Il mio primo modulo raster" -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -623,7 +623,7 @@ msgstr "Il mio primo modulo raster" msgid "Raster map <%s> not found" msgstr "Mappa raster <%s> non trovata" -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -808,11 +808,11 @@ msgstr "Mappa raster <%s> non trovata" msgid "vector" msgstr "vettoriale" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 msgid "My first vector module" msgstr "Il mio primo modulo vettoriale" -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -834,12 +834,12 @@ msgstr "Il mio primo modulo vettoriale" msgid "Vector map <%s> not found" msgstr "Mappa vettoriale <%s> non trovata" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 msgid "Unable to set predetermined vector open level" msgstr "Impossibile impostare il predeterminato livello del vettoriale aperto" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -911,7 +911,7 @@ msgstr "Impossibile impostare il predeterminato livello del vettoriale aperto" msgid "Unable to open vector map <%s>" msgstr "Impossibile aprire la mappa vettoriale <%s>" -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -966,7 +966,7 @@ msgstr "Impossibile aprire la mappa vettoriale <%s>" msgid "Unable to create vector map <%s>" msgstr "Non è possibile creare la mappa vettoriale <%s>" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -982,7 +982,7 @@ msgstr "Non è possibile creare la mappa vettoriale <%s>" msgid "Database connection not defined for layer %d" msgstr "Connessione al database non definita per il layer %d" -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -996,7 +996,7 @@ msgstr "Connessione al database non definita per il layer %d" msgid "Unable to start driver <%s>" msgstr "Impossibile avviare il driver <%s>" -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1047,7 +1047,7 @@ msgstr "Impossibile avviare il driver <%s>" msgid "Unable to open database <%s> by driver <%s>" msgstr "Impossibile aprire il database <%s> col driver <%s>" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1062,17 +1062,17 @@ msgstr "Impossibile aprire il database <%s> col driver <%s>" msgid "Unable to describe table <%s>" msgstr "Impossibile descrivere la tabella <%s>" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, c-format msgid "Unable to get attribute data for cat %d" msgstr "Impossibile ottenere gli attributi per la categoria %d" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, c-format msgid "Error while retrieving database record for cat %d" msgstr "Errore ricevendo il record del database per la categoria %d" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "" @@ -69850,7 +69850,7 @@ msgstr "" #~ msgstr "Numero o nome del layer 'to'" #, fuzzy -#~ msgid "Numpy array with columns names." +#~ msgid "NumPy array with columns names." #~ msgstr "Troppi nomi di colonna" #, fuzzy diff --git a/locale/po/grassmods_ja.po b/locale/po/grassmods_ja.po index c4aa7ebd561..236287ce5e5 100644 --- a/locale/po/grassmods_ja.po +++ b/locale/po/grassmods_ja.po @@ -259,7 +259,7 @@ msgstr "" "ドライバー: %s\n" "データベース: %s" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -551,19 +551,19 @@ msgstr "" msgid "raster" msgstr "ラスター" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 msgid "My first raster module" msgstr "自分の最初のラスターモジュール" -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -617,7 +617,7 @@ msgstr "自分の最初のラスターモジュール" msgid "Raster map <%s> not found" msgstr "ラスタマップ <%s> が見つかりません" -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -802,11 +802,11 @@ msgstr "ラスタマップ <%s> が見つかりません" msgid "vector" msgstr "ベクトル" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 msgid "My first vector module" msgstr "自分の最初のベクトルモジュール" -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -828,12 +828,12 @@ msgstr "自分の最初のベクトルモジュール" msgid "Vector map <%s> not found" msgstr "ベクトルマップ <%s> が見つかりません" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 msgid "Unable to set predetermined vector open level" msgstr "既設のベクトル オープン レベルを設定できません" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -905,7 +905,7 @@ msgstr "既設のベクトル オープン レベルを設定できません" msgid "Unable to open vector map <%s>" msgstr "ベクトルマップ <%s> を開けません" -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -960,7 +960,7 @@ msgstr "ベクトルマップ <%s> を開けません" msgid "Unable to create vector map <%s>" msgstr "ベクトルマップ <%s> を作成できません" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -976,7 +976,7 @@ msgstr "ベクトルマップ <%s> を作成できません" msgid "Database connection not defined for layer %d" msgstr "データベース接続はレイヤー %d に定義されていません" -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -990,7 +990,7 @@ msgstr "データベース接続はレイヤー %d に定義されていませ msgid "Unable to start driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1041,7 +1041,7 @@ msgstr "" msgid "Unable to open database <%s> by driver <%s>" msgstr "ドライバー <%s> でデータベース <%s> を開けません" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1056,17 +1056,17 @@ msgstr "ドライバー <%s> でデータベース <%s> を開けません" msgid "Unable to describe table <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, c-format msgid "Unable to get attribute data for cat %d" msgstr "カテゴリー %d の属性データを取得できません" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, c-format msgid "Error while retrieving database record for cat %d" msgstr "カテゴリー %d のデータベース レコード取得中にエラー" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "属性テーブルをベクトルマップ <%s> へコピーできません" @@ -68931,7 +68931,7 @@ msgstr "" #~ msgstr "レイヤー数 (from)" #, fuzzy -#~ msgid "Numpy array with columns names." +#~ msgid "NumPy array with columns names." #~ msgstr "列名が多すぎます " #, fuzzy diff --git a/locale/po/grassmods_ko.po b/locale/po/grassmods_ko.po index 09064eb9df8..ac9b25d1df9 100644 --- a/locale/po/grassmods_ko.po +++ b/locale/po/grassmods_ko.po @@ -250,7 +250,7 @@ msgid "" "database: %s" msgstr "" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -542,19 +542,19 @@ msgstr "" msgid "raster" msgstr "래스터" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 msgid "My first raster module" msgstr "" -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -608,7 +608,7 @@ msgstr "" msgid "Raster map <%s> not found" msgstr "래스터 지도 <%s>를 찾을 수 없습니다" -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -793,11 +793,11 @@ msgstr "래스터 지도 <%s>를 찾을 수 없습니다" msgid "vector" msgstr "벡터" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 msgid "My first vector module" msgstr "" -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -819,12 +819,12 @@ msgstr "" msgid "Vector map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 msgid "Unable to set predetermined vector open level" msgstr "미리 결정된 벡터 열기 단계를 설정할 수 없습니다" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -896,7 +896,7 @@ msgstr "미리 결정된 벡터 열기 단계를 설정할 수 없습니다" msgid "Unable to open vector map <%s>" msgstr "벡터 지도 <%s>를 열 수 없습니다" -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -951,7 +951,7 @@ msgstr "벡터 지도 <%s>를 열 수 없습니다" msgid "Unable to create vector map <%s>" msgstr "벡터 지도 <%s>를 생성할 수 없습니다" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -967,7 +967,7 @@ msgstr "벡터 지도 <%s>를 생성할 수 없습니다" msgid "Database connection not defined for layer %d" msgstr "레이어 %d를 위한 데이터베이스 연결이 정의되지 않았습니다" -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -981,7 +981,7 @@ msgstr "레이어 %d를 위한 데이터베이스 연결이 정의되지 않았 msgid "Unable to start driver <%s>" msgstr "드라이버 <%s>를 시작할 수 없습니다" -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1032,7 +1032,7 @@ msgstr "드라이버 <%s>를 시작할 수 없습니다" msgid "Unable to open database <%s> by driver <%s>" msgstr "데이터베이스 <%s>를 드라이버 <%s>로 열 수 없습니다" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1047,17 +1047,17 @@ msgstr "데이터베이스 <%s>를 드라이버 <%s>로 열 수 없습니다" msgid "Unable to describe table <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, c-format msgid "Unable to get attribute data for cat %d" msgstr "cat %d를 위한 속성 자료를 얻을 수 없습니다" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, c-format msgid "Error while retrieving database record for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "속성 테이블을 벡터 지도 <%s>로 복사할 수 없습니다" diff --git a/locale/po/grassmods_lv.po b/locale/po/grassmods_lv.po index cd5cf65275a..2fb56f36516 100644 --- a/locale/po/grassmods_lv.po +++ b/locale/po/grassmods_lv.po @@ -254,7 +254,7 @@ msgid "" "database: %s" msgstr "" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -546,19 +546,19 @@ msgstr "" msgid "raster" msgstr "rastrs" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 msgid "My first raster module" msgstr "" -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -612,7 +612,7 @@ msgstr "" msgid "Raster map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -797,11 +797,11 @@ msgstr "" msgid "vector" msgstr "vektors" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 msgid "My first vector module" msgstr "" -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -823,12 +823,12 @@ msgstr "" msgid "Vector map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 msgid "Unable to set predetermined vector open level" msgstr "" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -900,7 +900,7 @@ msgstr "" msgid "Unable to open vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -955,7 +955,7 @@ msgstr "" msgid "Unable to create vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -971,7 +971,7 @@ msgstr "" msgid "Database connection not defined for layer %d" msgstr "" -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -985,7 +985,7 @@ msgstr "" msgid "Unable to start driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1036,7 +1036,7 @@ msgstr "" msgid "Unable to open database <%s> by driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1051,17 +1051,17 @@ msgstr "" msgid "Unable to describe table <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, fuzzy, c-format msgid "Unable to get attribute data for cat %d" msgstr "Nevar iatlasīt datus no tabulas" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, c-format msgid "Error while retrieving database record for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "" @@ -68905,7 +68905,7 @@ msgstr "Izejas rastra kartes nosaukums" #~ msgstr "Slāņa numurs vai nosaukums" #, fuzzy -#~ msgid "Numpy array with columns names." +#~ msgid "NumPy array with columns names." #~ msgstr "Atslēga (id) kolonas nosaukums" #, fuzzy diff --git a/locale/po/grassmods_ml.po b/locale/po/grassmods_ml.po index cfe442a427e..e03bdff269f 100644 --- a/locale/po/grassmods_ml.po +++ b/locale/po/grassmods_ml.po @@ -251,7 +251,7 @@ msgid "" "database: %s" msgstr "" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -543,19 +543,19 @@ msgstr "" msgid "raster" msgstr "" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 msgid "My first raster module" msgstr "" -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -609,7 +609,7 @@ msgstr "" msgid "Raster map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -794,11 +794,11 @@ msgstr "" msgid "vector" msgstr "" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 msgid "My first vector module" msgstr "" -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -820,12 +820,12 @@ msgstr "" msgid "Vector map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 msgid "Unable to set predetermined vector open level" msgstr "" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -897,7 +897,7 @@ msgstr "" msgid "Unable to open vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -952,7 +952,7 @@ msgstr "" msgid "Unable to create vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -968,7 +968,7 @@ msgstr "" msgid "Database connection not defined for layer %d" msgstr "" -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -982,7 +982,7 @@ msgstr "" msgid "Unable to start driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1033,7 +1033,7 @@ msgstr "" msgid "Unable to open database <%s> by driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1048,17 +1048,17 @@ msgstr "" msgid "Unable to describe table <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, c-format msgid "Unable to get attribute data for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, c-format msgid "Error while retrieving database record for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "" diff --git a/locale/po/grassmods_pl.po b/locale/po/grassmods_pl.po index ce0193ce676..7d966975e22 100644 --- a/locale/po/grassmods_pl.po +++ b/locale/po/grassmods_pl.po @@ -257,7 +257,7 @@ msgstr "" "sterownik: %s\n" "baza danych: %s" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -549,19 +549,19 @@ msgstr "" msgid "raster" msgstr "raster" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 msgid "My first raster module" msgstr "Mój pierwszy moduł rastrowy" -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -615,7 +615,7 @@ msgstr "Mój pierwszy moduł rastrowy" msgid "Raster map <%s> not found" msgstr "Mapa rastrowa <%s> nie została znaleziona" -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -800,11 +800,11 @@ msgstr "Mapa rastrowa <%s> nie została znaleziona" msgid "vector" msgstr "wektor" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 msgid "My first vector module" msgstr "Mój pierwszy moduł wektorowy" -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -826,12 +826,12 @@ msgstr "Mój pierwszy moduł wektorowy" msgid "Vector map <%s> not found" msgstr "Nie znaleziono mapy wektorowej <%s>" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 msgid "Unable to set predetermined vector open level" msgstr "" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -903,7 +903,7 @@ msgstr "" msgid "Unable to open vector map <%s>" msgstr "Nie można otworzyć mapy wektorowej <%s>" -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -958,7 +958,7 @@ msgstr "Nie można otworzyć mapy wektorowej <%s>" msgid "Unable to create vector map <%s>" msgstr "Nie można utworzyć mapy wektorowej <%s>" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -974,7 +974,7 @@ msgstr "Nie można utworzyć mapy wektorowej <%s>" msgid "Database connection not defined for layer %d" msgstr "Połączenie z bazą danych nie zostało zdefiniowane dla warstwy %d" -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -988,7 +988,7 @@ msgstr "Połączenie z bazą danych nie zostało zdefiniowane dla warstwy %d" msgid "Unable to start driver <%s>" msgstr "Nie można uruchomić sterownika <%s>" -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1039,7 +1039,7 @@ msgstr "Nie można uruchomić sterownika <%s>" msgid "Unable to open database <%s> by driver <%s>" msgstr "Nie można otworzyć bazy danych <%s> za pomocą sterownika <%s>" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1054,17 +1054,17 @@ msgstr "Nie można otworzyć bazy danych <%s> za pomocą sterownika <%s>" msgid "Unable to describe table <%s>" msgstr "Nie można opisać tabeli <%s>" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, c-format msgid "Unable to get attribute data for cat %d" msgstr "Nie można wybrać atrybutów dla cat %d" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, c-format msgid "Error while retrieving database record for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "Nie można skopiować tabeli atrybutów do mapy wektorowej <%s>" @@ -69282,7 +69282,7 @@ msgstr "" #~ msgstr "Liczba warstw (na)" #, fuzzy -#~ msgid "Numpy array with columns names." +#~ msgid "NumPy array with columns names." #~ msgstr "Zbyt dużo nazw kolmun" #, fuzzy diff --git a/locale/po/grassmods_pt.po b/locale/po/grassmods_pt.po index a22f945601b..8f06bf66795 100644 --- a/locale/po/grassmods_pt.po +++ b/locale/po/grassmods_pt.po @@ -255,7 +255,7 @@ msgid "" "database: %s" msgstr "" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -547,19 +547,19 @@ msgstr "" msgid "raster" msgstr "" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 msgid "My first raster module" msgstr "" -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -613,7 +613,7 @@ msgstr "" msgid "Raster map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -798,11 +798,11 @@ msgstr "" msgid "vector" msgstr "" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 msgid "My first vector module" msgstr "" -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -824,12 +824,12 @@ msgstr "" msgid "Vector map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 msgid "Unable to set predetermined vector open level" msgstr "" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -901,7 +901,7 @@ msgstr "" msgid "Unable to open vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -956,7 +956,7 @@ msgstr "" msgid "Unable to create vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -972,7 +972,7 @@ msgstr "" msgid "Database connection not defined for layer %d" msgstr "" -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -986,7 +986,7 @@ msgstr "" msgid "Unable to start driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1037,7 +1037,7 @@ msgstr "" msgid "Unable to open database <%s> by driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1052,17 +1052,17 @@ msgstr "" msgid "Unable to describe table <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, c-format msgid "Unable to get attribute data for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, c-format msgid "Error while retrieving database record for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "" diff --git a/locale/po/grassmods_pt_BR.po b/locale/po/grassmods_pt_BR.po index a40e9100d63..285fc19148c 100644 --- a/locale/po/grassmods_pt_BR.po +++ b/locale/po/grassmods_pt_BR.po @@ -251,7 +251,7 @@ msgid "" "database: %s" msgstr "" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -543,19 +543,19 @@ msgstr "" msgid "raster" msgstr "raster" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "palavra-chave2" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "palavra-chave3" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 msgid "My first raster module" msgstr "Meu primeiro módulo raster" -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -609,7 +609,7 @@ msgstr "Meu primeiro módulo raster" msgid "Raster map <%s> not found" msgstr "Mapa raster <%s> não encontrado" -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -794,11 +794,11 @@ msgstr "Mapa raster <%s> não encontrado" msgid "vector" msgstr "vetor" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 msgid "My first vector module" msgstr "Meu primeiro módulo vetorial" -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -820,12 +820,12 @@ msgstr "Meu primeiro módulo vetorial" msgid "Vector map <%s> not found" msgstr "Mapa vetorial <%s> não encontrado" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 msgid "Unable to set predetermined vector open level" msgstr "Não foi possível definir o nível vetorial aberto pré-determinado" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -897,7 +897,7 @@ msgstr "Não foi possível definir o nível vetorial aberto pré-determinado" msgid "Unable to open vector map <%s>" msgstr "Não foi possível abrir mapa vetorial <%s>" -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -952,7 +952,7 @@ msgstr "Não foi possível abrir mapa vetorial <%s>" msgid "Unable to create vector map <%s>" msgstr "Não foi possível criar mapa vetorial <%s>" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -968,7 +968,7 @@ msgstr "Não foi possível criar mapa vetorial <%s>" msgid "Database connection not defined for layer %d" msgstr "Conexão do banco de dados não definida para camada %d" -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -982,7 +982,7 @@ msgstr "Conexão do banco de dados não definida para camada %d" msgid "Unable to start driver <%s>" msgstr "Não foi possível iniciar o driver <%s>" -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1033,7 +1033,7 @@ msgstr "Não foi possível iniciar o driver <%s>" msgid "Unable to open database <%s> by driver <%s>" msgstr "Não foi possível abrir banco de dados <%s> com driver <%s>" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1048,17 +1048,17 @@ msgstr "Não foi possível abrir banco de dados <%s> com driver <%s>" msgid "Unable to describe table <%s>" msgstr "Não foi possível descrever a tabela <%s>" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, c-format msgid "Unable to get attribute data for cat %d" msgstr "Não foi possível obter dados de atributo para categoria %d" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, c-format msgid "Error while retrieving database record for cat %d" msgstr "Erro ao recuperar o registro do banco de dados para categoria %d" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "" diff --git a/locale/po/grassmods_ro.po b/locale/po/grassmods_ro.po index 4f49bec89f3..381049e994b 100644 --- a/locale/po/grassmods_ro.po +++ b/locale/po/grassmods_ro.po @@ -255,7 +255,7 @@ msgstr "" "driver: %s\n" "baza de date: %s" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -547,19 +547,19 @@ msgstr "" msgid "raster" msgstr "raster" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "cuvânt cheie 2" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "cuvânt cheie 3" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 msgid "My first raster module" msgstr "" -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -613,7 +613,7 @@ msgstr "" msgid "Raster map <%s> not found" msgstr "Harta raster <%s> nu este găsită" -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -798,11 +798,11 @@ msgstr "Harta raster <%s> nu este găsită" msgid "vector" msgstr "vector" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 msgid "My first vector module" msgstr "" -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -824,12 +824,12 @@ msgstr "" msgid "Vector map <%s> not found" msgstr "Harta vectorială <%s> nu a fost găsită" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 msgid "Unable to set predetermined vector open level" msgstr "" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -901,7 +901,7 @@ msgstr "" msgid "Unable to open vector map <%s>" msgstr "Imposibil de deschis harta vectorială <%s>" -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -956,7 +956,7 @@ msgstr "Imposibil de deschis harta vectorială <%s>" msgid "Unable to create vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -972,7 +972,7 @@ msgstr "" msgid "Database connection not defined for layer %d" msgstr "Conexiunea bazei de date nu este definită pentru stratul %d" -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -986,7 +986,7 @@ msgstr "Conexiunea bazei de date nu este definită pentru stratul %d" msgid "Unable to start driver <%s>" msgstr "Imposibil de pornit driver-ul<%s>" -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1037,7 +1037,7 @@ msgstr "Imposibil de pornit driver-ul<%s>" msgid "Unable to open database <%s> by driver <%s>" msgstr "Imposibil de deschis baza de date <%s> cu driverl <%s>" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1052,17 +1052,17 @@ msgstr "Imposibil de deschis baza de date <%s> cu driverl <%s>" msgid "Unable to describe table <%s>" msgstr "Nu s-a putut descrie tabelul<%s>" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, c-format msgid "Unable to get attribute data for cat %d" msgstr "Imposibil de obținut datele atribut pentru cat %d" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, c-format msgid "Error while retrieving database record for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "Imposibil de copiat tabela de atribute la harta vectorială <%s>" diff --git a/locale/po/grassmods_ru.po b/locale/po/grassmods_ru.po index 87d6bbad54e..35bc5c306c4 100644 --- a/locale/po/grassmods_ru.po +++ b/locale/po/grassmods_ru.po @@ -255,7 +255,7 @@ msgstr "" "драйвер: %s\n" "база данных: %s" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -547,19 +547,19 @@ msgstr "" msgid "raster" msgstr "" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 msgid "My first raster module" msgstr "" -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -613,7 +613,7 @@ msgstr "" msgid "Raster map <%s> not found" msgstr "Растровая карта <%s> не найдена" -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -798,11 +798,11 @@ msgstr "Растровая карта <%s> не найдена" msgid "vector" msgstr "" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 msgid "My first vector module" msgstr "" -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -824,12 +824,12 @@ msgstr "" msgid "Vector map <%s> not found" msgstr "Векторная карта <%s> не найдена" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 msgid "Unable to set predetermined vector open level" msgstr "" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -901,7 +901,7 @@ msgstr "" msgid "Unable to open vector map <%s>" msgstr "Не удалось открыть векторную карту <%s>" -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -956,7 +956,7 @@ msgstr "Не удалось открыть векторную карту <%s>" msgid "Unable to create vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -972,7 +972,7 @@ msgstr "" msgid "Database connection not defined for layer %d" msgstr "Не определено соединение с базой данных для слоя %d" -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -986,7 +986,7 @@ msgstr "Не определено соединение с базой данны msgid "Unable to start driver <%s>" msgstr "Не удалось запустить драйвер <%s>" -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1037,7 +1037,7 @@ msgstr "Не удалось запустить драйвер <%s>" msgid "Unable to open database <%s> by driver <%s>" msgstr "Не удалось открыть базу данных <%s> с помощью драйвера <%s>" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1052,17 +1052,17 @@ msgstr "Не удалось открыть базу данных <%s> с пом msgid "Unable to describe table <%s>" msgstr "Невозможно описать таблицу <%s>" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, c-format msgid "Unable to get attribute data for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, c-format msgid "Error while retrieving database record for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "" diff --git a/locale/po/grassmods_si.po b/locale/po/grassmods_si.po index 2acdb89fbfe..d6aa6cebc6c 100644 --- a/locale/po/grassmods_si.po +++ b/locale/po/grassmods_si.po @@ -251,7 +251,7 @@ msgid "" "database: %s" msgstr "" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -543,19 +543,19 @@ msgstr "" msgid "raster" msgstr "" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 msgid "My first raster module" msgstr "" -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -609,7 +609,7 @@ msgstr "" msgid "Raster map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -794,11 +794,11 @@ msgstr "" msgid "vector" msgstr "" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 msgid "My first vector module" msgstr "" -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -820,12 +820,12 @@ msgstr "" msgid "Vector map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 msgid "Unable to set predetermined vector open level" msgstr "" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -897,7 +897,7 @@ msgstr "" msgid "Unable to open vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -952,7 +952,7 @@ msgstr "" msgid "Unable to create vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -968,7 +968,7 @@ msgstr "" msgid "Database connection not defined for layer %d" msgstr "" -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -982,7 +982,7 @@ msgstr "" msgid "Unable to start driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1033,7 +1033,7 @@ msgstr "" msgid "Unable to open database <%s> by driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1048,17 +1048,17 @@ msgstr "" msgid "Unable to describe table <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, c-format msgid "Unable to get attribute data for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, c-format msgid "Error while retrieving database record for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "" diff --git a/locale/po/grassmods_sl.po b/locale/po/grassmods_sl.po index c1f78896be4..981de091d76 100644 --- a/locale/po/grassmods_sl.po +++ b/locale/po/grassmods_sl.po @@ -264,7 +264,7 @@ msgid "" "database: %s" msgstr "" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -556,20 +556,20 @@ msgstr "" msgid "raster" msgstr "" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 #, fuzzy msgid "My first raster module" msgstr "Ime obstoječega rastrskega sloja." -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -623,7 +623,7 @@ msgstr "Ime obstoječega rastrskega sloja." msgid "Raster map <%s> not found" msgstr "Ne najdem vhodnega rastrskega sloja <%s>." -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -808,12 +808,12 @@ msgstr "Ne najdem vhodnega rastrskega sloja <%s>." msgid "vector" msgstr "" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 #, fuzzy msgid "My first vector module" msgstr "Ime obstoječega rastrskega sloja." -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -835,13 +835,13 @@ msgstr "Ime obstoječega rastrskega sloja." msgid "Vector map <%s> not found" msgstr "Ne najdem vhodnega vektorja <%s>" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 #, fuzzy msgid "Unable to set predetermined vector open level" msgstr "Ime izhodnega vektorskega točkovnega sloja" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -913,7 +913,7 @@ msgstr "Ime izhodnega vektorskega točkovnega sloja" msgid "Unable to open vector map <%s>" msgstr "Ime izhodnega rastrskega sloja" -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -968,7 +968,7 @@ msgstr "Ime izhodnega rastrskega sloja" msgid "Unable to create vector map <%s>" msgstr "Ime izhodnega rastrskega sloja" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -984,7 +984,7 @@ msgstr "Ime izhodnega rastrskega sloja" msgid "Database connection not defined for layer %d" msgstr "Povezava z bazo podatkov ni definirana\n" -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -998,7 +998,7 @@ msgstr "Povezava z bazo podatkov ni definirana\n" msgid "Unable to start driver <%s>" msgstr "Ne morem zagnati gonilnika '%s'." -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1049,7 +1049,7 @@ msgstr "Ne morem zagnati gonilnika '%s'." msgid "Unable to open database <%s> by driver <%s>" msgstr "Ne morem odpreti baze podatkov %s z gonilnikom %s" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1064,17 +1064,17 @@ msgstr "Ne morem odpreti baze podatkov %s z gonilnikom %s" msgid "Unable to describe table <%s>" msgstr "Ne morem opisati tabele" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, fuzzy, c-format msgid "Unable to get attribute data for cat %d" msgstr "Ne morem izbrati (select) podatkov iz tabele" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, fuzzy, c-format msgid "Error while retrieving database record for cat %d" msgstr "Za vrstico (cat = %d) ni zapisa (record)<" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, fuzzy, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "Ime izhodnega rastrskega sloja" @@ -73421,11 +73421,11 @@ msgstr "Ime izhodnega rastrskega sloja" #~ msgstr "Vhodni sloj" #, fuzzy -#~ msgid "Numpy array with vector cats." +#~ msgid "NumPy array with vector cats." #~ msgstr "%d točk je bilo zapisanih na izhodno datoteko\n" #, fuzzy -#~ msgid "Numpy array with columns names." +#~ msgid "NumPy array with columns names." #~ msgstr "Ime rastrskega sloja" #, fuzzy diff --git a/locale/po/grassmods_ta.po b/locale/po/grassmods_ta.po index 05719977a92..2cc76db5743 100644 --- a/locale/po/grassmods_ta.po +++ b/locale/po/grassmods_ta.po @@ -252,7 +252,7 @@ msgid "" "database: %s" msgstr "" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -544,19 +544,19 @@ msgstr "" msgid "raster" msgstr "" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 msgid "My first raster module" msgstr "" -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -610,7 +610,7 @@ msgstr "" msgid "Raster map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -795,11 +795,11 @@ msgstr "" msgid "vector" msgstr "" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 msgid "My first vector module" msgstr "" -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -821,12 +821,12 @@ msgstr "" msgid "Vector map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 msgid "Unable to set predetermined vector open level" msgstr "" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -898,7 +898,7 @@ msgstr "" msgid "Unable to open vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -953,7 +953,7 @@ msgstr "" msgid "Unable to create vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -969,7 +969,7 @@ msgstr "" msgid "Database connection not defined for layer %d" msgstr "" -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -983,7 +983,7 @@ msgstr "" msgid "Unable to start driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1034,7 +1034,7 @@ msgstr "" msgid "Unable to open database <%s> by driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1049,17 +1049,17 @@ msgstr "" msgid "Unable to describe table <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, c-format msgid "Unable to get attribute data for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, c-format msgid "Error while retrieving database record for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "" diff --git a/locale/po/grassmods_th.po b/locale/po/grassmods_th.po index 65e557c85d9..4f763f25733 100644 --- a/locale/po/grassmods_th.po +++ b/locale/po/grassmods_th.po @@ -252,7 +252,7 @@ msgid "" "database: %s" msgstr "" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -544,19 +544,19 @@ msgstr "" msgid "raster" msgstr "" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 msgid "My first raster module" msgstr "" -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -610,7 +610,7 @@ msgstr "" msgid "Raster map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -795,11 +795,11 @@ msgstr "" msgid "vector" msgstr "" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 msgid "My first vector module" msgstr "" -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -821,12 +821,12 @@ msgstr "" msgid "Vector map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 msgid "Unable to set predetermined vector open level" msgstr "" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -898,7 +898,7 @@ msgstr "" msgid "Unable to open vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -953,7 +953,7 @@ msgstr "" msgid "Unable to create vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -969,7 +969,7 @@ msgstr "" msgid "Database connection not defined for layer %d" msgstr "" -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -983,7 +983,7 @@ msgstr "" msgid "Unable to start driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1034,7 +1034,7 @@ msgstr "" msgid "Unable to open database <%s> by driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1049,17 +1049,17 @@ msgstr "" msgid "Unable to describe table <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, c-format msgid "Unable to get attribute data for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, c-format msgid "Error while retrieving database record for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "" diff --git a/locale/po/grassmods_tr.po b/locale/po/grassmods_tr.po index ecb42b13505..3ca146dfced 100644 --- a/locale/po/grassmods_tr.po +++ b/locale/po/grassmods_tr.po @@ -254,7 +254,7 @@ msgid "" "database: %s" msgstr "" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -546,19 +546,19 @@ msgstr "" msgid "raster" msgstr "raster" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 msgid "My first raster module" msgstr "İlk raster modülüm" -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -612,7 +612,7 @@ msgstr "İlk raster modülüm" msgid "Raster map <%s> not found" msgstr "<%s> raster haritası bulunamadı" -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -797,11 +797,11 @@ msgstr "<%s> raster haritası bulunamadı" msgid "vector" msgstr "vektör" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 msgid "My first vector module" msgstr "İlk vektör modülüm" -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -823,12 +823,12 @@ msgstr "İlk vektör modülüm" msgid "Vector map <%s> not found" msgstr "<%s> vektör haritası bulunamadı" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 msgid "Unable to set predetermined vector open level" msgstr "Önceden belirlenen vektör açma katmanı ayarlanamıyor" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -900,7 +900,7 @@ msgstr "Önceden belirlenen vektör açma katmanı ayarlanamıyor" msgid "Unable to open vector map <%s>" msgstr "<%s> vektör haritası açılamıyor" -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -955,7 +955,7 @@ msgstr "<%s> vektör haritası açılamıyor" msgid "Unable to create vector map <%s>" msgstr "<%s> vektör haritası oluşturulamıyor" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -971,7 +971,7 @@ msgstr "<%s> vektör haritası oluşturulamıyor" msgid "Database connection not defined for layer %d" msgstr "%d katmanının veritabanı bağlantısı belirlenmedi" -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -985,7 +985,7 @@ msgstr "%d katmanının veritabanı bağlantısı belirlenmedi" msgid "Unable to start driver <%s>" msgstr "Sürücü başlatılamıyor<%s>" -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1036,7 +1036,7 @@ msgstr "Sürücü başlatılamıyor<%s>" msgid "Unable to open database <%s> by driver <%s>" msgstr "<%s> sürücüsüyle <%s> veritabanı açılamıyor" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1051,17 +1051,17 @@ msgstr "<%s> sürücüsüyle <%s> veritabanı açılamıyor" msgid "Unable to describe table <%s>" msgstr "<%s> tablosu tanımlanamıyor" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, c-format msgid "Unable to get attribute data for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, c-format msgid "Error while retrieving database record for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "" @@ -68743,7 +68743,7 @@ msgstr "" #~ msgstr "Katman numarası" #, fuzzy -#~ msgid "Numpy array with columns names." +#~ msgid "NumPy array with columns names." #~ msgstr "Çok fazla sütun adı" #, fuzzy diff --git a/locale/po/grassmods_uk.po b/locale/po/grassmods_uk.po index 65b9178e653..7403997e3ba 100644 --- a/locale/po/grassmods_uk.po +++ b/locale/po/grassmods_uk.po @@ -252,7 +252,7 @@ msgid "" "database: %s" msgstr "" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -544,19 +544,19 @@ msgstr "" msgid "raster" msgstr "" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 msgid "My first raster module" msgstr "" -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -610,7 +610,7 @@ msgstr "" msgid "Raster map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -795,11 +795,11 @@ msgstr "" msgid "vector" msgstr "" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 msgid "My first vector module" msgstr "" -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -821,12 +821,12 @@ msgstr "" msgid "Vector map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 msgid "Unable to set predetermined vector open level" msgstr "" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -898,7 +898,7 @@ msgstr "" msgid "Unable to open vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -953,7 +953,7 @@ msgstr "" msgid "Unable to create vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -969,7 +969,7 @@ msgstr "" msgid "Database connection not defined for layer %d" msgstr "" -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -983,7 +983,7 @@ msgstr "" msgid "Unable to start driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1034,7 +1034,7 @@ msgstr "" msgid "Unable to open database <%s> by driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1049,17 +1049,17 @@ msgstr "" msgid "Unable to describe table <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, c-format msgid "Unable to get attribute data for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, c-format msgid "Error while retrieving database record for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "" diff --git a/locale/po/grassmods_vi.po b/locale/po/grassmods_vi.po index 49b0683b187..7002874a944 100644 --- a/locale/po/grassmods_vi.po +++ b/locale/po/grassmods_vi.po @@ -252,7 +252,7 @@ msgid "" "database: %s" msgstr "" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -544,19 +544,19 @@ msgstr "" msgid "raster" msgstr "" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 msgid "My first raster module" msgstr "" -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -610,7 +610,7 @@ msgstr "" msgid "Raster map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -795,11 +795,11 @@ msgstr "" msgid "vector" msgstr "" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 msgid "My first vector module" msgstr "" -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -821,12 +821,12 @@ msgstr "" msgid "Vector map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 msgid "Unable to set predetermined vector open level" msgstr "" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -898,7 +898,7 @@ msgstr "" msgid "Unable to open vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -953,7 +953,7 @@ msgstr "" msgid "Unable to create vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -969,7 +969,7 @@ msgstr "" msgid "Database connection not defined for layer %d" msgstr "" -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -983,7 +983,7 @@ msgstr "" msgid "Unable to start driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1034,7 +1034,7 @@ msgstr "" msgid "Unable to open database <%s> by driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1049,17 +1049,17 @@ msgstr "" msgid "Unable to describe table <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, c-format msgid "Unable to get attribute data for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, c-format msgid "Error while retrieving database record for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "" diff --git a/locale/po/grassmods_zh.po b/locale/po/grassmods_zh.po index cccf638e84a..0be17badee4 100644 --- a/locale/po/grassmods_zh.po +++ b/locale/po/grassmods_zh.po @@ -251,7 +251,7 @@ msgid "" "database: %s" msgstr "" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -543,19 +543,19 @@ msgstr "" msgid "raster" msgstr "" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 msgid "My first raster module" msgstr "" -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -609,7 +609,7 @@ msgstr "" msgid "Raster map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -794,11 +794,11 @@ msgstr "" msgid "vector" msgstr "" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 msgid "My first vector module" msgstr "" -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -820,12 +820,12 @@ msgstr "" msgid "Vector map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 msgid "Unable to set predetermined vector open level" msgstr "" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -897,7 +897,7 @@ msgstr "" msgid "Unable to open vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -952,7 +952,7 @@ msgstr "" msgid "Unable to create vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -968,7 +968,7 @@ msgstr "" msgid "Database connection not defined for layer %d" msgstr "" -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -982,7 +982,7 @@ msgstr "" msgid "Unable to start driver <%s>" msgstr "无法启动驱动 <%s>" -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1033,7 +1033,7 @@ msgstr "无法启动驱动 <%s>" msgid "Unable to open database <%s> by driver <%s>" msgstr "无法由驱动<%s>打开数据库 <%s>" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1048,17 +1048,17 @@ msgstr "无法由驱动<%s>打开数据库 <%s>" msgid "Unable to describe table <%s>" msgstr "无法描述表 <%s>" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, c-format msgid "Unable to get attribute data for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, c-format msgid "Error while retrieving database record for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "" diff --git a/locale/po/grassmods_zh_CN.po b/locale/po/grassmods_zh_CN.po index 455581b506d..87e296038ea 100644 --- a/locale/po/grassmods_zh_CN.po +++ b/locale/po/grassmods_zh_CN.po @@ -252,7 +252,7 @@ msgid "" "database: %s" msgstr "" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -544,19 +544,19 @@ msgstr "" msgid "raster" msgstr "" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 msgid "My first raster module" msgstr "" -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -610,7 +610,7 @@ msgstr "" msgid "Raster map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -795,11 +795,11 @@ msgstr "" msgid "vector" msgstr "" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 msgid "My first vector module" msgstr "" -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -821,12 +821,12 @@ msgstr "" msgid "Vector map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 msgid "Unable to set predetermined vector open level" msgstr "" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -898,7 +898,7 @@ msgstr "" msgid "Unable to open vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -953,7 +953,7 @@ msgstr "" msgid "Unable to create vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -969,7 +969,7 @@ msgstr "" msgid "Database connection not defined for layer %d" msgstr "" -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -983,7 +983,7 @@ msgstr "" msgid "Unable to start driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1034,7 +1034,7 @@ msgstr "" msgid "Unable to open database <%s> by driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1049,17 +1049,17 @@ msgstr "" msgid "Unable to describe table <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, c-format msgid "Unable to get attribute data for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, c-format msgid "Error while retrieving database record for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "" diff --git a/locale/po/grasswxpy_ar.po b/locale/po/grasswxpy_ar.po index 0475f287e81..5a3439edac1 100644 --- a/locale/po/grasswxpy_ar.po +++ b/locale/po/grasswxpy_ar.po @@ -1092,7 +1092,7 @@ msgstr "" msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" diff --git a/locale/po/grasswxpy_bn.po b/locale/po/grasswxpy_bn.po index 76ade0c0e31..70368624833 100644 --- a/locale/po/grasswxpy_bn.po +++ b/locale/po/grasswxpy_bn.po @@ -1094,7 +1094,7 @@ msgstr "" msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" diff --git a/locale/po/grasswxpy_cs.po b/locale/po/grasswxpy_cs.po index 24423cf5314..c2e97a4a0ba 100644 --- a/locale/po/grasswxpy_cs.po +++ b/locale/po/grasswxpy_cs.po @@ -1111,12 +1111,12 @@ msgstr "Nastavení 3D zobrazení bylo uloženo do souboru <%s>." msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" "Tento modul vyžaduje modul NumPy, který nelze importovat. Nejspíše není " "nainstalován (není součástí standardní instalace Pythonu). Podívejte se na " -"stránku Numeric Python (http://numpy.scipy.org), kde naleznete další " +"stránku Numeric Python (https://numpy.org), kde naleznete další " "informace o stažení a instalaci." #: ../gui/wxpython/nviz/wxnviz.py:361 ../gui/wxpython/nviz/wxnviz.py:372 diff --git a/locale/po/grasswxpy_de.po b/locale/po/grasswxpy_de.po index f0047ccae3d..42e35b2a4c8 100644 --- a/locale/po/grasswxpy_de.po +++ b/locale/po/grasswxpy_de.po @@ -1122,12 +1122,12 @@ msgstr "Einstellungen für 3D-Ansicht gespeichert in Datei <%s>." msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" "Dieses erfordert NumPy, das nicht importiert werden konnte. Es ist eventuell " "nicht installiert (es ist nicht Bestandteil der Standard-Python-" -"Distribution). Gehe zur Numeric Python Website (http://numpy.scipy.org), um " +"Distribution). Gehe zur Numeric Python Website (https://numpy.org), um " "den Quellcode oder Binaries zu erhalten." #: ../gui/wxpython/nviz/wxnviz.py:361 ../gui/wxpython/nviz/wxnviz.py:372 diff --git a/locale/po/grasswxpy_el.po b/locale/po/grasswxpy_el.po index 582972aba2b..59e9e135df9 100644 --- a/locale/po/grasswxpy_el.po +++ b/locale/po/grasswxpy_el.po @@ -1097,14 +1097,14 @@ msgstr "Οι επιλογες της 3D προβολής αποθηκεύτηκ msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" "Αυτό το πρόσθετο απαιτεί το πρόσθετο NumPy, το οποίο δεν μπόρεσε να " "εισαχθεί. Πιθανότατα να μην είναι εγκατεστημένο (δεν είναι μέρος της γενικής " "διανομής της Python). Για περισσότερες πληροφορίες και για λήψη αρχείων προς " "εγκατάσταση καθώς και του πηγαίου κώδικα, απευθυνθήτε στην ιστοσελίδα της " -"Numpy (http://numpy.scipy.org)." +"NumPy (https://numpy.org)." #: ../gui/wxpython/nviz/wxnviz.py:361 ../gui/wxpython/nviz/wxnviz.py:372 #: ../gui/wxpython/iclass/g.gui.iclass.py:88 diff --git a/locale/po/grasswxpy_es.po b/locale/po/grasswxpy_es.po index dd64f3e2ca1..e5ccaf4d59c 100644 --- a/locale/po/grasswxpy_es.po +++ b/locale/po/grasswxpy_es.po @@ -1130,12 +1130,12 @@ msgstr "Las configuraciones de la vista 3D se han guardado en el archivo <%s>." msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" -"Este módulo requiere el módulo NumP, que no pudo ser importado. " +"Este módulo requiere el módulo NumPy, que no pudo ser importado. " "Probablemente no está instalado (no es parte de la distibución estándar de " -"Python). Ver el site Numérico de Python (http://numpy.scipy.org) para más " +"Python). Ver el site Numérico de Python (https://numpy.org) para más " "información sobre la fuente de descarga o binarios." #: ../gui/wxpython/nviz/wxnviz.py:361 ../gui/wxpython/nviz/wxnviz.py:372 diff --git a/locale/po/grasswxpy_fi.po b/locale/po/grasswxpy_fi.po index 72ce83830d8..ab1451450e7 100644 --- a/locale/po/grasswxpy_fi.po +++ b/locale/po/grasswxpy_fi.po @@ -1094,7 +1094,7 @@ msgstr "" msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" diff --git a/locale/po/grasswxpy_fr.po b/locale/po/grasswxpy_fr.po index 8879c526ca7..ca568bbe2ae 100644 --- a/locale/po/grasswxpy_fr.po +++ b/locale/po/grasswxpy_fr.po @@ -1127,12 +1127,12 @@ msgstr "Paramètres de vue 3D enregistrés dans le fichier <%s>." msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" "Ce module requiert le module NumPy, qui n'a pas pu être importé. Il n'est " "probablement pas installé (ne fait pas partie de la distribution standard " -"Python). Voir le site Numeric Python (http://numpy.scipy.org) pour " +"Python). Voir le site Numeric Python (https://numpy.org) pour " "télécharger les binaires ou le code source." #: ../gui/wxpython/nviz/wxnviz.py:361 ../gui/wxpython/nviz/wxnviz.py:372 diff --git a/locale/po/grasswxpy_hu.po b/locale/po/grasswxpy_hu.po index c461a875154..4b1d57eb8fe 100644 --- a/locale/po/grasswxpy_hu.po +++ b/locale/po/grasswxpy_hu.po @@ -1101,12 +1101,12 @@ msgstr "3D nézet beállításait mentettem a <%s> fájlba." msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" "Ez a modul a NumPy modult igényli, melyet nem tudtam importálni. Ezt " "valószínűleg nem telepítették (ez nem része a standard Python kiadásoknak). " -"Lásd a Numeric Python oldalt (http://numpy.scipy.org) a forrás vagy bináris " +"Lásd a Numeric Python oldalt (https://numpy.org) a forrás vagy bináris " "letöltési információkért." #: ../gui/wxpython/nviz/wxnviz.py:361 ../gui/wxpython/nviz/wxnviz.py:372 diff --git a/locale/po/grasswxpy_id_ID.po b/locale/po/grasswxpy_id_ID.po index e1983612035..16ef34c40be 100644 --- a/locale/po/grasswxpy_id_ID.po +++ b/locale/po/grasswxpy_id_ID.po @@ -1094,7 +1094,7 @@ msgstr "" msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" diff --git a/locale/po/grasswxpy_it.po b/locale/po/grasswxpy_it.po index ddeb21e75d2..6f400df3b19 100644 --- a/locale/po/grasswxpy_it.po +++ b/locale/po/grasswxpy_it.po @@ -1119,12 +1119,12 @@ msgstr "Impostazioni visualizzazione 3D salvate nel file <%s>." msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" "Questo modulo richiede NumPy, che non è stato possibile importare. " "Probabilmente non è installato (non fa parte della distribuzione standard di " -"Python). Controlla il sito di Numeric Python (http://numpy.scipy.org) per " +"Python). Controlla il sito di Numeric Python (https://numpy.org) per " "maggiori informazione sul download del sorgente o dei binari." #: ../gui/wxpython/nviz/wxnviz.py:361 ../gui/wxpython/nviz/wxnviz.py:372 diff --git a/locale/po/grasswxpy_ja.po b/locale/po/grasswxpy_ja.po index dce28745650..6e43667a294 100644 --- a/locale/po/grasswxpy_ja.po +++ b/locale/po/grasswxpy_ja.po @@ -1102,13 +1102,13 @@ msgstr "3D表示設定がファイルに保存されました <%s>." msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" -"このモジュールはインポートされないNumpyモジュールを必要とします. おそらくイン" +"このモジュールはインポートされないNumPyモジュールを必要とします. おそらくイン" "ストールされていません(標準的なPython分布の一環ではありません). ソースまた" -"はバイナリのダウンロードに関する情報は数値Pythonのサイト(http://numpy.scipy." -"org)を参照してください" +"はバイナリのダウンロードに関する情報は数値Pythonのサイト(https://numpy.org" +")を参照してください" #: ../gui/wxpython/nviz/wxnviz.py:361 ../gui/wxpython/nviz/wxnviz.py:372 #: ../gui/wxpython/iclass/g.gui.iclass.py:88 diff --git a/locale/po/grasswxpy_ko.po b/locale/po/grasswxpy_ko.po index 2562d99de06..aedfb614f94 100644 --- a/locale/po/grasswxpy_ko.po +++ b/locale/po/grasswxpy_ko.po @@ -1094,12 +1094,12 @@ msgstr "" msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" "이 모듈은 NumPy 모듈이 필요하지만 가져올 수 없었습니다. 표준 파이썬 배포에 포" "함되지 않았기 때문에 아마도 설치되지 않은 것 같습니다. 소스나 이진코드를 내려" -"받는데 필요한 정보는 Numeric Python 사이트 http://numpy.scipy.org에서 확인하" +"받는데 필요한 정보는 Numeric Python 사이트 https://numpy.org에서 확인하" "세요." #: ../gui/wxpython/nviz/wxnviz.py:361 ../gui/wxpython/nviz/wxnviz.py:372 diff --git a/locale/po/grasswxpy_lv.po b/locale/po/grasswxpy_lv.po index e733c4449a3..55fc29c8662 100644 --- a/locale/po/grasswxpy_lv.po +++ b/locale/po/grasswxpy_lv.po @@ -1096,7 +1096,7 @@ msgstr "" msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" diff --git a/locale/po/grasswxpy_ml.po b/locale/po/grasswxpy_ml.po index f0cd245758a..6add41bd33a 100644 --- a/locale/po/grasswxpy_ml.po +++ b/locale/po/grasswxpy_ml.po @@ -1094,7 +1094,7 @@ msgstr "" msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" diff --git a/locale/po/grasswxpy_pl.po b/locale/po/grasswxpy_pl.po index f399314c413..d049c5c7263 100644 --- a/locale/po/grasswxpy_pl.po +++ b/locale/po/grasswxpy_pl.po @@ -1105,7 +1105,7 @@ msgstr "Ustawienia widoku 3D zapisano w pliku <%s>." msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" diff --git a/locale/po/grasswxpy_pt.po b/locale/po/grasswxpy_pt.po index 9ac71057c8c..5e097cfeb15 100644 --- a/locale/po/grasswxpy_pt.po +++ b/locale/po/grasswxpy_pt.po @@ -1097,7 +1097,7 @@ msgstr "" msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" diff --git a/locale/po/grasswxpy_pt_BR.po b/locale/po/grasswxpy_pt_BR.po index bd7f33c244d..dc2cfb7d4c2 100644 --- a/locale/po/grasswxpy_pt_BR.po +++ b/locale/po/grasswxpy_pt_BR.po @@ -1121,12 +1121,12 @@ msgstr "Configurações de visualização 3D salvas no arquivo <%s>." msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" "Este módulo requer o módulo NumPy, que não pôde ser importado. Provavelmente " "não está instalado (não faz parte da distribuição padrão do Python). " -"Consulte o site Numeric Python (http://numpy.scipy.org) para obter " +"Consulte o site Numeric Python (https://numpy.org) para obter " "informações sobre como baixar source ou binários." #: ../gui/wxpython/nviz/wxnviz.py:361 ../gui/wxpython/nviz/wxnviz.py:372 diff --git a/locale/po/grasswxpy_ro.po b/locale/po/grasswxpy_ro.po index 973569f6110..0d1c17d2d7f 100644 --- a/locale/po/grasswxpy_ro.po +++ b/locale/po/grasswxpy_ro.po @@ -1118,12 +1118,12 @@ msgstr "Setările de vizualizare 3D salvate ca fișier <%s>." msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" "Acest modul necesită modulul NumPy, care nu a putut fi importat. Probabil nu " "este instalat (nu este parte a distribuției standard de Python). Vezi site-" -"ul Numeric Python (http://numpy.scipy.org) pentru informații despre " +"ul Numeric Python (https://numpy.org) pentru informații despre " "descărcarea codului sursă." #: ../gui/wxpython/nviz/wxnviz.py:361 ../gui/wxpython/nviz/wxnviz.py:372 diff --git a/locale/po/grasswxpy_ru.po b/locale/po/grasswxpy_ru.po index 67c026b0770..5d3705c7621 100644 --- a/locale/po/grasswxpy_ru.po +++ b/locale/po/grasswxpy_ru.po @@ -1118,14 +1118,14 @@ msgstr "Параметры трёхмерного вида сохранены в msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" "Для работы данного модуля необходимо наличие модуля NumPy, который не " "удалось импортировать. Вероятно, он не установлен (не является частью " "стандартного дистрибутива Python). Для получения информации о загрузке " -"исходного кода или двоичных файлы посетите сайт «Числовой Python» (http://" -"numpy.scipy.org)." +"исходного кода или двоичных файлы посетите сайт «Числовой Python» (https://" +"numpy.org)." #: ../gui/wxpython/nviz/wxnviz.py:361 ../gui/wxpython/nviz/wxnviz.py:372 #: ../gui/wxpython/iclass/g.gui.iclass.py:88 diff --git a/locale/po/grasswxpy_si.po b/locale/po/grasswxpy_si.po index f3d27cc8385..7d7c44acc16 100644 --- a/locale/po/grasswxpy_si.po +++ b/locale/po/grasswxpy_si.po @@ -1094,7 +1094,7 @@ msgstr "" msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" diff --git a/locale/po/grasswxpy_sl.po b/locale/po/grasswxpy_sl.po index d38b3ea58a5..0fd856d5061 100644 --- a/locale/po/grasswxpy_sl.po +++ b/locale/po/grasswxpy_sl.po @@ -1092,7 +1092,7 @@ msgstr "" msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" diff --git a/locale/po/grasswxpy_ta.po b/locale/po/grasswxpy_ta.po index 7d752f435f6..78526979368 100644 --- a/locale/po/grasswxpy_ta.po +++ b/locale/po/grasswxpy_ta.po @@ -1095,7 +1095,7 @@ msgstr "" msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" diff --git a/locale/po/grasswxpy_th.po b/locale/po/grasswxpy_th.po index b65840cfe09..f3b63365d9b 100644 --- a/locale/po/grasswxpy_th.po +++ b/locale/po/grasswxpy_th.po @@ -1094,7 +1094,7 @@ msgstr "" msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" diff --git a/locale/po/grasswxpy_tr.po b/locale/po/grasswxpy_tr.po index 760a131dcc6..34c69f06ff0 100644 --- a/locale/po/grasswxpy_tr.po +++ b/locale/po/grasswxpy_tr.po @@ -1096,7 +1096,7 @@ msgstr "" msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" diff --git a/locale/po/grasswxpy_uk.po b/locale/po/grasswxpy_uk.po index 4bf446da553..db3b10f93f3 100644 --- a/locale/po/grasswxpy_uk.po +++ b/locale/po/grasswxpy_uk.po @@ -1095,7 +1095,7 @@ msgstr "" msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" diff --git a/locale/po/grasswxpy_vi.po b/locale/po/grasswxpy_vi.po index 364aed72ba1..1e15d066449 100644 --- a/locale/po/grasswxpy_vi.po +++ b/locale/po/grasswxpy_vi.po @@ -1095,7 +1095,7 @@ msgstr "" msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" diff --git a/locale/po/grasswxpy_zh.po b/locale/po/grasswxpy_zh.po index 4d2ab8dfd85..330b876a438 100644 --- a/locale/po/grasswxpy_zh.po +++ b/locale/po/grasswxpy_zh.po @@ -1089,11 +1089,11 @@ msgstr "" msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" "该模块需要使用的NumPy模块无法导入。可能没有安装该模块(非Python标准模块)。参" -"见Numeric Python网站(http://numpy.scipy.org)查看下载源码或安装文件信息。" +"见Numeric Python网站(https://numpy.org)查看下载源码或安装文件信息。" #: ../gui/wxpython/nviz/wxnviz.py:361 ../gui/wxpython/nviz/wxnviz.py:372 #: ../gui/wxpython/iclass/g.gui.iclass.py:88 diff --git a/locale/po/grasswxpy_zh_CN.po b/locale/po/grasswxpy_zh_CN.po index cbb050149b9..f03f638b9eb 100644 --- a/locale/po/grasswxpy_zh_CN.po +++ b/locale/po/grasswxpy_zh_CN.po @@ -1095,7 +1095,7 @@ msgstr "" msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" diff --git a/locale/templates/grassmods.pot b/locale/templates/grassmods.pot index 30b09b22a2b..2648cf1de3b 100644 --- a/locale/templates/grassmods.pot +++ b/locale/templates/grassmods.pot @@ -251,7 +251,7 @@ msgid "" "database: %s" msgstr "" -#: ../doc/raster/r.example/main.c:83 ../raster/r.external/main.c:57 +#: ../doc/examples/raster/r.example/main.c:83 ../raster/r.external/main.c:57 #: ../raster/r.quantile/main.c:297 ../raster/r.terraflow/main.cpp:457 #: ../raster/r.info/main.c:71 ../raster/r.patch/main.c:68 #: ../raster/r.support.stats/main.c:35 ../raster/r.what.color/main.c:88 @@ -543,19 +543,19 @@ msgstr "" msgid "raster" msgstr "" -#: ../doc/raster/r.example/main.c:84 ../doc/vector/v.example/main.c:50 +#: ../doc/examples/raster/r.example/main.c:84 ../doc/examples/vector/v.example/main.c:50 msgid "keyword2" msgstr "" -#: ../doc/raster/r.example/main.c:85 ../doc/vector/v.example/main.c:51 +#: ../doc/examples/raster/r.example/main.c:85 ../doc/examples/vector/v.example/main.c:51 msgid "keyword3" msgstr "" -#: ../doc/raster/r.example/main.c:86 +#: ../doc/examples/raster/r.example/main.c:86 msgid "My first raster module" msgstr "" -#: ../doc/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 +#: ../doc/examples/raster/r.example/main.c:105 ../raster/r.terraflow/main.cpp:207 #: ../raster/r.terraflow/main.cpp:354 ../raster/r.terraflow/main.cpp:390 #: ../raster/r.info/main.c:111 ../raster/r.cost/main.c:364 #: ../raster/r.cost/main.c:391 ../raster/r.cost/main.c:711 @@ -609,7 +609,7 @@ msgstr "" msgid "Raster map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 +#: ../doc/examples/vector/v.example/main.c:49 ../raster/r.contour/main.c:90 #: ../raster/r.random/main.c:55 ../misc/m.nviz.script/main.c:71 #: ../misc/m.nviz.image/main.c:52 #: ../locale/scriptstrings/v.clip_to_translate.c:2 @@ -794,11 +794,11 @@ msgstr "" msgid "vector" msgstr "" -#: ../doc/vector/v.example/main.c:52 +#: ../doc/examples/vector/v.example/main.c:52 msgid "My first vector module" msgstr "" -#: ../doc/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 +#: ../doc/examples/vector/v.example/main.c:76 ../raster/r.cost/main.c:357 #: ../raster/r.walk/main.c:460 ../raster/r.carve/main.c:153 #: ../misc/m.nviz.image/vector.c:82 ../general/g.region/main.c:505 #: ../vector/v.lidar.edgedetection/main.c:181 ../vector/v.profile/main.c:371 @@ -820,12 +820,12 @@ msgstr "" msgid "Vector map <%s> not found" msgstr "" -#: ../doc/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 +#: ../doc/examples/vector/v.example/main.c:82 ../vector/v.profile/main.c:378 #: ../vector/v.cluster/main.c:137 msgid "Unable to set predetermined vector open level" msgstr "" -#: ../doc/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 +#: ../doc/examples/vector/v.example/main.c:88 ../raster/r.cost/main.c:599 #: ../raster/r.cost/main.c:666 ../raster/r.drain/main.c:277 #: ../raster/r.walk/main.c:760 ../raster/r.walk/main.c:827 #: ../raster/r.region/main.c:172 ../raster/r.sim/simlib/observation_points.c:40 @@ -897,7 +897,7 @@ msgstr "" msgid "Unable to open vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 +#: ../doc/examples/vector/v.example/main.c:101 ../raster/r.to.vect/main.c:159 #: ../raster/r.drain/main.c:204 ../raster/r.resamp.bspline/main.c:484 #: ../raster/r.sim/simlib/output.c:49 ../raster/r.sim/simlib/output.c:56 #: ../raster/r.flow/flow_io.c:181 ../raster/r.contour/main.c:155 @@ -952,7 +952,7 @@ msgstr "" msgid "Unable to create vector map <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 +#: ../doc/examples/vector/v.example/main.c:110 ../vector/v.generalize/misc.c:167 #: ../vector/v.to.rast/support.c:101 ../vector/v.to.rast/support.c:271 #: ../vector/v.to.rast/support.c:499 ../vector/v.distance/main.c:563 #: ../vector/v.buffer/main.c:422 ../vector/v.univar/main.c:236 @@ -968,7 +968,7 @@ msgstr "" msgid "Database connection not defined for layer %d" msgstr "" -#: ../doc/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 +#: ../doc/examples/vector/v.example/main.c:130 ../raster/r.stream.extract/close.c:176 #: ../db/db.execute/main.c:68 ../db/db.createdb/main.c:38 #: ../db/db.columns/main.c:47 ../db/db.dropdb/main.c:39 #: ../db/db.databases/main.c:47 ../db/db.describe/main.c:51 @@ -982,7 +982,7 @@ msgstr "" msgid "Unable to start driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 +#: ../doc/examples/vector/v.example/main.c:138 ../raster/r.to.vect/main.c:189 #: ../raster/r.contour/main.c:170 ../raster/r.volume/main.c:248 #: ../raster/r.random/random.c:67 ../misc/m.nviz.image/vector.c:301 #: ../raster3d/r3.flow/main.c:46 ../ps/ps.map/catval.c:53 @@ -1033,7 +1033,7 @@ msgstr "" msgid "Unable to open database <%s> by driver <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:143 ../db/db.columns/main.c:58 +#: ../doc/examples/vector/v.example/main.c:143 ../db/db.columns/main.c:58 #: ../db/db.describe/main.c:62 ../vector/v.db.connect/main.c:229 #: ../vector/v.out.vtk/writeVTK.c:651 ../vector/v.out.postgis/table.c:41 #: ../vector/v.reclass/main.c:189 ../vector/v.in.ascii/main.c:446 @@ -1048,17 +1048,17 @@ msgstr "" msgid "Unable to describe table <%s>" msgstr "" -#: ../doc/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 +#: ../doc/examples/vector/v.example/main.c:176 ../vector/v.profile/main.c:708 #, c-format msgid "Unable to get attribute data for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 +#: ../doc/examples/vector/v.example/main.c:185 ../vector/v.profile/main.c:717 #, c-format msgid "Error while retrieving database record for cat %d" msgstr "" -#: ../doc/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 +#: ../doc/examples/vector/v.example/main.c:220 ../vector/v.fill.holes/main.c:196 #, c-format msgid "Unable to copy attribute table to vector map <%s>" msgstr "" diff --git a/locale/templates/grasswxpy.pot b/locale/templates/grasswxpy.pot index 57126b7f1a1..899d4518078 100644 --- a/locale/templates/grasswxpy.pot +++ b/locale/templates/grasswxpy.pot @@ -1093,7 +1093,7 @@ msgstr "" msgid "" "This module requires the NumPy module, which could not be imported. It " "probably is not installed (it's not part of the standard Python " -"distribution). See the Numeric Python site (http://numpy.scipy.org) for " +"distribution). See the Numeric Python site (https://numpy.org) for " "information on downloading source or binaries." msgstr "" diff --git a/macos/ReadMe.md b/macos/ReadMe.md new file mode 100644 index 00000000000..b7a1a853e99 --- /dev/null +++ b/macos/ReadMe.md @@ -0,0 +1,148 @@ +# Build GRASS GIS with Anaconda + +This is a script package for automated build of GRASS GIS as a macOS +application bundle (GRASS-x.x.app). + +The building script `build_grass_app.bash` will do all the steps – creating App +bundle, installing Conda dependencies (using the package manager Miniforge), +to patching, compiling and installing GRASS GIS – to end up with an +installed GRASS.app in `/Applications`. It can also create a compressed dmg +file if so wished. + +Usage: + +```text +./build_grass_app.bash [arguments] + +Arguments: + -s + --sdk [path] MacOS SDK - full path, spaces in path not allowed. + -t + --target [target] Set deployment target version (MACOSX_DEPLOYMENT_TARGET), + e.g. "10.14", optional, default is set from SDK. + -o + --dmg-out-dir [path] Output directory path for DMG file creation + This is a requirement for creating .dmg files. + -c + --conda-file [path] Conda package requirement file, optional. + --with-liblas Include libLAS support, optional, default is no support. + -u + --update-conda-stable Update the stable explicit conda requirement file. This + is only allowed if conda-requirements-dev-[arm64|x86_64].txt + is used (with --conda-file), to keep the two files in sync. + -r + --repackage Recreate dmg file from previously built app, + setting [-o | --dmg-out-dir] is a requirement. + --notarize Code sign and notarize app and dmg for distribution + Setting Apple developer ID, keychain profile, and + provisionprofile is a prerequisite. + -h + --help Usage information. + +``` + +## Requirements + +- Apple's Command Line Tools + +You need to install Apple's Command Line Tools (CLT), with or without Xcode. +Installing CLT is possible with following terminal command: + +```sh +xcode-select --install +``` + +Xcode is available for download at Apple's App Store. + +CLT will typically install SDKs in `/Library/Developer/CommandLineTools/SDKs/`, +while finding Xcode's default SDK can be achieved with e.g. +`xcrun --show-sdk-path`. (See `man xcrun` for more functions.) + +**Note**: Compiling GRASS (C/C++ based) addon extensions with the resulting +GRASS.app requires CLT installation too. + +## Instructions + +```sh +cd [grass-source-dir] + +./macos/build_grass_app.bash +``` + +There is one required variable: full path to MacOS SDK. By default +this is attempted to be set by `xcrun --show-sdk-path`. Alternatively, +it can be set either through editing the +`$HOME/.config/grass/configure-build-[arm64|x86_64].sh` file, +or by giving it as argument to the main script: `./build_grass_app.bash`. + +```sh +./macos/build_grass_app.bash \ + --sdk /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk +``` + +Argument given to `./build_grass_app.bash` will override settings in +`configure-build-[arm64|x86_64].sh`. You can also do +`./build_grass_app.bash --help` for info on possible configurations. + +Required settings: + +- SDK full path to the SDK that will be set to -isysroot (path may **not** + contain spaces) + +Example by executing with arguments: + +```sh +./macos/build_grass_app.bash \ + --sdk /Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk \ + --target 10.14 +``` + +Example of building and creating dmg by executing with arguments: + +```sh +./macos/build_grass_app.bash \ + --sdk /Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk \ + --conda-file ./Desktop/requirement.txt \ + --dmg-out-dir ~/Desktop +``` + +## Build Target Architecture + +Building GRASS on a x86_64 (Intel) machine can create a binary *only* for the +x86_64 architecture. On an Apple silicon based machine, a binary can be created +for *either* x86_64 or arm64 (creating Universal Binary is at the moment *not* +possible). + +The building target architecture depends ultimately on the result of `uname -m` +in the Terminal running the `build_grass_app.bash` script. Building on Apple silicon +machines by opening the Terminal in Rosetta mode, creates a x86_64 binary. + +## Settings + +By default a conda environment will be created by an explicit conda requirement +file (`files/conda-requirements-stable-[arm64|x86_64].txt`). It was created by +executing `conda list --explicit` on an environment created by the file +`files/conda-requirements-dev-[arm64|x86_64].txt`. This enables reproducibility +and stability. It is also possible to use a customized conda requirement file, +set as an argument (or in `configure-build-[arm64|x86_64].sh`). + +To be able to bump dependency versions and/or add/remove dependencies for the +`files/conda-requirements-stable-[arm64|x86_64].txt` file the command flag +`--update-conda-stable` can be added. A requirement for this is that +`files/conda-requirements-dev-[arm64|x86_64].txt` is used for `--conda-file`. +This function is primarily intended to be used for updating this git repo. + +```sh +./macos/build_grass_app.bash --update-conda-stable +``` + +GRASS build configure settings can be set in configure files located in +`$HOME/.config/grass` (or `$XDG_CONFIG_HOME/grass` if set), e.g.: + +```sh +mkdir -p $HOME/.config/grass +cp macos/files/configure-build.sh.in $HOME/.config/grass/configure-build-arm64.sh +cp macos/files/configure-build.sh.in $HOME/.config/grass/configure-build-x86_64.sh +``` + +Edit the configure file(s) to your needs. diff --git a/macos/build_grass_app.bash b/macos/build_grass_app.bash new file mode 100755 index 00000000000..f7177430ddd --- /dev/null +++ b/macos/build_grass_app.bash @@ -0,0 +1,758 @@ +#!/bin/bash + +############################################################################ +# +# TOOL: build_grass_app.bash +# AUTHOR(s): Nicklas Larsson +# PURPOSE: Build and bundle GRASS GIS app for macOS +# COPYRIGHT: (c) 2020-2025 Nicklas Larsson and the GRASS Development Team +# (c) 2020 Michael Barton +# (c) 2018 Eric Hutton, Community Surface Dynamics Modeling +# System +# +# This package is written by Nicklas Larsson and is heavily based +# on work by Eric Hutton with contributions by Michael Barton. +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +############################################################################# + +this_script=$(basename "$0") +this_script_dir=$(cd "$(dirname "$0")" || exit; pwd) +arch=$(uname -m) +cache_dir="${this_script_dir}/cache" +config_home="${HOME}/.config" +sdk= +grassdir=$(cd "${this_script_dir}/.." || exit; pwd) +deployment_target= +grass_version="" +grass_version_major="" +grass_version_minor="" +grass_version_release= +grass_version_date= +patch_dir= +grass_app_name="" +grass_app_bundle="" +conda_stable_file="${this_script_dir}/files/conda-requirements-stable-${arch}.txt" +conda_dev_file="${this_script_dir}/files/conda-requirements-dev-${arch}.txt" +conda_req_file="$conda_stable_file" +conda_temp_dir=$(mktemp -d -t GRASS) +conda_bin="${conda_temp_dir}/bin/mamba" +conda_update_stable=0 +miniconda_url="https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-MacOSX-${arch}.sh" +dmg_title= +dmg_name= +dmg_out_dir= +bundle_version= +repackage=0 +with_liblas=0 +cs_entitlements="${this_script_dir}/files/grass.entitlements" +cs_ident= +cs_keychain_profile= +cs_provisionprofile= +notarize=0 + +bash=/bin/bash +codesign=/usr/bin/codesign +ditto=/usr/bin/ditto +install_name_tool=/usr/bin/install_name_tool +xcrun=/usr/bin/xcrun + +# patch needed for GRASS 8.0+ +IFS='' read -r -d '' inst_dir_patch <<'EOF' +--- include/Make/Platform.make.in.orig ++++ include/Make/Platform.make.in +@@ -37,7 +37,7 @@ + exec_prefix = @exec_prefix@ + ARCH = @host@ + UNIX_BIN = @BINDIR@ +-INST_DIR = @INSTDIR@ ++INST_DIR = @exec_prefix@ + + GRASS_HOME = @GRASS_HOME@ + RUN_GISBASE = @GISBASE@ +EOF + + +# read in configurations +if [ -n "$XDG_CONFIG_HOME" ]; then + config_home="$XDG_CONFIG_HOME" +fi +if [ -f "${config_home}/grass/configure-build-${arch}.sh" ]; then + source "${config_home}/grass/configure-build-${arch}.sh" +fi + +############################################################################# +# Functions +############################################################################# + +function display_usage () { cat <<- _EOF_ + +GRASS GIS build script for Anaconda. + +Description... + +Usage: $this_script [arguments] +Arguments: + -s + --sdk [path] MacOS SDK - full path to the SDK, which will be set as + -isysroot, required, spaces in path not allowed + -t + --target [target] Set deployment target version (MACOSX_DEPLOYMENT_TARGET), + e.g. "10.14", optional, default is set from SDK + -o + --dmg-out-dir [path] Output directory path for DMG file creation + This is a requirement for creating .dmg files. + -c + --conda-file [path] Conda package requirement file, optional. + --with-liblas Include libLAS support, optional, default is no support. + -u + --update-conda-stable Update the stable explicit conda requirement file. This + is only allowed if conda-requirements-dev-[arm64|x86_64].txt + is used (with --conda-file), to keep the two files in sync. + -r + --repackage Recreate dmg file from previously built app, + setting [-o | --dmg-out-dir] is a requirement. + --notarize Code sign and notarize app and dmg for distribution + Setting Apple developer ID, keychain profile, and + provisionprofile is a prerequisite. + -h + --help Usage information + +Example: + ./$this_script + ./$this_script -s /Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk + +_EOF_ +} + +function printtag() { + # GitHub Actions tag format + [[ "$CI" == "true" ]] && echo "::$1::${2-}" +} + +function begingroup() { + printtag "group" "$1" +} + +function endgroup() { + printtag "endgroup" +} + +function exit_nice () { + error_code=$1 + if [[ "$#" -eq 2 && $2 = "cleanup" ]]; then + rm -rf "$conda_temp_dir" + fi + exit "$error_code" +} + +function read_grass_version () { + begingroup "Read GRASS version" + local versionfile="${grassdir}/include/VERSION" + local arr=() + while read -r line; do + arr+=("$line") + done < "$versionfile" + grass_version_major=${arr[0]} + grass_version_minor=${arr[1]} + grass_version_release=${arr[2]} + grass_version_date=${arr[3]} + grass_version="${grass_version_major}.${grass_version_minor}.${grass_version_release}" + patch_dir="${this_script_dir}/patches/${grass_version}" + grass_app_name="GRASS-${grass_version_major}.${grass_version_minor}.app" + grass_app_bundle="/Applications/${grass_app_name}" + dmg_title="GRASS-GIS-${grass_version}" + dmg_name="grass-${grass_version}-${arch}.dmg" + echo "GRASS_VERSION: ${grass_version}" + endgroup +} + +# This set the build version for CFBundleVersion, in case of dev version the +# git short commit hash number is added. +function set_bundle_version () { + begingroup "Set bundle version" + pushd "$grassdir" > /dev/null || exit + bundle_version=$grass_version + + local is_git_repo + is_git_repo=$(git rev-parse --is-inside-work-tree 2> /dev/null) + if [[ ! $? -eq 0 && ! "$is_git_repo" = "true" ]]; then + popd > /dev/null || exit + return + fi + + if [[ "$grass_version_release" = *"dev"* ]]; then + local git_commit + git_commit=$(git rev-parse --short HEAD) + bundle_version="${bundle_version} \(${git_commit}\)" + fi + popd > /dev/null || exit + echo "BUNDLE_VERSION: ${bundle_version}" + endgroup +} + +function make_app_bundle_dir () { + begingroup "Make app bundle dir" + local contents_dir="${grass_app_bundle}/Contents" + local resources_dir="${contents_dir}/Resources" + local macos_dir="${contents_dir}/MacOS" + local grass_bin_in="grass.sh.in" + mkdir -p "$resources_dir" + chmod 0755 "$grass_app_bundle" "$contents_dir" "$resources_dir" + mkdir -m 0755 "$macos_dir" + + local info_plist_in="${grassdir}/macos/files/Info.plist.in" + + sed "s|@GRASS_VERSION_DATE@|${grass_version_date}|g" "$info_plist_in" | \ + sed "s|@GRASS_VERSION_MAJOR@|${grass_version_major}|g" | \ + sed "s|@GRASS_VERSION_MINOR@|${grass_version_minor}|g" | \ + sed "s|@GRASS_VERSION_RELEASE@|${grass_version_release}|g" | \ + sed "s|@BUNDLE_VERSION@|${bundle_version}|g" | \ + sed "s|@DEPLOYMENT_TARGET@|${deployment_target}|g" \ + > "$contents_dir/Info.plist" + + local grassbin="grass" + sed "s|@GRASSBIN@|$grassbin|g" \ + "${this_script_dir}/files/$grass_bin_in" > "${macos_dir}/grass.sh" + cp -p "${this_script_dir}/files/grass.scpt" "${macos_dir}/grass.scpt" + cp -p "${grassdir}/macos/files/AppIcon.icns" "${resources_dir}/AppIcon.icns" + cp -p "${grassdir}/macos/files/GRASSDocument_gxw.icns" \ + "${resources_dir}/GRASSDocument_gxw.icns" + + chmod 0644 "${contents_dir}/Info.plist" + chmod 0755 "${macos_dir}/grass.sh" + chmod 0644 "${resources_dir}/AppIcon.icns" + chmod 0644 "${resources_dir}/GRASSDocument_gxw.icns" + + # swiftc -v "${this_script_dir}/files/main.swift" \ + # -sdk "$sdk" \ + # -target "${arch}-apple-macos${deployment_target}" \ + # -o "${macos_dir}/GRASS" + clang -x objective-c "-mmacosx-version-min=${deployment_target}" \ + -target "${arch}-apple-macos${deployment_target}" \ + -mmacosx-version-min="$deployment_target" \ + -isysroot "$sdk" -fobjc-arc -Os \ + -o "${macos_dir}/GRASS" "${this_script_dir}/files/main.m" || exit_nice 1 + echo "GRASS_APP_BUNDLE created: $grass_app_bundle" + endgroup +} + +function patch_grass () { + begingroup "Apply patches" + echo "$inst_dir_patch" | patch -d "$grassdir" -p0 + endgroup +} + +function reset_grass_patches () { + begingroup "Reverting patches" + echo "Reverting patches..." + echo "$inst_dir_patch" | patch -d "$grassdir" -p0 -R + echo "Reverting patches done." + endgroup +} + +function set_up_conda () { + begingroup "Set up Conda" + mkdir -p "$cache_dir" + # move existing miniconda script to new external directory + if [ -f "${this_script_dir}/miniconda3.sh" ]; then + mv "${this_script_dir}/miniconda3.sh" "${cache_dir}/miniconda3.sh" + fi + + # download miniconda if not already existing + local miniconda="${cache_dir}/miniconda3-${arch}.sh" + if [ ! -f "$miniconda" ]; then + curl -L "$miniconda_url" --output "$miniconda" || exit_nice 1 cleanup + fi + + $bash "$miniconda" -b -f -p "$conda_temp_dir" + if [ ! -f "$conda_bin" ]; then + echo "Error, could not find conda binary file at ${conda_bin}" + exit_nice 1 cleanup + fi + + $conda_bin create --yes -p "${grass_app_bundle}/Contents/Resources" \ + --file="${conda_req_file}" -c conda-forge || exit_nice 1 cleanup + + export PATH="${grass_app_bundle}/Contents/Resources/bin:$PATH" + + # remove, causing Notarization issues + rm -rf "${grass_app_bundle}/Contents/Resources/share/gdb" + endgroup +} + +function install_grass_session () { + begingroup "Install grass-session" + local python_bin="${grass_app_bundle}/Contents/Resources/bin/python" + $python_bin -m pip install --upgrade pip + $python_bin -m pip install grass-session + endgroup +} + +function create_dmg () { + begingroup "Create dmg" + echo + echo "Create dmg file of $grass_app_bundle ..." + + if [ ! -d "$grass_app_bundle" ]; then + echo "Error, attempt to create dmg file, but no app could be found" + exit_nice 1 + fi + + local dmg_size + local dmg_tmpfile + local exact_app_size + local tmpdir + + if [[ "$CI" == "true" ]]; then + tmpdir="$RUNNER_TEMP" + else + tmpdir=$(mktemp -d /tmp/org.osgeo.grass.XXXXXX) + fi + dmg_tmpfile=${tmpdir}/grass-tmp-$$.dmg + exact_app_size=$(du -ks "$grass_app_bundle" | cut -f 1) + dmg_size=$((exact_app_size*120/100)) + + if [[ "$CI" == "true" ]] + then + # workaround for sometimes failed attempts on macos-13 (x86_64) runner + local max_attempts=10 + local i=0 + until hdiutil create -srcfolder "$grass_app_bundle" \ + -volname "$dmg_title" \ + -fs HFS+ \ + -fsargs "-c c=64,a=16,e=16" \ + -format UDRW \ + -size ${dmg_size}k "$dmg_tmpfile" + do + if [ $i -eq $max_attempts ]; then + echo "Error: hdiutil failed after ${max_attempts} attempts." + exit 1 + fi + i=$((i+1)) + done + else + if ! hdiutil create -srcfolder "$grass_app_bundle" \ + -volname "$dmg_title" \ + -fs HFS+ \ + -fsargs "-c c=64,a=16,e=16" \ + -format UDRW \ + -size ${dmg_size}k "$dmg_tmpfile" + then + rm -rf "$tmpdir" + exit_nice 1 + fi + fi + + if [[ "$CI" != "true" ]]; then + DEVICE=$(hdiutil attach -readwrite -noverify -noautoopen "${dmg_tmpfile}" | grep -E '^/dev/' | sed -e "s/^\/dev\///g" -e 1q | awk '{print $1}') + hdiutil attach "$dmg_tmpfile" || error "Can't attach temp DMG" + + mkdir -p "/Volumes/${dmg_title}/.background" + cp -p "${this_script_dir}/files/dmg-background.png" \ + "/Volumes/${dmg_title}/.background/background.png" + + osascript << EOF +tell application "Finder" + tell disk "$dmg_title" + open + set current view of container window to icon view + set toolbar visible of container window to false + set statusbar visible of container window to false + set the bounds of container window to {400, 100, 1040, 460} + set theViewOptions to the icon view options of container window + set arrangement of theViewOptions to not arranged + set icon size of theViewOptions to 100 + set background picture of theViewOptions to file ".background:background.png" + make new alias file at container window to POSIX file "/Applications" with properties {name:"Applications"} + set position of item "$grass_app_name" of container window to {187, 163} + set position of item "Applications" of container window to {452, 163} + update without registering applications + delay 5 + close + end tell +end tell +EOF + + sync + sync + sleep 3 + hdiutil detach "$DEVICE" + fi + + DMG_FILE="${dmg_out_dir}/${dmg_name}" + + if ! hdiutil convert "${dmg_tmpfile}" \ + -format UDZO -imagekey zlib-level=9 -o "${DMG_FILE}" + then + [[ "$CI" != "true" ]] && rm -rf "$tmpdir" + exit_nice 1 + fi + + [[ "$CI" != "true" ]] && rm -rf "$tmpdir" + + echo + [[ "$CI" == "true" ]] && echo "DMG_NAME=${dmg_name}" >> "$GITHUB_ENV" + [[ "$CI" == "true" ]] && echo "DMG_FILE=${DMG_FILE}" >> "$GITHUB_ENV" + endgroup +} + +function remove_dmg () { + local disk + if [ -d "/Volumes/${dmg_title}" ]; then + disk=$(diskutil list | grep "$dmg_title" | awk -F\ '{print $NF}') + diskutil unmount "$disk" + fi + rm -rf "${dmg_out_dir:?}/${dmg_name:?}" +} + +function codesign_app () { + local bins + local grass_libs + local libs + begingroup "Code sign app" + pushd "${grass_app_bundle}/Contents" > /dev/null || exit $? + + # remove build stage rpaths from grass libraries + + grass_libs=$(find ./Resources -type f \( -name "*libgrass_*.dylib" \)) + + while IFS= read -r file || [[ -n $file ]]; do + rpath=$(otool -l "$file" | grep "dist.*/lib" | awk '{$1=$1};1' | cut -d " " -f 2) + if [[ -n "$rpath" ]]; then + "$install_name_tool" -delete_rpath "$rpath" "$file" + fi + done < <(printf '%s\n' "$grass_libs") + + # codesign embedded libraries + + libs=$(find ./Resources -type f \( -name "*.so" -or -name "*.dylib" \)) + + while IFS= read -r file || [[ -n $file ]]; do + "$codesign" --sign "${cs_ident}" --force --verbose --timestamp "${file}" & + done < <(printf '%s\n' "$libs") + wait + + # codesign embedded binaries + + bins=$(find ./Resources -type f -perm +111 ! \( -name "*.so" -or -name "*.dylib" -or -name "*.a" \ + -or -name "*.py" -or -name "*.sh" \) -exec file '{}' \; | \ + grep "x86_64\|arm64" | \ + cut -d ":" -f 1 | \ + grep -v "for architecture") + wait + + while IFS= read -r file || [[ -n $file ]]; do + "$codesign" --sign "${cs_ident}" --force --verbose --timestamp --options runtime \ + --entitlements "$cs_entitlements" "${file}" & + done < <(printf '%s\n' "$bins") + wait + + # codesign "extra" files in ./MacOS directory + + "$codesign" --sign "${cs_ident}" --force --verbose --timestamp --options runtime \ + --entitlements "$cs_entitlements" "./MacOS/grass.sh" + "$codesign" --sign "${cs_ident}" --force --verbose --timestamp --options runtime \ + --entitlements "$cs_entitlements" "./MacOS/grass.scpt" + + cp "$cs_provisionprofile" embedded.provisionprofile + # xattr -r -d com.apple.FinderInfo embedded.provisionprofile + + popd > /dev/null || exit $? + + # codesign the app bundle + + "$codesign" --force --verbose --timestamp --sign "$cs_ident" --options runtime \ + --entitlements "$cs_entitlements" "$grass_app_bundle" + endgroup +} + +function codesign_dmg () { + begingroup "Code sign dmg" + "$codesign" --force --verbose --timestamp --sign "$cs_ident" --options runtime \ + --entitlements "$cs_entitlements" "${dmg_out_dir}/${dmg_name}" + endgroup +} + +function notarize_app () { + local tmpdir + local zip_tmpfile + + begingroup "Notarize app" + if [[ "$CI" == "true" ]]; then + tmpdir="$RUNNER_TEMP" + else + tmpdir=$(mktemp -d /tmp/org.osgeo.grass.XXXXXX) + fi + zip_tmpfile="${tmpdir}/${grass_app_name}.zip" + + "$ditto" -c -k --keepParent "$grass_app_bundle" "$zip_tmpfile" + + "$xcrun" notarytool submit "$zip_tmpfile" \ + --keychain-profile "$cs_keychain_profile" --wait || exit 1 + + "$xcrun" stapler staple "$grass_app_bundle" + "$xcrun" stapler validate "$grass_app_bundle" + + if [[ "$CI" == "true" ]]; then + rm -rf "$zip_tmpfile" + else + rm -rf "$tmpdir" + fi + endgroup +} + +function notarize_dmg () { + begingroup "Notarize dmg" + "$xcrun" notarytool submit "${dmg_out_dir}/${dmg_name}" \ + --keychain-profile "$cs_keychain_profile" --wait + + "$xcrun" stapler staple "${dmg_out_dir}/${dmg_name}" + endgroup +} + +############################################################################# +# Read script arguments +############################################################################# + +while [ "$1" != "" ]; do + case $1 in + -s | --sdk ) shift + sdk=$1 + ;; + -t | --target ) shift + deployment_target=$1 + ;; + -o | --dmg-out-dir ) shift + dmg_out_dir=$1 + ;; + -c | --conda-file ) shift + conda_req_file=$1 + ;; + --with-liblas ) + with_liblas=1 + ;; + -r | --repackage ) + repackage=1 + ;; + -u | --update-conda-stable ) + conda_update_stable=1 + ;; + --notarize ) + notarize=1 + ;; + -h | --help ) + display_usage + exit 0 + ;; + *) + # unknown option + echo "ERROR" + display_usage + exit 1 + ;; + esac + shift +done + +############################################################################# +# Check arguments and files +############################################################################# + +# make full path of CONDA_REQ_FILE +conda_req_file=$(cd "$(dirname "${conda_req_file}")" && pwd)/$(basename "$conda_req_file") + +if [[ ! -d "$sdk" && -f "$xcrun" ]]; then + sdk=$("$xcrun" --show-sdk-path) +fi + +if [ ! -f "${sdk}/SDKSettings.plist" ]; then + echo "Error, could not find valid MacOS SDK at $sdk" + display_usage + exit_nice 1 +fi + +# if DEPLOYMENT_TARGET hasn't been set, extract from SDK +if [ -z "$deployment_target" ]; then + deployment_target=$(plutil -extract DefaultProperties.MACOSX_DEPLOYMENT_TARGET xml1 \ +-o - "${sdk}/SDKSettings.plist" | awk -F '[<>]' '/string/{print $3}') +fi + +read_grass_version +set_bundle_version + +if [[ ! -d "$patch_dir" && "${grass_version_major}${grass_version_minor}" -le 80 ]]; then + echo "Error, no patch directory \"$patch_dir\" found" + exit_nice 1 +fi + +if [[ -n "$dmg_out_dir" && ! -d "$dmg_out_dir" ]]; then + echo "Error, dmg output directory \"${dmg_out_dir}\" does not exist." + exit_nice 1 +fi + +if [[ -n "$dmg_out_dir" && -f "${dmg_out_dir}/${dmg_name}" ]]; then + echo "Warning, there exists a dmg file \"${dmg_name}\" in \"${dmg_out_dir}\"." + while true; do + read -r -p "Do you wish to delete it (y|n)? " yn + case $yn in + [Yy]* ) remove_dmg; break;; + [Nn]* ) exit_nice 0;; + * ) echo "Please answer yes or no.";; + esac + done +fi + +if [[ "$repackage" -eq 1 && ! -d "$grass_app_bundle" ]]; then + echo "Error, attempt to repackage a non-existing \"$grass_app_bundle\" app bundle." + exit_nice 1 +fi + +# updating the stable conda explicit requirement file is only allowed if +# files/conda-requirements-dev-[arm64|x86_64].txt is used, to keep the two files in sync +if [[ "$conda_update_stable" -eq 1 ]]; then + if [ "$conda_req_file" != "$conda_dev_file" ]; then + echo "Note, conda requirement file used is ${conda_dev_file}!" + conda_req_file="$conda_dev_file" + fi + grass_app_bundle=$(mktemp -d /tmp/org.osgeo.grass.XXXXXX) +fi + +# check if destination app bundle exists, with option to cancel if true +if [[ -d "$grass_app_bundle" && "$repackage" -eq 0 && "$conda_update_stable" -eq 0 ]]; then + echo "Warning, \"${grass_app_bundle}\" already exists." + while true; do + read -r -p "Do you wish to delete it (y|n)? " yn + case $yn in + [Yy]* ) rm -rf "$grass_app_bundle"; break;; + [Nn]* ) exit_nice 0;; + * ) echo "Please answer yes or no.";; + esac + done +fi + +if [[ "$notarize" -eq 1 && ( -z $cs_ident || -z $cs_keychain_profile || -z $cs_provisionprofile ) ]]; then + echo "Error, attempt to notarize the app without setting necessary" + echo "code signing identity, provision profile and keychain profile." + exit_nice 1 +fi + +if [[ "$notarize" -eq 1 && ! -f $cs_provisionprofile ]]; then + echo "Error, the provisioning profile file can not be found." + exit_nice 1 +fi + +############################################################################# +# Start setting up and compiling procedures +############################################################################# + +# only create a new dmg file of existing app bundle +if [[ -n "$dmg_out_dir" && "$repackage" -eq 1 ]]; then + create_dmg + if [[ "$notarize" -eq 1 ]]; then + codesign_dmg + notarize_dmg + fi + exit_nice 0 +fi + +make_app_bundle_dir + +set_up_conda + +# update the stable conda explicit (stable) requirement file and exit +if [[ "$conda_update_stable" -eq 1 ]]; then + $conda_bin list -p "${grass_app_bundle}/Contents/Resources" \ + --explicit > "$conda_stable_file" + rm -rf "$grass_app_bundle"; + exit_nice 0 +fi + +install_grass_session + +if [[ "$with_liblas" -eq 1 ]]; then + begingroup "Build and install libLAS" + source "${this_script_dir}/files/liblas-install.sh" + endgroup +fi + +patch_grass + +begingroup "Build and install GRASS GIS" +# configure and compile GRASS GIS + +pushd "$grassdir" > /dev/null || exit + +echo "Starting \"make distclean\"..." +make distclean &>/dev/null +echo "Finished \"make distclean\"" + +source "${this_script_dir}/files/configure-grass.sh" + +if ! make -j"$(sysctl -n hw.ncpu)" +then + echo "Compilation failed, you may need to reset the GRASS git repository." + echo "This can be made with: \"cd [grass-source-dir] && git reset --hard\"." + echo + popd > /dev/null || exit + exit_nice 1 +fi + +echo +echo "Start installation..." +if ! make install +then + echo "Installation failed, you may need to reset the GRASS git repository." + echo "This can be made with: \"cd [grass-source-dir] && git reset --hard\"." + echo + popd > /dev/null || exit + exit_nice 1 +fi +echo "Finished installation." + +popd > /dev/null || exit +endgroup + +reset_grass_patches + +# replace SDK with a unversioned one of Command Line Tools +file="${grass_app_bundle}/Contents/Resources/include/Make/Platform.make" +sed -i .bak "s|-isysroot ${sdk}|-isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk|g" "$file" \ + || rm -f "${file}.bak" + +begingroup "List of installed packages" +# print list of installed packages +echo "=================================================================" +echo +$conda_bin list -p "${grass_app_bundle}/Contents/Resources" +if [[ "$with_liblas" -eq 1 ]]; then + liblas_version=$("${grass_app_bundle}/Contents/Resources/bin/liblas-config" --version) + echo "libLAS ${liblas_version}" +fi +echo +echo "=================================================================" +endgroup + +if [[ "$CI" == "true" ]]; then + begingroup "Move app to RUNNER_TEMP" + ditto "$grass_app_bundle" "${RUNNER_TEMP}${grass_app_bundle}" + grass_app_bundle="${RUNNER_TEMP}${grass_app_bundle}" + endgroup +fi + +if [[ "$notarize" -eq 1 ]]; then + codesign_app + notarize_app +fi + +# create dmg file +if [[ -n "$dmg_out_dir" ]]; then + create_dmg + if [[ "$notarize" -eq 1 ]]; then + codesign_dmg + notarize_dmg + fi +fi + +exit_nice 0 cleanup diff --git a/macosx/app/AppIcon.icns b/macos/files/AppIcon.icns similarity index 100% rename from macosx/app/AppIcon.icns rename to macos/files/AppIcon.icns diff --git a/macosx/app/GRASSDocument_gxw.icns b/macos/files/GRASSDocument_gxw.icns similarity index 100% rename from macosx/app/GRASSDocument_gxw.icns rename to macos/files/GRASSDocument_gxw.icns diff --git a/macosx/app/GRASSDocument_gxw.svg b/macos/files/GRASSDocument_gxw.svg similarity index 100% rename from macosx/app/GRASSDocument_gxw.svg rename to macos/files/GRASSDocument_gxw.svg diff --git a/macosx/app/Info.plist.in b/macos/files/Info.plist.in similarity index 100% rename from macosx/app/Info.plist.in rename to macos/files/Info.plist.in diff --git a/.github/workflows/macos_dependencies.txt b/macos/files/conda-requirements-dev-arm64.txt similarity index 80% rename from .github/workflows/macos_dependencies.txt rename to macos/files/conda-requirements-dev-arm64.txt index 28e680d3ae5..c94d484e0dd 100644 --- a/.github/workflows/macos_dependencies.txt +++ b/macos/files/conda-requirements-dev-arm64.txt @@ -1,3 +1,4 @@ +boost cairo clangxx_osx-arm64 clang_osx-arm64 @@ -11,7 +12,6 @@ gettext ghostscript giflib git -ipython krb5 lastools libgdal-arrow-parquet @@ -28,6 +28,7 @@ libjpeg-turbo libpng libsvm libtiff +lld llvm-openmp matplotlib numpy<2 @@ -39,15 +40,10 @@ pkg-config ply postgresql proj -pytest -pytest-github-actions-annotate-failures -pytest-timeout -pytest-xdist -python python.app -pyyaml +python<3.13 setuptools six sqlite -wxpython +wxpython=4.2.2 zstd diff --git a/macos/files/conda-requirements-dev-x86_64.txt b/macos/files/conda-requirements-dev-x86_64.txt new file mode 100644 index 00000000000..f6dbe63902d --- /dev/null +++ b/macos/files/conda-requirements-dev-x86_64.txt @@ -0,0 +1,49 @@ +boost +cairo +clangxx_osx-64 +clang_osx-64 +cmake +expat +fftw +flex +freetype +geos +gettext +ghostscript +giflib +git +krb5 +lastools +libgdal-arrow-parquet +libgdal-core +libgdal-hdf4 +libgdal-hdf5 +libgdal-netcdf +libgdal-pdf +libgdal-pg +libgdal-postgisraster +libgdal-tiledb +libiconv +libjpeg-turbo +libpng +libsvm +libtiff +lld +llvm-openmp +matplotlib +numpy<2 +openblas +pandoc +pdal +pillow +pkg-config +ply +postgresql +proj +python.app +python<3.13 +setuptools +six +sqlite +wxpython=4.2.2 +zstd diff --git a/macos/files/conda-requirements-stable-arm64.txt b/macos/files/conda-requirements-stable-arm64.txt new file mode 100644 index 00000000000..d6f7c96b13d --- /dev/null +++ b/macos/files/conda-requirements-stable-arm64.txt @@ -0,0 +1,279 @@ +# This file may be used to create an environment using: +# $ conda create --name --file +# platform: osx-arm64 +# created-by: conda 24.11.2 +@EXPLICIT +https://conda.anaconda.org/conda-forge/osx-arm64/pandoc-3.6.2-hce30654_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.10.6-h5505292_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-h99b78c6_7.conda +https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.4-h5505292_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/ca-certificates-2024.12.14-hf0a4a13_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/dav1d-1.2.1-hb547adb_0.conda +https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda +https://conda.anaconda.org/conda-forge/osx-arm64/giflib-5.2.2-h93a5062_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/json-c-0.18-he4178ee_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libboost-headers-1.85.0-hce30654_4.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.1.0-hd74edd7_2.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-19.1.7-ha82da77_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.23-hec38601_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.6.4-h286801f_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libfabric1-2.0.0-h5505292_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.4.2-h3422bc3_5.tar.bz2 +https://conda.anaconda.org/conda-forge/osx-arm64/libiconv-1.17-h0d3ecfb_2.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.0.0-hb547adb_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.6.3-h39f12f2_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libntlm-1.8-h5505292_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libutf8proc-2.9.0-h5505292_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.50.0-h5505292_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.5.0-h2471fea_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda +https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-19.1.7-hdb05f8b_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/lzo-2.10-h93a5062_1001.conda +https://conda.anaconda.org/conda-forge/osx-arm64/m4-1.4.18-h642e427_1001.tar.bz2 +https://conda.anaconda.org/conda-forge/osx-arm64/metis-5.1.0-h15f6cfe_1007.conda +https://conda.anaconda.org/conda-forge/osx-arm64/mpi-1.0-openmpi.tar.bz2 +https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_2.conda +https://conda.anaconda.org/conda-forge/osx-arm64/perl-5.32.1-7_h4614cfb_perl5.conda +https://conda.anaconda.org/conda-forge/noarch/poppler-data-0.4.12-hd8ed1ab_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda +https://conda.anaconda.org/conda-forge/osx-arm64/python_abi-3.12-5_cp312.conda +https://conda.anaconda.org/conda-forge/osx-arm64/rav1e-0.6.6-h69fbcac_2.conda +https://conda.anaconda.org/conda-forge/osx-arm64/rhash-1.4.5-h7ab814d_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/tzcode-2025a-h5505292_0.conda +https://conda.anaconda.org/conda-forge/noarch/tzdata-2025a-h78e105d_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-h5505292_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hd74edd7_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/aom-3.9.1-h7bae524_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.0-hc8a0bd2_5.conda +https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.2-hc8a0bd2_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.2-hc8a0bd2_4.conda +https://conda.anaconda.org/conda-forge/osx-arm64/draco-1.5.7-h2ffa867_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/eigen-3.4.0-h1995070_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/expat-2.6.4-h286801f_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/flex-2.6.4-h1474e2a_1004.tar.bz2 +https://conda.anaconda.org/conda-forge/osx-arm64/fmt-11.0.2-h420ef59_0.conda +https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2 +https://conda.anaconda.org/conda-forge/osx-arm64/geos-3.13.0-hf9b8971_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/gflags-2.2.2-hf9b8971_1005.conda +https://conda.anaconda.org/conda-forge/osx-arm64/ghostscript-10.04.0-hf9b8971_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/gmp-6.3.0-h7bae524_2.conda +https://conda.anaconda.org/conda-forge/osx-arm64/hdf4-4.2.15-h2ee6834_7.conda +https://conda.anaconda.org/conda-forge/osx-arm64/lastools-v2.0.2-hebf3989_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.0.0-h9a09cb3_0.tar.bz2 +https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20240722.0-cxx17_h07bc746_4.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libaec-1.1.3-hebf3989_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libasprintf-0.22.5-h8414b35_3.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.1.0-hd74edd7_2.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.1.0-hd74edd7_2.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libcrc32c-1.1.2-hbdafb3b_0.tar.bz2 +https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-devel-19.1.7-h6dc3340_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libde265-1.0.15-h2ffa867_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20240808-pl5321hafb1f1b_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libfabric-2.0.0-hce30654_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-13.2.0-hf226fd6_3.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libintl-0.22.5-h8414b35_3.conda +https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-devel-5.6.3-h39f12f2_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.45-h3783ad8_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.48.0-h3f77e49_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libsvm-335-hf9b8971_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libxml2-2.13.5-h178c5d8_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/lz4-c-1.10.0-h286801f_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/nitro-2.7.dev8-h13dd4ca_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/nspr-4.36-h5833ebf_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.4.0-h81ee809_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/pcre2-10.44-h297a79d_2.conda +https://conda.anaconda.org/conda-forge/osx-arm64/pixman-0.44.2-h2f9eb0b_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/qhull-2020.2-h420ef59_5.conda +https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h92ec313_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.1-h98b9ce2_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/svt-av1-2.3.0-hf24288c_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/tapi-1300.6.5-h03f4b80_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h5083fa2_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/uriparser-0.9.8-h00cdb27_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/x265-3.5-hbc6ce65_3.tar.bz2 +https://conda.anaconda.org/conda-forge/osx-arm64/xerces-c-3.2.5-h92fc2f4_2.conda +https://conda.anaconda.org/conda-forge/osx-arm64/xz-gpl-tools-5.6.3-h9a6d368_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/xz-tools-5.6.3-h39f12f2_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/zlib-1.3.1-h8359307_2.conda +https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.6-hb46c0d2_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.8.1-hc8a0bd2_3.conda +https://conda.anaconda.org/conda-forge/osx-arm64/blosc-1.21.6-h7dd00d9_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.1.0-hd74edd7_2.conda +https://conda.anaconda.org/conda-forge/osx-arm64/capnproto-1.0.2-h221ca0e_3.conda +https://conda.anaconda.org/conda-forge/osx-arm64/cpd-0.5.5-h420ef59_2.conda +https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 +https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.12.1-hadb7bae_2.conda +https://conda.anaconda.org/conda-forge/osx-arm64/gettext-tools-0.22.5-h8414b35_3.conda +https://conda.anaconda.org/conda-forge/osx-arm64/glog-0.7.1-heb240a5_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libarchive-3.7.7-h3b16cec_3.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libasprintf-devel-0.22.5-h8414b35_3.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libavif16-1.1.1-h45b7238_2.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libevent-2.1.12-h2757513_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libgettextpo-0.22.5-h8414b35_3.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-5.0.0-13_2_0_hd922786_3.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libglib-2.82.2-hdff4504_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libhwloc-2.11.2-default_hbce5d74_1001.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libintl-devel-0.22.5-h8414b35_3.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libkml-1.3.0-he250239_1021.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libllvm19-19.1.7-hc4b4ae8_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.64.0-h6d7220d_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-5.28.3-h3bd63a1_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2024.07.02-h07bc746_2.conda +https://conda.anaconda.org/conda-forge/osx-arm64/librttopo-1.1.0-ha2cf0f4_17.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h9cc3647_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.0-h551f018_3.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libxslt-1.1.39-h223e5b9_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libzip-1.11.2-h1336266_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/minizip-4.0.7-hff1a8ea_3.conda +https://conda.anaconda.org/conda-forge/osx-arm64/mpfr-4.2.1-hb693164_3.conda +https://conda.anaconda.org/conda-forge/osx-arm64/nss-3.107-hc555b47_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.8-hc22306f_1_cpython.conda +https://conda.anaconda.org/conda-forge/osx-arm64/sigtool-0.1.3-h44b9a77_0.tar.bz2 +https://conda.anaconda.org/conda-forge/osx-arm64/spdlog-1.15.0-h096ffd4_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/sqlite-3.48.0-hd7222ec_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/xz-5.6.3-h9a6d368_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.15.3-haba67d1_6.conda +https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.1.0-hd74edd7_2.conda +https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/cyrus-sasl-2.1.27-h60b93bd_7.conda +https://conda.anaconda.org/conda-forge/osx-arm64/fftw-3.3.10-nompi_h6637ab6_110.conda +https://conda.anaconda.org/conda-forge/osx-arm64/fontconfig-2.15.0-h1383a14_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/freexl-2.0.0-h3ab3353_2.conda +https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.4.8-py312h2c4a281_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.16-ha0e7c42_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/ld64_osx-arm64-951.9-hb91ea2e_2.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libboost-1.85.0-hf763ba5_4.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libclang-cpp19.1-19.1.7-default_hf90f093_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.11.1-h73640d1_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libgettextpo-devel-0.22.5-h8414b35_3.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libheif-1.19.5-gpl_h297b2c4_100.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.28-openmp_hf332438_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libpmix-5.0.6-h6500a5a_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libsuitesparseconfig-7.8.3-ss783_h714a54a.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libthrift-0.21.0-h64651cc_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/lld-19.1.7-hc093e66_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/llvm-tools-19-19.1.7-h87a4c7e_0.conda +https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2 +https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.3-h8a3d83b_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/orc-2.0.3-h0ff2369_2.conda +https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda +https://conda.anaconda.org/conda-forge/osx-arm64/pkg-config-0.29.2-hde07d2e_1009.conda +https://conda.anaconda.org/conda-forge/noarch/ply-3.11-pyhd8ed1ab_3.conda +https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.1-pyhd8ed1ab_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/python.app-1.4-py312h830b0dc_4.conda +https://conda.anaconda.org/conda-forge/osx-arm64/re2-2024.07.02-h6589ca4_2.conda +https://conda.anaconda.org/conda-forge/noarch/setuptools-75.8.0-pyhff2d567_0.conda +https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.4.2-py312hea69d52_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-16.0.0-py312hea69d52_0.conda +https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-event-stream-0.5.0-h54f970a_11.conda +https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.9.2-h96aa502_4.conda +https://conda.anaconda.org/conda-forge/osx-arm64/azure-core-cpp-1.14.0-hd50102c_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/cairo-1.18.2-h6a3b0d2_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/clang-19-19.1.7-default_hf90f093_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/cmake-3.31.4-ha25475f_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/fonttools-4.55.3-py312h998013c_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/gettext-0.22.5-h8414b35_3.conda +https://conda.anaconda.org/conda-forge/osx-arm64/git-2.47.1-pl5321hd71a902_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-1.14.3-nompi_ha698983_108.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libamd-3.3.3-ss783_h6dbf161.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.9.0-26_osxarm64_openblas.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libboost-devel-1.85.0-hf450f58_4.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libbtf-2.3.2-ss783_h6c9afe8.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libcamd-3.3.3-ss783_h6c9afe8.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libccolamd-3.3.4-ss783_h6c9afe8.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libcolamd-3.3.4-ss783_h6c9afe8.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libcxsparse-4.4.1-ss783_hbf61d5d.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.67.1-h0a426d6_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libldl-3.3.2-ss783_h6c9afe8.conda +https://conda.anaconda.org/conda-forge/osx-arm64/librbio-4.3.4-ss783_h6c9afe8.conda +https://conda.anaconda.org/conda-forge/osx-arm64/llvm-tools-19.1.7-hd2aecb6_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/openblas-0.3.28-openmp_hea878ba_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/openldap-2.6.9-hbe55e7a_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/openmpi-5.0.6-hc946d3c_101.conda +https://conda.anaconda.org/conda-forge/osx-arm64/pillow-11.1.0-py312h50aef2c_0.conda +https://conda.anaconda.org/conda-forge/noarch/pip-24.3.1-pyh8b19718_2.conda +https://conda.anaconda.org/conda-forge/osx-arm64/proj-9.5.1-h1318a7e_0.conda +https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/wxwidgets-3.2.6-h6a86719_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.8.0-hfc2798a_16.conda +https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-mqtt-0.11.0-h24f418c_12.conda +https://conda.anaconda.org/conda-forge/osx-arm64/azure-identity-cpp-1.10.0-hc602bab_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-common-cpp-12.8.0-h9ca1f76_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/cctools_osx-arm64-1010.6-h3f5b1a0_2.conda +https://conda.anaconda.org/conda-forge/osx-arm64/clang-19.1.7-default_h474c9e2_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/fgt-0.4.11-h745860c_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/geotiff-1.7.3-h82bf549_3.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.9.0-26_osxarm64_openblas.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-2.33.0-hdbe95d5_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.9.0-26_osxarm64_openblas.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libnetcdf-4.9.2-nompi_h610d594_116.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libpq-17.2-ha9b7db8_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libspatialite-5.1.0-hf92fc0a_12.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libspex-3.2.1-ss783_h30f3287.conda +https://conda.anaconda.org/conda-forge/osx-arm64/poppler-24.12.0-ha29e788_2.conda +https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.7.9-h1be5864_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-blobs-cpp-12.13.0-h7585a09_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/clangxx-19.1.7-default_h1ffe849_0.conda +https://conda.anaconda.org/conda-forge/noarch/compiler-rt_osx-arm64-19.1.7-h7969c41_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libcholmod-5.3.0-ss783_h87d6651.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-core-3.10.1-h9ef0d2d_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libgoogle-cloud-storage-2.33.0-h7081f7f_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.26.4-py312h8442bc7_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/postgresql-17.2-h0e1cb1d_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/aws-crt-cpp-0.29.9-h1ced3ac_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/azure-storage-files-datalake-cpp-12.12.0-hcdd55da_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/compiler-rt-19.1.7-hd2aecb6_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.1-py312hb23fbb9_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libboost-python-1.85.0-py312hffe1f2a_4.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-hdf4-3.10.1-hd589a83_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-hdf5-3.10.1-h8e86020_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-pdf-3.10.1-hb9cf988_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-pg-3.10.1-h98ad515_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-postgisraster-3.10.1-h98ad515_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libklu-2.3.5-ss783_h4a7adf4.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libpdal-core-2.8.3-ha226718_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libspqr-4.3.4-ss783_h93d26d6.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libumfpack-6.3.5-ss783_h852ec90.conda +https://conda.anaconda.org/conda-forge/osx-arm64/wxpython-4.2.2-py312hd8f9ff3_3.conda +https://conda.anaconda.org/conda-forge/osx-arm64/aws-sdk-cpp-1.11.488-h0e5014b_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/clang_impl_osx-arm64-19.1.7-h76e6a08_23.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libboost-python-devel-1.85.0-py312ha814d7c_4.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-netcdf-3.10.1-h3ef4abb_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libparu-1.0.0-ss783_hf1d7083.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libpdal-cpd-2.8.3-h286801f_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libpdal-draco-2.8.3-h286801f_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libpdal-e57-2.8.3-he43c3ef_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libpdal-icebridge-2.8.3-h137fbe7_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libpdal-nitf-2.8.3-h61de071_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libpdal-pgpointcloud-2.8.3-h8f22ee4_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.10.0-py312hdbc7e53_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/python-pdal-3.4.5-py312h6f04bcc_13.conda +https://conda.anaconda.org/conda-forge/osx-arm64/boost-1.85.0-ha814d7c_4.conda +https://conda.anaconda.org/conda-forge/osx-arm64/clang_osx-arm64-19.1.7-h07b0088_23.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-18.1.0-h85c24c5_10_cpu.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libpdal-hdf-2.8.3-h137fbe7_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.10.0-py312h1f38498_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/suitesparse-7.8.3-ss783_h3b2878a.conda +https://conda.anaconda.org/conda-forge/osx-arm64/tiledb-2.27.0-he1ce1b3_6.conda +https://conda.anaconda.org/conda-forge/osx-arm64/ceres-solver-2.2.0-h30efb5c_5.conda +https://conda.anaconda.org/conda-forge/osx-arm64/clangxx_impl_osx-arm64-19.1.7-h276745f_23.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-acero-18.1.0-hf07054f_10_cpu.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-tiledb-3.10.1-h3b22183_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libparquet-18.1.0-h636d7b7_10_cpu.conda +https://conda.anaconda.org/conda-forge/osx-arm64/clangxx_osx-arm64-19.1.7-h07b0088_23.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libarrow-dataset-18.1.0-hf07054f_10_cpu.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libpdal-tiledb-2.8.3-hb24f26d_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libpdal-trajectory-2.8.3-h0cbbbd6_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libgdal-arrow-parquet-3.10.1-h43e3b2e_1.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libpdal-arrow-2.8.3-h35fe574_0.conda +https://conda.anaconda.org/conda-forge/osx-arm64/libpdal-2.8.3-hce30654_0.conda +https://conda.anaconda.org/conda-forge/noarch/pdal-2.8.3-hd8ed1ab_0.conda diff --git a/macos/files/conda-requirements-stable-x86_64.txt b/macos/files/conda-requirements-stable-x86_64.txt new file mode 100644 index 00000000000..ce0b9e76cb3 --- /dev/null +++ b/macos/files/conda-requirements-stable-x86_64.txt @@ -0,0 +1,279 @@ +# This file may be used to create an environment using: +# $ conda create --name --file +# platform: osx-64 +# created-by: conda 24.11.2 +@EXPLICIT +https://conda.anaconda.org/conda-forge/osx-64/pandoc-3.6.2-h694c41f_0.conda +https://conda.anaconda.org/conda-forge/osx-64/aws-c-common-0.10.6-h6e16a3a_0.conda +https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-hfdf4475_7.conda +https://conda.anaconda.org/conda-forge/osx-64/c-ares-1.34.4-hf13058a_0.conda +https://conda.anaconda.org/conda-forge/osx-64/ca-certificates-2024.12.14-h8857fd0_0.conda +https://conda.anaconda.org/conda-forge/osx-64/dav1d-1.2.1-h0dc2134_0.conda +https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 +https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda +https://conda.anaconda.org/conda-forge/osx-64/giflib-5.2.2-h10d778d_0.conda +https://conda.anaconda.org/conda-forge/osx-64/icu-75.1-h120a0e1_0.conda +https://conda.anaconda.org/conda-forge/osx-64/json-c-0.18-hc62ec3d_0.conda +https://conda.anaconda.org/conda-forge/osx-64/libboost-headers-1.85.0-h694c41f_4.conda +https://conda.anaconda.org/conda-forge/osx-64/libbrotlicommon-1.1.0-h00291cd_2.conda +https://conda.anaconda.org/conda-forge/osx-64/libcxx-19.1.7-hf95d169_0.conda +https://conda.anaconda.org/conda-forge/osx-64/libdeflate-1.23-he65b83e_0.conda +https://conda.anaconda.org/conda-forge/osx-64/libev-4.33-h10d778d_2.conda +https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.6.4-h240833e_0.conda +https://conda.anaconda.org/conda-forge/osx-64/libfabric1-2.0.0-h6e16a3a_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libffi-3.4.2-h0d85af4_5.tar.bz2 +https://conda.anaconda.org/conda-forge/osx-64/libiconv-1.17-hd75f5a5_2.conda +https://conda.anaconda.org/conda-forge/osx-64/libjpeg-turbo-3.0.0-h0dc2134_1.conda +https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.6.3-hd471939_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libntlm-1.8-h6e16a3a_0.conda +https://conda.anaconda.org/conda-forge/osx-64/libutf8proc-2.10.0-h777c5d8_0.conda +https://conda.anaconda.org/conda-forge/osx-64/libuv-1.50.0-h4cb831e_0.conda +https://conda.anaconda.org/conda-forge/osx-64/libwebp-base-1.5.0-h6cf52b4_0.conda +https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.1-hd23fc13_2.conda +https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-19.1.7-ha54dae1_0.conda +https://conda.anaconda.org/conda-forge/osx-64/lzo-2.10-h10d778d_1001.conda +https://conda.anaconda.org/conda-forge/osx-64/m4-1.4.18-haf1e3a3_1001.tar.bz2 +https://conda.anaconda.org/conda-forge/osx-64/metis-5.1.0-h3023b02_1007.conda +https://conda.anaconda.org/conda-forge/osx-64/mpi-1.0-openmpi.tar.bz2 +https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-h0622a9a_2.conda +https://conda.anaconda.org/conda-forge/osx-64/perl-5.32.1-7_h10d778d_perl5.conda +https://conda.anaconda.org/conda-forge/noarch/poppler-data-0.4.12-hd8ed1ab_0.conda +https://conda.anaconda.org/conda-forge/osx-64/pthread-stubs-0.4-h00291cd_1002.conda +https://conda.anaconda.org/conda-forge/osx-64/python_abi-3.12-5_cp312.conda +https://conda.anaconda.org/conda-forge/osx-64/rav1e-0.6.6-h7205ca4_2.conda +https://conda.anaconda.org/conda-forge/osx-64/rhash-1.4.5-ha44c9a9_0.conda +https://conda.anaconda.org/conda-forge/osx-64/tzcode-2025a-h6e16a3a_0.conda +https://conda.anaconda.org/conda-forge/noarch/tzdata-2025a-h78e105d_0.conda +https://conda.anaconda.org/conda-forge/osx-64/xorg-libxau-1.0.12-h6e16a3a_0.conda +https://conda.anaconda.org/conda-forge/osx-64/xorg-libxdmcp-1.1.5-h00291cd_0.conda +https://conda.anaconda.org/conda-forge/osx-64/aom-3.9.1-hf036a51_0.conda +https://conda.anaconda.org/conda-forge/osx-64/aws-c-compression-0.3.0-hc0df2db_5.conda +https://conda.anaconda.org/conda-forge/osx-64/aws-c-sdkutils-0.2.2-hc0df2db_0.conda +https://conda.anaconda.org/conda-forge/osx-64/aws-checksums-0.2.2-hc0df2db_4.conda +https://conda.anaconda.org/conda-forge/osx-64/draco-1.5.7-h7728843_0.conda +https://conda.anaconda.org/conda-forge/osx-64/eigen-3.4.0-h1c7c39f_0.conda +https://conda.anaconda.org/conda-forge/osx-64/expat-2.6.4-h240833e_0.conda +https://conda.anaconda.org/conda-forge/osx-64/flex-2.6.4-hcc69e7b_1004.tar.bz2 +https://conda.anaconda.org/conda-forge/osx-64/fmt-11.0.2-h3c5361c_0.conda +https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2 +https://conda.anaconda.org/conda-forge/osx-64/geos-3.13.0-hac325c4_0.conda +https://conda.anaconda.org/conda-forge/osx-64/gflags-2.2.2-hac325c4_1005.conda +https://conda.anaconda.org/conda-forge/osx-64/ghostscript-10.04.0-hac325c4_0.conda +https://conda.anaconda.org/conda-forge/osx-64/gmp-6.3.0-hf036a51_2.conda +https://conda.anaconda.org/conda-forge/osx-64/hdf4-4.2.15-h8138101_7.conda +https://conda.anaconda.org/conda-forge/osx-64/lastools-20171231-h046ec9c_1002.tar.bz2 +https://conda.anaconda.org/conda-forge/osx-64/lerc-4.0.0-hb486fe8_0.tar.bz2 +https://conda.anaconda.org/conda-forge/osx-64/libabseil-20240722.0-cxx17_h0e468a2_4.conda +https://conda.anaconda.org/conda-forge/osx-64/libaec-1.1.3-h73e2aa4_0.conda +https://conda.anaconda.org/conda-forge/osx-64/libasprintf-0.22.5-hdfe23c8_3.conda +https://conda.anaconda.org/conda-forge/osx-64/libbrotlidec-1.1.0-h00291cd_2.conda +https://conda.anaconda.org/conda-forge/osx-64/libbrotlienc-1.1.0-h00291cd_2.conda +https://conda.anaconda.org/conda-forge/osx-64/libcrc32c-1.1.2-he49afe7_0.tar.bz2 +https://conda.anaconda.org/conda-forge/osx-64/libcxx-devel-19.1.7-h7c275be_0.conda +https://conda.anaconda.org/conda-forge/osx-64/libde265-1.0.15-h7728843_0.conda +https://conda.anaconda.org/conda-forge/osx-64/libedit-3.1.20240808-pl5321ha958ccf_0.conda +https://conda.anaconda.org/conda-forge/osx-64/libfabric-2.0.0-h694c41f_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-13.2.0-h2873a65_3.conda +https://conda.anaconda.org/conda-forge/osx-64/libintl-0.22.5-hdfe23c8_3.conda +https://conda.anaconda.org/conda-forge/osx-64/liblzma-devel-5.6.3-hd471939_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libpng-1.6.45-h3c4a55f_0.conda +https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.48.0-hdb6dae5_0.conda +https://conda.anaconda.org/conda-forge/osx-64/libsvm-335-hac325c4_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libxcb-1.17.0-hf1f96e2_0.conda +https://conda.anaconda.org/conda-forge/osx-64/libxml2-2.13.5-hebb159f_1.conda +https://conda.anaconda.org/conda-forge/osx-64/lz4-c-1.10.0-h240833e_1.conda +https://conda.anaconda.org/conda-forge/osx-64/nitro-2.7.dev8-he965462_0.conda +https://conda.anaconda.org/conda-forge/osx-64/nspr-4.36-h97d8b74_0.conda +https://conda.anaconda.org/conda-forge/osx-64/openssl-3.4.0-hc426f3f_1.conda +https://conda.anaconda.org/conda-forge/osx-64/pcre2-10.44-h7634a1b_2.conda +https://conda.anaconda.org/conda-forge/osx-64/pixman-0.44.2-h1fd1274_0.conda +https://conda.anaconda.org/conda-forge/osx-64/pkg-config-0.29.2-hf7e621a_1009.conda +https://conda.anaconda.org/conda-forge/osx-64/qhull-2020.2-h3c5361c_5.conda +https://conda.anaconda.org/conda-forge/osx-64/readline-8.2-h9e318b2_1.conda +https://conda.anaconda.org/conda-forge/osx-64/snappy-1.2.1-haf3c120_1.conda +https://conda.anaconda.org/conda-forge/osx-64/svt-av1-2.3.0-h97d8b74_0.conda +https://conda.anaconda.org/conda-forge/osx-64/tapi-1300.6.5-h390ca13_0.conda +https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h1abcd95_1.conda +https://conda.anaconda.org/conda-forge/osx-64/uriparser-0.9.8-h6aefe2f_0.conda +https://conda.anaconda.org/conda-forge/osx-64/x265-3.5-hbb4e6a2_3.tar.bz2 +https://conda.anaconda.org/conda-forge/osx-64/xerces-c-3.2.5-h197e74d_2.conda +https://conda.anaconda.org/conda-forge/osx-64/xz-gpl-tools-5.6.3-h357f2ed_1.conda +https://conda.anaconda.org/conda-forge/osx-64/xz-tools-5.6.3-hd471939_1.conda +https://conda.anaconda.org/conda-forge/osx-64/zlib-1.3.1-hd23fc13_2.conda +https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.6-h915ae27_0.conda +https://conda.anaconda.org/conda-forge/osx-64/aws-c-cal-0.8.1-hc0df2db_3.conda +https://conda.anaconda.org/conda-forge/osx-64/blosc-1.21.6-hd145fbb_1.conda +https://conda.anaconda.org/conda-forge/osx-64/brotli-bin-1.1.0-h00291cd_2.conda +https://conda.anaconda.org/conda-forge/osx-64/capnproto-1.0.2-h1c0ecac_3.conda +https://conda.anaconda.org/conda-forge/osx-64/cpd-0.5.5-h3c5361c_2.conda +https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 +https://conda.anaconda.org/conda-forge/osx-64/freetype-2.12.1-h60636b9_2.conda +https://conda.anaconda.org/conda-forge/osx-64/gettext-tools-0.22.5-hdfe23c8_3.conda +https://conda.anaconda.org/conda-forge/osx-64/glog-0.7.1-h2790a97_0.conda +https://conda.anaconda.org/conda-forge/osx-64/krb5-1.21.3-h37d8d59_0.conda +https://conda.anaconda.org/conda-forge/osx-64/libarchive-3.7.7-h1a33361_3.conda +https://conda.anaconda.org/conda-forge/osx-64/libasprintf-devel-0.22.5-hdfe23c8_3.conda +https://conda.anaconda.org/conda-forge/osx-64/libavif16-1.1.1-h71406da_2.conda +https://conda.anaconda.org/conda-forge/osx-64/libevent-2.1.12-ha90c15b_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libgettextpo-0.22.5-hdfe23c8_3.conda +https://conda.anaconda.org/conda-forge/osx-64/libgfortran-5.0.0-13_2_0_h97931a8_3.conda +https://conda.anaconda.org/conda-forge/osx-64/libglib-2.82.2-h5c976ab_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libhwloc-2.11.2-default_h4cdd727_1001.conda +https://conda.anaconda.org/conda-forge/osx-64/libintl-devel-0.22.5-hdfe23c8_3.conda +https://conda.anaconda.org/conda-forge/osx-64/libkml-1.3.0-h9ee1731_1021.conda +https://conda.anaconda.org/conda-forge/osx-64/libllvm19-19.1.7-hc29ff6c_0.conda +https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.64.0-hc7306c3_0.conda +https://conda.anaconda.org/conda-forge/osx-64/libprotobuf-5.28.3-h6401091_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libre2-11-2024.07.02-h0e468a2_2.conda +https://conda.anaconda.org/conda-forge/osx-64/librttopo-1.1.0-hdfb80b9_17.conda +https://conda.anaconda.org/conda-forge/osx-64/libssh2-1.11.1-h3dc7d44_0.conda +https://conda.anaconda.org/conda-forge/osx-64/libtiff-4.7.0-hb77a491_3.conda +https://conda.anaconda.org/conda-forge/osx-64/libxslt-1.1.39-h03b04e6_0.conda +https://conda.anaconda.org/conda-forge/osx-64/libzip-1.11.2-h31df5bb_0.conda +https://conda.anaconda.org/conda-forge/osx-64/minizip-4.0.7-hfb7a1ec_3.conda +https://conda.anaconda.org/conda-forge/osx-64/mpfr-4.2.1-haed47dc_3.conda +https://conda.anaconda.org/conda-forge/osx-64/nss-3.107-h81a00e3_0.conda +https://conda.anaconda.org/conda-forge/osx-64/python-3.12.8-h9ccd52b_1_cpython.conda +https://conda.anaconda.org/conda-forge/osx-64/sigtool-0.1.3-h88f4db0_0.tar.bz2 +https://conda.anaconda.org/conda-forge/osx-64/spdlog-1.15.0-h0ec5880_0.conda +https://conda.anaconda.org/conda-forge/osx-64/sqlite-3.48.0-h2e4c9dc_0.conda +https://conda.anaconda.org/conda-forge/osx-64/xz-5.6.3-h357f2ed_1.conda +https://conda.anaconda.org/conda-forge/osx-64/aws-c-io-0.15.3-h7bd4489_6.conda +https://conda.anaconda.org/conda-forge/osx-64/brotli-1.1.0-h00291cd_2.conda +https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda +https://conda.anaconda.org/conda-forge/osx-64/cyrus-sasl-2.1.27-hf9bab2b_7.conda +https://conda.anaconda.org/conda-forge/osx-64/fftw-3.3.10-nompi_h292e606_110.conda +https://conda.anaconda.org/conda-forge/osx-64/fontconfig-2.15.0-h37eeddb_1.conda +https://conda.anaconda.org/conda-forge/osx-64/freexl-2.0.0-h3183152_2.conda +https://conda.anaconda.org/conda-forge/osx-64/kiwisolver-1.4.8-py312h9275861_0.conda +https://conda.anaconda.org/conda-forge/osx-64/lcms2-2.16-ha2f27b4_0.conda +https://conda.anaconda.org/conda-forge/osx-64/ld64_osx-64-951.9-h9d2de3d_2.conda +https://conda.anaconda.org/conda-forge/osx-64/libboost-1.85.0-hcca3243_4.conda +https://conda.anaconda.org/conda-forge/osx-64/libclang-cpp19.1-19.1.7-default_h3571c67_0.conda +https://conda.anaconda.org/conda-forge/osx-64/libcurl-8.11.1-h5dec5d8_0.conda +https://conda.anaconda.org/conda-forge/osx-64/libgettextpo-devel-0.22.5-hdfe23c8_3.conda +https://conda.anaconda.org/conda-forge/osx-64/libheif-1.19.5-gpl_hc62a4a2_100.conda +https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.28-openmp_hbf64a52_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libpmix-5.0.6-h2a508f7_0.conda +https://conda.anaconda.org/conda-forge/osx-64/libsuitesparseconfig-7.8.3-ss783_he344b7b.conda +https://conda.anaconda.org/conda-forge/osx-64/libthrift-0.21.0-h75589b3_0.conda +https://conda.anaconda.org/conda-forge/osx-64/lld-19.1.7-hcd1802e_0.conda +https://conda.anaconda.org/conda-forge/osx-64/llvm-tools-19-19.1.7-he90a8e3_0.conda +https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2 +https://conda.anaconda.org/conda-forge/osx-64/openjpeg-2.5.3-h7fd6d84_0.conda +https://conda.anaconda.org/conda-forge/osx-64/orc-2.0.3-h85ea3fe_2.conda +https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda +https://conda.anaconda.org/conda-forge/noarch/ply-3.11-pyhd8ed1ab_3.conda +https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.1-pyhd8ed1ab_0.conda +https://conda.anaconda.org/conda-forge/osx-64/python.app-1.4-py312hb553811_4.conda +https://conda.anaconda.org/conda-forge/osx-64/re2-2024.07.02-ha5e900a_2.conda +https://conda.anaconda.org/conda-forge/noarch/setuptools-75.8.0-pyhff2d567_0.conda +https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda +https://conda.anaconda.org/conda-forge/osx-64/tornado-6.4.2-py312h01d7ebd_0.conda +https://conda.anaconda.org/conda-forge/osx-64/unicodedata2-16.0.0-py312h01d7ebd_0.conda +https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda +https://conda.anaconda.org/conda-forge/osx-64/aws-c-event-stream-0.5.0-h8236443_11.conda +https://conda.anaconda.org/conda-forge/osx-64/aws-c-http-0.9.2-h5492b4a_4.conda +https://conda.anaconda.org/conda-forge/osx-64/azure-core-cpp-1.14.0-h9a36307_0.conda +https://conda.anaconda.org/conda-forge/osx-64/cairo-1.18.2-h950ec3b_1.conda +https://conda.anaconda.org/conda-forge/osx-64/clang-19-19.1.7-default_h3571c67_0.conda +https://conda.anaconda.org/conda-forge/osx-64/cmake-3.31.4-h477996e_0.conda +https://conda.anaconda.org/conda-forge/osx-64/fonttools-4.55.3-py312h3520af0_1.conda +https://conda.anaconda.org/conda-forge/osx-64/gettext-0.22.5-hdfe23c8_3.conda +https://conda.anaconda.org/conda-forge/osx-64/git-2.47.1-pl5321h0e333bc_0.conda +https://conda.anaconda.org/conda-forge/osx-64/hdf5-1.14.3-nompi_h1607680_108.conda +https://conda.anaconda.org/conda-forge/osx-64/libamd-3.3.3-ss783_hd70f86c.conda +https://conda.anaconda.org/conda-forge/osx-64/libblas-3.9.0-26_osx64_openblas.conda +https://conda.anaconda.org/conda-forge/osx-64/libboost-devel-1.85.0-h2b186f8_4.conda +https://conda.anaconda.org/conda-forge/osx-64/libbtf-2.3.2-ss783_ha7556f6.conda +https://conda.anaconda.org/conda-forge/osx-64/libcamd-3.3.3-ss783_ha7556f6.conda +https://conda.anaconda.org/conda-forge/osx-64/libccolamd-3.3.4-ss783_ha7556f6.conda +https://conda.anaconda.org/conda-forge/osx-64/libcolamd-3.3.4-ss783_ha7556f6.conda +https://conda.anaconda.org/conda-forge/osx-64/libcxsparse-4.4.1-ss783_h6325aac.conda +https://conda.anaconda.org/conda-forge/osx-64/libgrpc-1.67.1-h4896ac0_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libldl-3.3.2-ss783_ha7556f6.conda +https://conda.anaconda.org/conda-forge/osx-64/librbio-4.3.4-ss783_ha7556f6.conda +https://conda.anaconda.org/conda-forge/osx-64/llvm-tools-19.1.7-h3fe3016_0.conda +https://conda.anaconda.org/conda-forge/osx-64/openblas-0.3.28-openmp_h30af337_1.conda +https://conda.anaconda.org/conda-forge/osx-64/openldap-2.6.9-hd8a590d_0.conda +https://conda.anaconda.org/conda-forge/osx-64/openmpi-5.0.6-ha3b7a7e_101.conda +https://conda.anaconda.org/conda-forge/osx-64/pillow-11.1.0-py312hd9f36e3_0.conda +https://conda.anaconda.org/conda-forge/noarch/pip-24.3.1-pyh8b19718_2.conda +https://conda.anaconda.org/conda-forge/osx-64/proj-9.5.1-h5273da6_0.conda +https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda +https://conda.anaconda.org/conda-forge/osx-64/wxwidgets-3.2.6-h482b343_1.conda +https://conda.anaconda.org/conda-forge/osx-64/aws-c-auth-0.8.0-h6661f4c_16.conda +https://conda.anaconda.org/conda-forge/osx-64/aws-c-mqtt-0.11.0-h3488609_12.conda +https://conda.anaconda.org/conda-forge/osx-64/azure-identity-cpp-1.10.0-ha4e2ba9_0.conda +https://conda.anaconda.org/conda-forge/osx-64/azure-storage-common-cpp-12.8.0-h1ccc5ac_1.conda +https://conda.anaconda.org/conda-forge/osx-64/cctools_osx-64-1010.6-hadbd6bd_2.conda +https://conda.anaconda.org/conda-forge/osx-64/clang-19.1.7-default_h576c50e_0.conda +https://conda.anaconda.org/conda-forge/osx-64/fgt-0.4.11-hf719765_1.conda +https://conda.anaconda.org/conda-forge/osx-64/geotiff-1.7.3-h2b6e260_3.conda +https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.9.0-26_osx64_openblas.conda +https://conda.anaconda.org/conda-forge/osx-64/libgoogle-cloud-2.33.0-h7000a09_1.conda +https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.9.0-26_osx64_openblas.conda +https://conda.anaconda.org/conda-forge/osx-64/libnetcdf-4.9.2-nompi_hf3c7182_116.conda +https://conda.anaconda.org/conda-forge/osx-64/libpq-17.2-h639cf83_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libspatialite-5.1.0-h74337a0_12.conda +https://conda.anaconda.org/conda-forge/osx-64/libspex-3.2.1-ss783_h2c43358.conda +https://conda.anaconda.org/conda-forge/osx-64/poppler-24.12.0-hcc361ce_2.conda +https://conda.anaconda.org/conda-forge/osx-64/aws-c-s3-0.7.9-ha6e97d4_0.conda +https://conda.anaconda.org/conda-forge/osx-64/azure-storage-blobs-cpp-12.13.0-h3d2f5f1_1.conda +https://conda.anaconda.org/conda-forge/osx-64/clangxx-19.1.7-default_heb2e8d1_0.conda +https://conda.anaconda.org/conda-forge/noarch/compiler-rt_osx-64-19.1.7-hc6f8467_0.conda +https://conda.anaconda.org/conda-forge/osx-64/libcholmod-5.3.0-ss783_h7fe8f01.conda +https://conda.anaconda.org/conda-forge/osx-64/libgdal-core-3.10.1-ha746336_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libgoogle-cloud-storage-2.33.0-h3f2b517_1.conda +https://conda.anaconda.org/conda-forge/osx-64/numpy-1.26.4-py312he3a82b2_0.conda +https://conda.anaconda.org/conda-forge/osx-64/postgresql-17.2-h9d4d8b1_1.conda +https://conda.anaconda.org/conda-forge/osx-64/aws-crt-cpp-0.29.9-hd3bd598_1.conda +https://conda.anaconda.org/conda-forge/osx-64/azure-storage-files-datalake-cpp-12.12.0-h86941f0_1.conda +https://conda.anaconda.org/conda-forge/osx-64/compiler-rt-19.1.7-h52031e2_0.conda +https://conda.anaconda.org/conda-forge/osx-64/contourpy-1.3.1-py312hc47a885_0.conda +https://conda.anaconda.org/conda-forge/osx-64/libboost-python-1.85.0-py312h44e70fa_4.conda +https://conda.anaconda.org/conda-forge/osx-64/libgdal-hdf4-3.10.1-hf89130c_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libgdal-hdf5-3.10.1-h953d8a0_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libgdal-pdf-3.10.1-h8f2cad4_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libgdal-pg-3.10.1-hebcac8f_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libgdal-postgisraster-3.10.1-hebcac8f_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libklu-2.3.5-ss783_hdf6d925.conda +https://conda.anaconda.org/conda-forge/osx-64/libpdal-core-2.8.2-hc5073cc_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libspqr-4.3.4-ss783_h0b03d82.conda +https://conda.anaconda.org/conda-forge/osx-64/libumfpack-6.3.5-ss783_h53c776b.conda +https://conda.anaconda.org/conda-forge/osx-64/wxpython-4.2.2-py312h97a8bdf_3.conda +https://conda.anaconda.org/conda-forge/osx-64/aws-sdk-cpp-1.11.458-h904bc55_6.conda +https://conda.anaconda.org/conda-forge/osx-64/clang_impl_osx-64-19.1.7-hc73cdc9_23.conda +https://conda.anaconda.org/conda-forge/osx-64/libboost-python-devel-1.85.0-py312h0be7463_4.conda +https://conda.anaconda.org/conda-forge/osx-64/libgdal-netcdf-3.10.1-hb143bf5_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libparu-1.0.0-ss783_hd366fff.conda +https://conda.anaconda.org/conda-forge/osx-64/libpdal-cpd-2.8.2-h9a36bc9_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libpdal-draco-2.8.2-h9a36bc9_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libpdal-e57-2.8.2-hce94de9_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libpdal-icebridge-2.8.2-h8a15d71_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libpdal-nitf-2.8.2-ha2ed768_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libpdal-pgpointcloud-2.8.2-h6c51b8f_1.conda +https://conda.anaconda.org/conda-forge/osx-64/matplotlib-base-3.10.0-py312h535dea3_0.conda +https://conda.anaconda.org/conda-forge/osx-64/python-pdal-3.4.5-py312h759e574_13.conda +https://conda.anaconda.org/conda-forge/osx-64/boost-1.85.0-h0be7463_4.conda +https://conda.anaconda.org/conda-forge/osx-64/clang_osx-64-19.1.7-h7e5c614_23.conda +https://conda.anaconda.org/conda-forge/osx-64/libarrow-18.1.0-hc7ff7ea_11_cpu.conda +https://conda.anaconda.org/conda-forge/osx-64/libpdal-hdf-2.8.2-h8a15d71_1.conda +https://conda.anaconda.org/conda-forge/osx-64/matplotlib-3.10.0-py312hb401068_0.conda +https://conda.anaconda.org/conda-forge/osx-64/suitesparse-7.8.3-ss783_h8b3df4c.conda +https://conda.anaconda.org/conda-forge/osx-64/tiledb-2.27.0-h7101a2f_5.conda +https://conda.anaconda.org/conda-forge/osx-64/ceres-solver-2.2.0-he6a300e_5.conda +https://conda.anaconda.org/conda-forge/osx-64/clangxx_impl_osx-64-19.1.7-hb295874_23.conda +https://conda.anaconda.org/conda-forge/osx-64/libarrow-acero-18.1.0-ha6338a2_11_cpu.conda +https://conda.anaconda.org/conda-forge/osx-64/libgdal-tiledb-3.10.1-h2d0d9f7_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libparquet-18.1.0-h3e22b07_11_cpu.conda +https://conda.anaconda.org/conda-forge/osx-64/clangxx_osx-64-19.1.7-h7e5c614_23.conda +https://conda.anaconda.org/conda-forge/osx-64/libarrow-dataset-18.1.0-ha6338a2_11_cpu.conda +https://conda.anaconda.org/conda-forge/osx-64/libpdal-tiledb-2.8.2-h36a597b_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libpdal-trajectory-2.8.2-h3c2bd59_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libgdal-arrow-parquet-3.10.1-h0a3f151_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libpdal-arrow-2.8.2-h1b45f5b_1.conda +https://conda.anaconda.org/conda-forge/osx-64/libpdal-2.8.2-h57a307c_1.conda +https://conda.anaconda.org/conda-forge/noarch/pdal-2.8.2-hd8ed1ab_0.conda diff --git a/macos/files/configure-build.sh.in b/macos/files/configure-build.sh.in new file mode 100755 index 00000000000..88bae79cb49 --- /dev/null +++ b/macos/files/configure-build.sh.in @@ -0,0 +1,16 @@ +#!/bin/bash + +# full path to the SDK to build with +# sdk="/Library/Developer/CommandLineTools/SDKs/MacOSX10.13.sdk" + +# setting DEPLOYMENT_TARGET here is optional (MACOSX_DEPLOYMENT_TARGET +# of the SDK is the default) this will set MACOSX_DEPLOYMENT_TARGET +deployment_target=10.13 + +# setting CONDA_REQ_FILE here is optional (files/conda-requirements-stable[arm64|x86_64].txt +# is used by default) +# conda_req_file="${this_script_dir}/files/conda-requirements-dev-x86_64.txt" + +cs_ident="" +cs_keychain_profile="" +cs_provisionprofile="" diff --git a/macos/files/configure-grass.sh b/macos/files/configure-grass.sh new file mode 100755 index 00000000000..4ddd787e825 --- /dev/null +++ b/macos/files/configure-grass.sh @@ -0,0 +1,101 @@ +#!/bin/bash + +############################################################################ +# +# TOOL: configure-grass.sh +# AUTHOR(s): Nicklas Larsson +# PURPOSE: Sets up configure and compiler flags and configures GRASS GIS +# COPYRIGHT: (c) 2020-2025 Nicklas Larsson and the GRASS Development Team +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +############################################################################# + +PREFIX=$(python3 -c 'import sys; print(sys.prefix)') +export PREFIX +export PATH=${PREFIX}/bin:/usr/bin:/bin:/usr/sbin:/etc:/usr/lib +GRASS_PYTHON=$(which pythonw) +export GRASS_PYTHON +export CC=${PREFIX}/bin/clang +export CXX=${PREFIX}/bin/clang++ +export MACOSX_DEPLOYMENT_TARGET=$deployment_target + +export LDFLAGS="-fuse-ld=lld -Wl,-rpath,${PREFIX}/lib" +export CFLAGS="-O2 -pipe -arch ${arch} -DGL_SILENCE_DEPRECATION" +export CXXFLAGS="-O2 -pipe -stdlib=libc++ -arch ${arch}" + +CONFIGURE_FLAGS="\ + --with-macosx-sdk=${sdk} \ + --with-opengl=aqua \ + --with-openmp \ + --prefix=${PREFIX} \ + --without-x \ + --with-freetype \ + --with-freetype-includes=${PREFIX}/include/freetype2 \ + --with-freetype-libs=${PREFIX}/lib \ + --with-gdal=${PREFIX}/bin/gdal-config \ + --with-proj-includes=${PREFIX}/include \ + --with-proj-libs=${PREFIX}/lib \ + --with-proj-share=${PREFIX}/share/proj \ + --with-geos=${PREFIX}/bin/geos-config \ + --with-libpng=${PREFIX}/bin/libpng-config \ + --with-tiff-includes=${PREFIX}/include \ + --with-tiff-libs=${PREFIX}/lib \ + --with-postgres=yes \ + --with-postgres-includes=${PREFIX}/include \ + --with-postgres-libs=${PREFIX}/lib \ + --without-mysql \ + --with-sqlite \ + --with-sqlite-libs=${PREFIX}/lib \ + --with-sqlite-includes=${PREFIX}/include \ + --with-fftw-includes=${PREFIX}/include \ + --with-fftw-libs=${PREFIX}/lib \ + --with-cxx \ + --with-cairo \ + --with-cairo-includes=${PREFIX}/include/cairo \ + --with-cairo-libs=${PREFIX}/lib \ + --with-cairo-ldflags="-lcairo" \ + --with-zstd \ + --with-zstd-libs=${PREFIX}/lib \ + --with-zstd-includes=${PREFIX}/include \ + --with-bzlib \ + --with-bzlib-libs=${PREFIX}/lib \ + --with-bzlib-includes=${PREFIX}/include \ + --with-netcdf=${PREFIX}/bin/nc-config \ + --with-netcdf=${PREFIX}/bin/nc-config \ + --with-nls \ + --with-libsvm \ + --with-libs=${PREFIX}/lib \ + --with-includes=${PREFIX}/include \ + --with-pdal=${PREFIX}/bin/pdal-config \ + --with-readline \ + --with-readline-includes=${PREFIX}/include/readline \ + --with-readline-libs=${PREFIX}/lib +" + +if [ "$grass_version_major$grass_version_minor" -ge 85 ]; then + CONFIGURE_FLAGS="\ + ${CONFIGURE_FLAGS} \ + --with-blas=openblas \ + --with-lapack=openblas + " +else + CONFIGURE_FLAGS="\ + ${CONFIGURE_FLAGS} \ + --with-blas \ + --with-blas-libs=${PREFIX}/lib \ + --with-blas-includes=${PREFIX}/include \ + --with-lapack \ + --with-lapack-includes=${PREFIX}/include \ + --with-lapack-libs=${PREFIX}/lib + " +fi + +if [[ "$with_liblas" -eq 1 ]]; then + CONFIGURE_FLAGS="\ + ${CONFIGURE_FLAGS} \ + --with-liblas=${PREFIX}/bin/liblas-config + " +fi + +./configure $CONFIGURE_FLAGS diff --git a/macos/files/dmg-background.png b/macos/files/dmg-background.png new file mode 100644 index 00000000000..08add9408f8 Binary files /dev/null and b/macos/files/dmg-background.png differ diff --git a/macos/files/grass.entitlements b/macos/files/grass.entitlements new file mode 100644 index 00000000000..8cc185af8d7 --- /dev/null +++ b/macos/files/grass.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.cs.disable-library-validation + + + diff --git a/macos/files/grass.scpt b/macos/files/grass.scpt new file mode 100755 index 00000000000..ba9a4c47e60 --- /dev/null +++ b/macos/files/grass.scpt @@ -0,0 +1,7 @@ +tell application "Terminal" + set this_scpt to (POSIX path of (path to me as string)) + set grass_path to characters 1 thru -((offset of "/" in (reverse of items of this_scpt as string)) + 1) of this_scpt as string + set grass_startup to (quoted form of (grass_path & "/grass.sh")) + set grassRun to grass_startup & "; exit" + do script grassRun +end tell diff --git a/macos/files/grass.sh.in b/macos/files/grass.sh.in new file mode 100755 index 00000000000..87c3dce0e76 --- /dev/null +++ b/macos/files/grass.sh.in @@ -0,0 +1,64 @@ +#!/bin/bash + +############################################################################# +# +# MODULE: GRASS Initialization +# AUTHOR(S): Justin Hickey - Thailand - jhickey@hpcc.nectec.or.th +# William Kyngesburye - kyngchaos@kyngchaos.com +# Eric Hutton +# Michael Barton - michael.barton@asu.edu +# PURPOSE: The GRASS GIS startup script for the macOS application. +# COPYRIGHT: (c) 2000-2025 by the GRASS Development Team +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +############################################################################# + +# script_dir=$(dirname "$(dirname "$0")") +app_dir="$(cd "$(dirname "$0")/../.."; pwd -P)" + +# Mac app only startup shell - complete rewrite for starting from a GRASS.app +# in Mac OS X. Sets defaults for unset env, adds some Mac-only config. + +trap "echo 'User break!' ; exit" 2 3 9 15 + +export GISBASE=$app_dir/Contents/Resources +grass_ver=$(cut -d . -f 1-2 "$GISBASE/etc/VERSIONNUMBER") + +export GISBASE_USER="$HOME/Library/GRASS/$grass_ver" +export GISBASE_SYSTEM="/Library/GRASS/$grass_ver" + +# add some OS X style app support paths, and create user one if missing. +mkdir -p "$GISBASE_USER/Addons/bin" +mkdir -p "$GISBASE_USER/Addons/scripts" +if [ ! "$GRASS_ADDON_BASE" ] ; then + GRASS_ADDON_BASE="$GISBASE_USER/Addons" +fi +export GRASS_ADDON_BASE + +mkdir -p "$GISBASE_USER/Addons/etc" +addpath="$GISBASE_USER/Addons/etc:$GISBASE_SYSTEM/Addons/etc" +if [ "$GRASS_ADDON_ETC" ] ; then + GRASS_ADDON_ETC="$GRASS_ADDON_ETC:$addpath" +else + GRASS_ADDON_ETC="$addpath" +fi +export GRASS_ADDON_ETC + +mkdir -p "$GISBASE_USER/Addons/lib" +mkdir -p "$GISBASE_USER/Addons/docs/html" + +# user fontcap files +if [ ! "$GRASS_FONT_CAP" ] ; then + GRASS_FONT_CAP="$GISBASE_USER/Addons/etc/fontcap" +fi +export GRASS_FONT_CAP + +# location of epsg database and other files needed by GDAL +export GDAL_DATA="$GISBASE/share/gdal" + +# set Python +export GRASS_PYTHON="$GISBASE/python.app/Contents/MacOS/python" + +# start GRASS +"$GRASS_PYTHON" "$GISBASE/bin/@GRASSBIN@" "--gui" "$@" diff --git a/macos/files/liblas-install.sh b/macos/files/liblas-install.sh new file mode 100755 index 00000000000..4d633917c70 --- /dev/null +++ b/macos/files/liblas-install.sh @@ -0,0 +1,135 @@ +#!/bin/bash + +############################################################################ +# +# TOOL: liblas-install.sh +# AUTHOR(s): Nicklas Larsson +# PURPOSE: Downloads, compiles and installs libLAS. +# COPYRIGHT: (c) 2021-2025 Nicklas Larsson and the GRASS Development Team +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +############################################################################# + +liblas_commit="6ada875661c46842433a13f28637f8d3d2c393bc" +liblas_zipfile_name="libLAS_${liblas_commit}.zip" +liblas_zipfile_url="https://github.com/libLAS/libLAS/archive/${liblas_commit}.zip" +liblas_source_dir_name="libLAS-${liblas_commit}" +liblas_build_dir_name=libLAS-build + +liblas_zipfile="${cache_dir}/${liblas_zipfile_name}" +liblas_source_dir="${cache_dir}/${liblas_source_dir_name}" +liblas_build_dir="${cache_dir}/${liblas_build_dir_name}" + +PREFIX=$(python3 -c 'import sys; print(sys.prefix)') +export PREFIX +export PATH="${PREFIX}/bin:/usr/bin:/bin:/usr/sbin:/etc:/usr/lib" +export CC="${PREFIX}/bin/clang" +export CXX="${PREFIX}/bin/clang++" +export MACOSX_DEPLOYMENT_TARGET="$deployment_target" +export CFLAGS="-O2 -pipe -arch ${arch}" +export CXXFLAGS="-O2 -pipe -arch ${arch} -stdlib=libc++" +export LDFLAGS="-fuse-ld=lld" + +cmake="${PREFIX}/bin/cmake" + +if [ ! -f "$liblas_zipfile" ]; then + echo "Downloading libLAS..." + curl -L "$liblas_zipfile_url" --output "$liblas_zipfile" || exit 1 +fi + +rm -rf "$liblas_source_dir" +rm -rf "$liblas_build_dir" +mkdir -p "$liblas_source_dir" +mkdir -p "$liblas_build_dir" + +unzip "$liblas_zipfile" -d "$cache_dir" &> /dev/null + +# patch needed for using now outdated GDAL api +patch -d "$liblas_source_dir" -p0 << EOF +--- src/gt_wkt_srs.cpp.orig 2020-12-14 19:56:40.000000000 +0100 ++++ src/gt_wkt_srs.cpp 2021-03-27 19:31:30.000000000 +0100 +@@ -299,7 +299,6 @@ + oSRS.SetFromUserInput(pszWKT); + oSRS.SetExtension( "PROJCS", "PROJ4", + "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs" ); +- oSRS.FixupOrdering(); + CPLFree(pszWKT); + pszWKT = NULL; + oSRS.exportToWkt(&pszWKT); +@@ -505,7 +504,6 @@ + { + char *pszWKT; + oSRS.morphFromESRI(); +- oSRS.FixupOrdering(); + if( oSRS.exportToWkt( &pszWKT ) == OGRERR_NONE ) + return pszWKT; + } +@@ -1107,7 +1105,6 @@ + /* ==================================================================== */ + char *pszWKT; + +- oSRS.FixupOrdering(); + + if( oSRS.exportToWkt( &pszWKT ) == OGRERR_NONE ) + return pszWKT; + +EOF + +# patch for missing architecture in endian detection +patch -d "$liblas_source_dir" -p0 << EOF +--- include/liblas/detail/endian.hpp.orig 2021-01-20 18:24:39.000000000 +0100 ++++ include/liblas/detail/endian.hpp 2022-09-27 18:21:23.000000000 +0200 +@@ -88,6 +88,7 @@ + || defined(_M_ALPHA) || defined(__amd64) \\ + || defined(__amd64__) || defined(_M_AMD64) \\ + || defined(__x86_64) || defined(__x86_64__) \\ ++ || defined(__arm64) || defined(__arm64__) \\ + || defined(_M_X64) + + # define LIBLAS_LITTLE_ENDIAN +EOF + +LIBLAS_CONFIGURE_FLAGS=" + -DCMAKE_OSX_SYSROOT=${sdk} \ + -DCMAKE_INCLUDE_PATH=${PREFIX}/include \ + -DCMAKE_INSTALL_PREFIX=${PREFIX} \ + -DCMAKE_MACOSX_RPATH=ON \ + -DCMAKE_INSTALL_RPATH=${PREFIX} \ + -DWITH_GEOTIFF=ON \ + -DGEOTIFF_INCLUDE_DIR=${PREFIX}/include \ + -DGEOTIFF_LIBRARY=${PREFIX}/lib/libgeotiff.dylib \ + -DWITH_GDAL=ON \ + -DGDAL_CONFIG=${PREFIX}/bin/gdal-config \ + -DPROJ4_INCLUDE_DIR=${PREFIX}/include/proj \ + -DPROJ4_LIBRARY=${PREFIX}/lib/proj7 \ + -DWITH_LASZIP=OFF \ + -DWITH_PKGCONFIG=OFF +" + +pushd "$liblas_build_dir" > /dev/null + +echo +echo "Configuring libLAS..." +"$cmake" -G "Unix Makefiles" $LIBLAS_CONFIGURE_FLAGS "$liblas_source_dir" + +echo "Compiling and installing libLAS..." +if ! make &> "${cache_dir}/libLAS_install.log" +then + echo "...libLAS compilation failed. See ${cache_dir}/libLAS_install.log." + popd > /dev/null + exit_nice 1 +fi + +if ! make install &> "${cache_dir}/libLAS_install.log" +then + echo "...libLAS installations failed. See ${cache_dir}/libLAS_install.log." + popd > /dev/null + exit_nice 1 +fi +echo "...libLAS installed successfully." + +popd > /dev/null + +export CFLAGS="" +export CXXFLAGS="" diff --git a/macos/files/main.m b/macos/files/main.m new file mode 100644 index 00000000000..0ac2799f97d --- /dev/null +++ b/macos/files/main.m @@ -0,0 +1,50 @@ +// +// grass-launcher-objc +// +// Created by Nicklas Larsson on 2024-02-27. +// (c) 2024-2025 Nicklas Larsson and the GRASS Development Team +// SPDX-License-Identifier: GPL-2.0-or-later + +#import +#import + +NSURL *mainExecutableParentDir(void) { + NSURL *url; + char *buf = (char *)malloc(sizeof(char) * MAXPATHLEN); + uint32_t bufSize = sizeof(buf); + if (_NSGetExecutablePath(buf, &bufSize) < 0) { + free(buf); + char *buf = (char *)malloc(sizeof(char) * bufSize); + if (_NSGetExecutablePath(buf, &bufSize) < 0) { + free(buf); + return nil; + } + url = [NSURL fileURLWithFileSystemRepresentation:buf + isDirectory:NO + relativeToURL:nil]; + free(buf); + } else { + url = [NSURL fileURLWithFileSystemRepresentation:buf + isDirectory:NO + relativeToURL:nil]; + free(buf); + } + return url.URLByDeletingLastPathComponent; +} + +int main(int argc, const char *argv[]) { + @autoreleasepool { + NSURL *script_path = mainExecutableParentDir(); + if (!script_path) { + return 1; + } + script_path = [script_path URLByAppendingPathComponent:@"grass.scpt"]; + + NSTask *task = [[NSTask alloc] init]; + task.executableURL = [NSURL fileURLWithPath:@"/usr/bin/osascript" + isDirectory:NO]; + task.arguments = @[ script_path.path ]; + [task launch]; + } + return 0; +} diff --git a/macos/files/main.swift b/macos/files/main.swift new file mode 100644 index 00000000000..343cf6e22a5 --- /dev/null +++ b/macos/files/main.swift @@ -0,0 +1,30 @@ +// +// grass-launcher +// +// Created by Nicklas Larsson on 2024-02-21. +// (c) 2024-2025 Nicklas Larsson and the GRASS Development Team +// SPDX-License-Identifier: GPL-2.0-or-later + + +import Foundation + +func mainExecutableParentDir() -> URL { + var buf = [CChar](repeating: 0, count: Int(MAXPATHLEN)) + var bufSize = UInt32(buf.count) + let success = _NSGetExecutablePath(&buf, &bufSize) >= 0 + if !success { + buf = [CChar](repeating: 0, count: Int(bufSize)) + let success2 = _NSGetExecutablePath(&buf, &bufSize) >= 0 + guard success2 else { fatalError() } + } + return URL(fileURLWithFileSystemRepresentation: buf, isDirectory: false, + relativeTo: nil).deletingLastPathComponent() +} + +var script_path = mainExecutableParentDir() +script_path.appendPathComponent("grass.scpt") + +let task = Process() +task.launchPath = "/usr/bin/osascript" +task.arguments = [script_path.path] +try task.run() diff --git a/macosx/Makefile b/macosx/Makefile deleted file mode 100644 index 059a04f8b42..00000000000 --- a/macosx/Makefile +++ /dev/null @@ -1,125 +0,0 @@ -MODULE_TOPDIR = .. - -include $(MODULE_TOPDIR)/include/Make/Vars.make - -SUBDIRS = -ifeq ($(MACOSX_APP),1) -SUBDIRS = app -endif - -include $(MODULE_TOPDIR)/include/Make/Dir.make - -MACOSX_APP_NAME = GRASS-${GRASS_VERSION_MAJOR}.${GRASS_VERSION_MINOR}.app -MACOSX_APPDIR = ${MACOSX_APP_NAME}/Contents/MacOS -INST_DIR_MACOSX = ${prefix}/${MACOSX_APPDIR} -MACOSX_SUPPDIR = /Library/GRASS/${GRASS_VERSION_MAJOR}.${GRASS_VERSION_MINOR} -MACOSX_BUNDLE_PREFIX = ${GRASS_HOME}/macosx/dist/${MACOSX_APPDIR} -VER_MINOR_REL = `printf "%d0%d" ${GRASS_VERSION_MINOR} ${GRASS_VERSION_RELEASE}` -VER_DATE = `date +"%g%m%d"` -MAKE_DIR_CMD = mkdir -p -m 755 -LN = /bin/ln -sf -LN_DIR = /bin/ln -sfh -# get python version - use python in path, should be config'd python -PYVER = `python -V 2>&1 | cut -d \ -f 2 | cut -d . -f 1,2` - -# packagemaker location, options and behaviour changed in xcode 3 -ifeq ($(findstring darwin8,$(ARCH)),darwin8) -XCODE_VER = xcode2 -PACKAGEMAKER = /Developer/Tools/packagemaker -PKG_INST_DIR = ${prefix}/${MACOSX_APP_NAME} -else -XCODE_VER = xcode3 -PACKAGEMAKER = /Developer/usr/bin/packagemaker -PKG_INST_DIR = ${prefix} -endif - -default: subdirs - -include bundle.make - -# Duplicated, mostly, from the top-level makefile. - -install-macosx: INST_DIR_TARGET = ${INST_DIR} -install-macosx: PREFIX_TARGET = ${prefix} -install-macosx: install-macosxapp install-basic-macosx macosx-support-dir bundle-macosx - echo ${ARCH_DISTDIR}/${MACOSX_APP_NAME} - -# create a packagemaker installer package. -# pretty much the same as install, but needs intermediate install location -# to assemble package parts. - -bindist-macosx: INST_DIR_TARGET = ${MACOSX_BUNDLE_PREFIX} -bindist-macosx: PREFIX_TARGET = ${GRASS_HOME}/macosx/dist -bindist-macosx: install-macosxapp install-basic-macosx bundle-macosx - @# installer pkg resources - ${MAKE_DIR_CMD} dist/resources - -sed -e "s#@INST_DIR@#${INST_DIR}#g" \ - -e "s#@MACOSX_SUPPDIR@#${MACOSX_SUPPDIR}#g" \ - -e "s#@GRASS_VER@#${GRASS_VERSION_MAJOR}.${GRASS_VERSION_MINOR}#g" \ - -e "s#@PYVER@#${PYVER}#g" \ - pkg/resources/postflight.in > dist/resources/postflight - -chmod a+rx dist/resources/postflight - -sed -e "s#@GRASS_VER@#${GRASS_VERSION_MAJOR}.${GRASS_VERSION_MINOR}#g" \ - pkg/resources/Description.plist.in > dist/Description.plist - -sed -e "s#@GRASS_VERSION_MAJOR@#${GRASS_VERSION_MAJOR}#g" \ - -e "s#@GRASS_VERSION_MINOR@#${GRASS_VERSION_MINOR}#g" \ - -e "s#@GRASS_VERSION_RELEASE@#${GRASS_VERSION_RELEASE}#g" \ - -e "s#@VER_MINOR_REL@#${VER_MINOR_REL}#g" \ - -e "s#@VER_DATE@#${VER_DATE}#g" \ - -e "s#@PKG_INST_DIR@#${PKG_INST_DIR}#g" \ - pkg/resources/Info.plist.in > dist/Info.plist - -${INSTALL_DATA} pkg/resources/License.rtf dist/resources - -${INSTALL_DATA} pkg/resources/ReadMe.rtf dist/resources - @# build the package -ifeq ($(XCODE_VER),xcode3) - ${PACKAGEMAKER} --target 10.4 --root-volume-only --id org.osgeo.grass --root dist/${MACOSX_APP_NAME} --info dist/Info.plist --out "GRASS-${GRASS_VERSION_MAJOR}.${GRASS_VERSION_MINOR}.pkg" --resources dist/resources - rm -f "GRASS-${GRASS_VERSION_MAJOR}.${GRASS_VERSION_MINOR}.pkg/Contents/Resources/TokenDefinitions.plist" -else - ${PACKAGEMAKER} -build -ds -p "GRASS-${GRASS_VERSION_MAJOR}.${GRASS_VERSION_MINOR}.pkg" -f dist/${MACOSX_APP_NAME} -r dist/resources -i dist/Info.plist -d dist/Description.plist -endif - -install-macosxapp: - -$(MAKE_DIR_CMD) ${INST_DIR_TARGET}/bin - -$(MAKE_DIR_CMD) ${INST_DIR_TARGET}/etc - -$(MAKE_DIR_CMD) ${PREFIX_TARGET}/${MACOSX_APP_NAME}/Contents/Resources/Scripts - -$(MAKE_DIR_CMD) ${PREFIX_TARGET}/${MACOSX_APP_NAME}/Contents/Resources/English.lproj/MainMenu.nib - $(INSTALL) app/build_html_user_index.sh ${INST_DIR_TARGET}/etc - $(INSTALL) app/build_gui_user_menu.sh ${INST_DIR_TARGET}/etc - $(INSTALL) app/python_wrapper ${INST_DIR_TARGET}/bin/python - $(INSTALL_DATA) app/AppIcon.icns ${PREFIX_TARGET}/${MACOSX_APP_NAME}/Contents/Resources - $(INSTALL_DATA) app/GRASSDocument_gxw.icns ${PREFIX_TARGET}/${MACOSX_APP_NAME}/Contents/Resources - $(INSTALL_DATA) app/${OBJDIR}/Info.plist ${PREFIX_TARGET}/${MACOSX_APP_NAME}/Contents - $(INSTALL_DATA) app/PkgInfo ${PREFIX_TARGET}/${MACOSX_APP_NAME}/Contents - $(INSTALL) app/${OBJDIR}/GRASS.scpt ${PREFIX_TARGET}/${MACOSX_APP_NAME}/Contents/Resources/Scripts/ - $(INSTALL) app/${OBJDIR}/grass.sh ${INST_DIR_TARGET} - $(INSTALL) app/${OBJDIR}/GRASS ${INST_DIR_TARGET} - $(INSTALL_DATA) app/English.lproj/MainMenu.nib/* ${PREFIX_TARGET}/${MACOSX_APP_NAME}/Contents/Resources/English.lproj/MainMenu.nib - -install-basic-macosx: - -tar cBCf $(GISBASE) - . | tar xBCf $(INST_DIR_TARGET) - 2>/dev/null - -sed -e 's#'$(GISBASE)'#'$(INST_DIR)'#g' \ - ${GISBASE}/etc/fontcap > ${INST_DIR_TARGET}/etc/fontcap - -sed -e 's#'$(GISBASE)'#'$(INST_DIR)'#g' \ - ${GISBASE}/demolocation/.grassrc$(GRASS_VERSION_MAJOR)$(GRASS_VERSION_MINOR) > ${INST_DIR_TARGET}/demolocation/.grassrc$(GRASS_VERSION_MAJOR)$(GRASS_VERSION_MINOR) - sed -e 's#^\(GRASS_HOME.[^=]*\).*#\1= $(INST_DIR)#g' \ - -e 's#$(GISBASE)#$(INST_DIR)#g' \ - -e "s#-isysroot /.*MacOSX.*\.sdk##g" \ - ${GISBASE}/include/Make/Platform.make > ${INST_DIR_TARGET}/include/Make/Platform.make - sed -e 's#^\(ARCH_DISTDIR.[^=]*\).*#\1= $(INST_DIR)#g' \ - -e 's#^\(ARCH_BINDIR.[^=]*\).*#\1= $(UNIX_BIN)#g' \ - ${GISBASE}/include/Make/Grass.make > ${INST_DIR_TARGET}/include/Make/Grass.make - -sed -e 's#'@GISBASE@'#'$(INST_DIR)'#g' \ - -e 's#'@LD_LIBRARY_PATH_VAR@'#'$(LD_LIBRARY_PATH_VAR)'#g' \ - -e 's#'@CONFIG_PROJSHARE@'#'$(PROJSHARE)'#g' \ - $(ARCH_DISTDIR)/$(GRASS_NAME).tmp > $(INST_DIR_TARGET)/$(GRASS_NAME) - -chmod a+x $(INST_DIR_TARGET)/$(GRASS_NAME) - -rm -f $(INST_DIR_TARGET)/$(GRASS_NAME).tmp - -chmod -R a+rX ${PREFIX_TARGET}/${MACOSX_APP_NAME} 2>/dev/null - -touch ${PREFIX_TARGET}/${MACOSX_APP_NAME} - -macosx-support-dir: - ${MAKE_DIR_CMD} "${MACOSX_SUPPDIR}" - ${MAKE_DIR_CMD} "${MACOSX_SUPPDIR}/Modules/bin" - ${MAKE_DIR_CMD} "${MACOSX_SUPPDIR}/Modules/docs/html" - ${MAKE_DIR_CMD} "${MACOSX_SUPPDIR}/Modules/etc" - ${MAKE_DIR_CMD} "${MACOSX_SUPPDIR}/Modules/lib" diff --git a/macosx/ReadMe.md b/macosx/ReadMe.md deleted file mode 100644 index fa9c13b1aff..00000000000 --- a/macosx/ReadMe.md +++ /dev/null @@ -1,703 +0,0 @@ -# GRASS GIS Mac OS X Build - -(and other Mac notes) - -## Table of Contents - -| :exclamation: Instructions below are not up-to-date. Update in progress. | -|---------------------------------------------------------------------------| - -- Quickstart -- About -- Building GRASS - - Optimization - - Configure Example - - Leopard Notes - - Building an Installer Package - - Bundling Libraries and Programs -- Developer Notes -- Help Files -- Addon Modules -- Dependency Build Notes - - Universal Aqua Tcl/Tk - - Universal GPSBabel - - Universal NetPBM - - Universal FFMPEG - -## Quickstart - -To build a Mac OS X application, add this to your configure options: - -`--prefix=/Applications --enable-macosx-app` - -## About - -This builds GRASS as a Mac OS X application, more or less. - -The startup wrapper is a simple AppleScript that fires up a custom -grass.sh (instead of the standard grass6x) and quits, leaving grass.sh -running in a new Terminal window. The custom grass.sh takes care of some -default and required environment settings (so the user doesn't have to -fiddle with .bash_profile), makes sure X11 in running, then starts -init.sh. - -## Building GRASS - -Building GRASS as a Mac OS X application is pretty much the same as for -a unix build (see the [INSTALL.md](../INSTALL.md) document for details). For an -application, just add this option to configure: - -`--enable-macosx-app` - -With this option, the `--prefix` specified is the path where GRASS.app -will be installed. The default GRASS prefix is `/usr/local`, but for a Mac -OS X application it should be `/Applications`. It is not possible to have -alternative default prefixes in configure, so you must set -`--prefix=/Applications` yourself for configure. - -The makefile tries to figure out the location of the GDAL programs, from -the configured GDAL libs. This is set in grass.sh, and if it doesn't -correctly figure it out (some GRASS commands fail to find needed GDAL -commands), set `GDAL_BIN` before running make: - -`export GDAL_BIN=[/path/to/gdal/bin]` - -### Universal Build - -The GRASS build system is very friendly to a Universal build (unlike -libtool-based builds). *This applies to a standard unix build as well as -the app build.* First, a couple things to check: - -**• The Universal SDK**. *This is only needed when building on OSX 10.4 -PPC - the Intel system is all Universal, and so is 10.5 for both -architectures (though you may still want to use an SDK).* Using the SDK -forces GCC to use an alternate root for everything, so if a dependency -is not in the SDK (/usr/local is NOT in the SDK), you will get -configure, compile and link errors about missing stuff. To fix this, -create symlinks in the SDK to where you have your extra dependencies. -The SDKs in Leopard are mostly taken care of. - -For example, /usr/local: - -```sh -sudo ln -s /usr/local /Developer/SDKs/MacOSX10.4u.sdk/usr/local -``` - -For Leopard, there may be problems locating bin programs. Add the -following: - -```sh -sudo ln -s /usr/local/bin /Developer/SDKs/MacOSX10.4u.sdk/usr/local/bin -sudo ln -s /usr/local/bin /Developer/SDKs/MacOSX10.5.sdk/usr/local/bin -``` - -If there are subfolders involved that are not already in the SDK, add -them first. Say you have some libraries in /Users/Shared/unix (I put my -static libraries there): - -```sh -sudo mkdir -p /Developer/SDKs/MacOSX10.4u.sdk/Users/Shared -ln -s /Users/Shared/unix /Developer/SDKs/MacOSX10.4u.sdk/Users/Shared/unix -``` - -For /Library/Frameworks: - -```sh -sudo mkdir -p /Developer/SDKs/MacOSX10.4u.sdk/Library -sudo ln -s /Library/Frameworks \ -/Developer/SDKs/MacOSX10.4u.sdk/Library/Frameworks -``` - -**• Universal Dependencies**. Make sure all your extra dependencies are -Universal. - -**• Tiger+**. This will NOT work on anything less than OSX 10.4. - -If those check OK, here's what to do. Simply add the universal flags to -all compile and link commands. This is done by setting CFLAGS, CXXFLAGS -and LDFLAGS before configuring the build. The basic flags are: - -`-arch ppc -arch i386` - -If you are building on OSX 10.4 PPC only, also add this: - -`-isysroot /Developer/SDKs/MacOSX10.4u.sdk` - -Put it all together, for an Intel Mac on OSX 10.4 or any Mac on 10.5+: - -```sh -export CFLAGS="-arch ppc -arch i386" -export CXXFLAGS="-arch ppc -arch i386" -export LDFLAGS="-arch ppc -arch i386" -``` - -Or for OSX 10.4 PPC: - -```sh -export SDKROOT=/Developer/SDKs/MacOSX10.4u.sdk -export CFLAGS="-arch ppc -arch i386 -isysroot $SDKROOT" -export CXXFLAGS="-arch ppc -arch i386 -isysroot $SDKROOT" -export LDFLAGS="-arch ppc -arch i386 -isysroot $SDKROOT" -``` - -For OSX 10.5, you can also build for 64bit (all processors except the -first Intel Core Duo), just add: - -`-arch ppc64 -arch x86_64` - -### Optimization - -By default, GRASS configures with debugging symbols turned on (the -"-g" flag), and level 2 optimization ("-O2"). If you don't want -debugging on (it greatly increases the binary size) or want to use a -different optimization, just add an optimization flag to the above -CFLAGS and CXXFLAGS exports. Specifying an optimization disables -debugging. Some common optimizations are (see the gcc man page for -complete details): - -- `-O2` most optimizations, a common default -- `-O3` high -- `-Os` optimize for speed, also keep the binary size small (the Apple standard) - -If you want debugging with a different optimization flag, use one of the above -optimization flags plus "-g". - -Remember to separate all flags with spaces. - -### Configure Example - -As an example, to build GRASS using my frameworks and Tcl/Tk as built in -the build notes below, this should work *(for a standard unix build, -just remove the `--prefix` and `--enable-macosx-app` flags)*: - -```bash -./configure \ - --enable-macosx-app \ - --prefix=/Applications \ - --with-cxx \ - --with-fftw-includes=/Library/Frameworks/FFTW3.framework/unix/include \ - --with-fftw-libs=/Library/Frameworks/FFTW3.framework/unix/lib \ - --with-freetype \ - --with-freetype-includes= \ - "/Library/Frameworks/FreeType.framework/unix/include/freetype2 \ - /Library/Frameworks/FreeType.framework/unix/include" \ - --with-freetype-libs=/Library/Frameworks/FreeType.framework/unix/lib \ - --with-gdal=/Library/Frameworks/GDAL.framework/Programs/gdal-config \ - --with-geos=/Library/Frameworks/GEOS.framework/Programs/geos-config \ - --with-jpeg-includes=/Library/Frameworks/UnixImageIO.framework/unix/include \ - --with-jpeg-libs=/Library/Frameworks/UnixImageIO.framework/unix/lib \ - --with-odbc \ - --with-opengl=aqua \ - --with-png-includes=/Library/Frameworks/UnixImageIO.framework/unix/include \ - --with-png-libs=/Library/Frameworks/UnixImageIO.framework/unix/lib \ - --with-proj \ - --with-proj-includes=/Library/Frameworks/PROJ.framework/unix/include \ - --with-proj-libs=/Library/Frameworks/PROJ.framework/unix/lib \ - --with-proj-share=/Library/Frameworks/PROJ.framework/Resources/proj \ - --with-sqlite \ - --with-sqlite-includes=/Library/Frameworks/SQLite3.framework/unix/include \ - --with-sqlite-libs=/Library/Frameworks/SQLite3.framework/unix/lib \ - --with-tcltk-includes="/Library/Frameworks/Tcl.framework/Headers \ - /Library/Frameworks/Tk.framework/Headers \ - /Library/Frameworks/Tk.framework/PrivateHeaders" \ - --with-tcltk-libs=/usr/local/lib \ - --with-tiff-includes=/Library/Frameworks/UnixImageIO.framework/unix/include \ - --with-tiff-libs=/Library/Frameworks/UnixImageIO.framework/unix/lib \ - --with-x \ - --without-glw \ - --without-motif \ - --without-mysql \ - --without-postgres \ - --without-readline -``` - -That's a long line, but you have to be very explicit in the GRASS configure -(and yes, the UnixImageIO framework is used multiple times). - -If you don't have the PROJ framework programs folder in your shell -PATH, it will fail to find nad2bin. To correct this set NAD2BIN before -running configure: - -`export NAD2BIN=/Library/Frameworks/PROJ.framework/Programs/nad2bin` - -Sometimes, for mysterious reasons so far, the automatic check for X11 -fails. Depending on various conditions, you may immediately notice this -because configure stops. If X11 isn't found automatically, add this to -the configure line *(this applies to both the unix build and app -build)*: - -`--x-includes=/usr/X11R6/include --x-libraries=/usr/X11R6/lib` - -To install the new Python GUI (see [REQUIREMENTS.md](../REQUIREMENTS.md) -and [gui/wxpython/README](../gui/wxpython/README), wxpython installer -available at [wxpython.org](https://wxpython.org/)), add this to configure (fill -in the correct version at x.x.x.x for the wxpython you have installed): - -```bash ---with-python ---with-wxwidgets=/usr/local/lib/wxPython-unicode-x.x.x.x/bin/wx-config -``` - -Note: as of wxpython 2.8.8.0 you can use an external wxpython with the -system Python on Leopard. - -If you want ffmpeg support (see build instructions below), add this: - -```bash ---with-ffmpeg \ ---with-ffmpeg-includes="/usr/local/include \ - /usr/local/include/libavcodec /usr/local/include/libavdevice \ - /usr/local/include/libavformat /usr/local/include/libavutil \ - /usr/local/include/libswscale" \ ---with-ffmpeg-libs=/usr/local/lib -``` - -For cairo support (see build -instructions at -[kyngchaos.com](https://web.archive.org/web/20161112052733/http://www.kyngchaos.com/macosx/build/cairo/)), -add this: - -```bash ---with-cairo \ ---with-cairo-includes=/usr/local/include/cairo \ ---with-cairo-libs=/usr/local/lib \ ---with-cairo-ldflags=-lcairo -``` - -### Leopard Notes - -• Linker Problem - -The linker in Leopard's GCC from Xcode 3.0 has a bit of a problem with -some libraries, notably X11's libGL. When trying to link libGL (for -example), it spits back an error: - -`ld: cycle in dylib re-exports with /usr/X11/lib/libGL.dylib for -architecture i386` - -Xcode 3.1 does not have this problem. In other words, make sure to -install the latest Xcode. - -### Building an Installer Package - -After running make, make bindist will assemble an installer package. -This will have the name "GRASS-[version].pkg" and will be found in -the macosx folder in the GRASS source. On Tiger, there **will be** some -**non-fatal errors** - *this is normal* and the installer package is -still generated correctly. - -The installer takes care of binary installation, and creating the -/Library/GRASS support folder and Help Viewer link. - -This installer currently does NOT do any verification of installed -dependencies or system version. - -### Bundling Libraries and Programs - -Additional dependent libraries and programs can be bundled with the -application package to make a more portable and easily installed GRASS -application. See `macosx/pkg/bundle.make` for details. - -GRASS uses `DYLD_LIBRARY_PATH`, which is set to the application's lib -folder, so it will find any libraries that are there that are not found -in their expected locations. It also adds the application bin folder to -the `PATH`, so helper programs installed there will be found. - -There is NO need to bundle *everything*. Bundling readily available libraries -and programs installed in standard locations is not necessary. Some things -you might want to bundle include: - -- GPSBabel (CLI) -- NetPBM programs (just what is used in GRASS) -- other support CLI programs for script modules -- ffmpeg libraries -- Postgres library (libpq) - -Things to leave out: - -- applications that duplicate the above items (ie GPSBabel.app) -- /Library/Frameworks stuff - -## Developer Notes - -The applescript startup and grass.sh can be customized if desired, ie -for a more complex startup that might give the user a dialog for startup -options. They just provide the basic core to build upon. - -## Help Files - -Help files are linked into the OSX help system. Independent of what -browser you choose to open help files (`GRASS_HTML_BROWSER`), you can -open Help Viewer from almost any application's Help menu (it's best to -use Mac Help from the Finder Help menu), and GRASS Help will be -available in the Library menu. - -Since they are linked to the help system, if GRASS.app is moved or -renamed, the link will be broken. This may change in the future. - -## Addon Modules - -modbuild deprecated (not installed now). Makefile changes in progress to -allow compiling external modules. - -## Dependency Build Notes - -They apply to any OSX build, not just an application package build: - -### Universal Aqua Tcl/Tk - -The simplest way to install Tcl/Tk Aqua is to download -[ActiveTcl](https://www.activestate.com/products/tcl/) from -ActiveState. This will give you a universal 32bit Tcl/Tk Aqua (64bit is -currently not possible). Make sure to run the *convenience* commands -below. - -To make it easier to configure for GRASS (and other software), run these -commands in a Terminal: - -```sh -sudo ln -sf /Library/Frameworks/Tcl.framework/Versions/8.5/Tcl \ - /usr/local/lib/libtcl.dylib -sudo ln -sf /Library/Frameworks/Tcl.framework/Versions/8.5/Tcl \ - /usr/local/lib/libtcl8.5.dylib -sudo ln -sf \ - /Library/Frameworks/Tcl.framework/Versions/8.5/libtclstub8.5.a \ - /usr/local/lib/libtclstub8.5.a -sudo ln -sf /Library/Frameworks/Tk.framework/Versions/8.5/Tk \ - /usr/local/lib/libtk.dylib -sudo ln -sf /Library/Frameworks/Tk.framework/Versions/8.5/Tk \ - /usr/local/lib/libtk8.5.dylib -sudo ln -sf /Library/Frameworks/Tk.framework/Versions/8.5/libtkstub8.5.a \ - /usr/local/lib/libtkstub8.5.a -``` - -### Universal GPSBabel - -A universal GPSBabel CLI executable is now included in the OSX binary -from [www.gpsbabel.org](http://www.gpsbabel.org/). This does not need to -be 64bit. - -### Universal NetPBM - -NetPBM is not too hard to build, it's just weird. These instructions -use my UnixImageIO framework for the tiff, jpeg, png and jasper -libraries. - -First, grab the [NetPBM](http://netpbm.sourceforge.net/) source and -unzip it. Now it gets strange - it's an interactive configuration, and -the various stages don't communicate with each other. Cd to the source -folder in a Terminal and: - -`./configure` ↵ - -`Platform [darwin] ==>` ↵ - -`Netpbm shared library directory [default] ==>` **`/usr/local`** - -choose where you will install it -default is NOT /usr/local or any location at all, so you MUST set this - -`regular or merge [regular] ==>` ↵ - -`static or shared [shared] ==>` ↵ - -could use static, since GRASS only needs a few of the progs - -`'#include' argument or NONE [] ==>` ↵ - -`What is your JPEG (graphics format) library?` -`library filename or 'none' [libjpeg.so] ==>` - **`/Library/Frameworks/UnixImageIO.framework/unix/lib/libjpeg.dylib`** - -`JPEG header directory [default] ==>` -**`/Library/Frameworks/UnixImageIO.framework/Headers`** - -`What is your TIFF (graphics format) library?` -`library filename or 'none' [libtiff.so] ==>` - **`/Library/Frameworks/UnixImageIO.framework/unix/lib/libtiff.dylib`** - -`TIFF header directory [default] ==>` - **`/Library/Frameworks/UnixImageIO.framework/Headers`** - -`What is your PNG (graphics format) library?` -`library filename or 'none' [libpng.so] ==>` - **`/Library/Frameworks/UnixImageIO.framework/unix/lib/libpng.dylib`** - -`PNG header directory [default] ==>` - **`/Library/Frameworks/UnixImageIO.framework/Headers`** - -`What is your Z (compression) library?` -`library filename or 'none' [libz.so] ==>` **`-lz`** - -`Z header directory [default] ==>` ↵ - -`Documentation URL [http://netpbm.sourceforge.net/doc/] ==>` ↵ - -ignore warning about libz - -```sh -echo "JASPERLIB = \ - /Library/Frameworks/UnixImageIO.framework/unix/lib/libjasper.dylib" \ - >> Makefile.config -echo "JASPERHDR_DIR = /Library/Frameworks/UnixImageIO.framework/Headers" \ - >> Makefile.config -``` - -For Tiger: - -```sh -echo "CC = /usr/bin/gcc -arch ppc -arch i386 \ - -isysroot /Developer/SDKs/MacOSX10.4u.sdk" \ - >> Makefile.config -``` - -For Leopard: - -```sh -echo "CC = /usr/bin/gcc -arch ppc -arch i386 -arch ppc64 -arch x86\_64" \ - >> Makefile.config -``` - -```sh -make -make package pkgdir=/path/to/some/temp/dir -``` - -it will create the folder and copy all binaries there - -`sudo ./installnetpbm` - -then answer some more questions: - -`Where is the install package you created with 'make package'?` -`package directory (/tmp/netpbm) ==>` **`/path/to/some/temp/dir`** - -same location as specified in the make package step (lack of - inter-communication!) - -`install prefix (/usr/local/netpbm) ==>` **`/usr/local`** - -same location used for the Netpbm shared library directory (again, -lack of inter-communication!) -(it may ask you if you want to create the dir) - -It'll ask for subfolders for bin, lib, data, headers, man - -For data, type: **`/usr/local/share/netpbm`** - -For man, type: **`/usr/local/share/man`** - -For the rest, use the defaults - -don't create the manweb.conf file - -And that's it. - -### Universal FFMPEG - -version: [0.5](http://www.ffmpeg.org/download.html) - -FFMPEG is constantly changing, and release versions are rare. For -simplicity use the 0.5 release version. If you know anything about SVN -you can try for a more up-to-date FFMPEG. Some options used below are -not in more recent SVN versions, so if configure complains about an -invalid option, just remove it. - -This will build a basic FFMPEG as dynamic libraries for use with GRASS. -The default static libraries don't work now because of reloc errors and -the need for extra link flags. No extra formats are included, such as -mp3lame and xvid. None of the ffmpeg programs are included either (GRASS -doesn't need them). - -It's a multi-step build, 1 for each architecture. Create a folder -inside the source for each arch: - -```sh -mkdir build-i386 -mkdir build-ppc -``` - -and also for Leopard: - -```sh -mkdir build-x86_64 -mkdir build-ppc64 -``` - -For i386 and ppc arches, if building on Tiger, you must add the -following to extra-cflags and extra-ldflags in the configure command: - -`-isysroot /Developer/SDKs/MacOSX10.4u.sdk` - -For i386: - -```sh -cd build-i386 -../configure \ - --arch=i386 \ - --disable-amd3dnow \ - --disable-debug \ - --disable-ffmpeg \ - --disable-ffplay \ - --disable-ffserver \ - --disable-network \ - --disable-static \ - --disable-vhook \ - --enable-gpl \ - --enable-pthreads \ - --enable-shared \ - --enable-swscale \ - --extra-cflags="-arch i386" \ - --extra-ldflags="-arch i386" -``` - -If you are building *on* a PPC Mac, you need to add the flag -"`--enable-cross-compile`" to the above configure command. - -```sh -make -sudo make install -``` - -Install at this point installs everything. We don't care about the -installed libraries yet, we just need the headers there. - -Now, the PPC build: - -```sh -cd ../build-ppc -../configure \ - --arch=ppc \ - --disable-debug \ - --disable-ffmpeg \ - --disable-ffplay \ - --disable-ffserver \ - --disable-network \ - --disable-static \ - --disable-vhook \ - --enable-altivec \ - --enable-gpl \ - --enable-pthreads \ - --enable-shared \ - --enable-swscale \ - --extra-cflags="-arch ppc" \ - --extra-ldflags="-arch ppc" -make -``` - -Don't install this one, there is no need. - -If you are building for Leopard, also do the 64bit varieties, otherwise -skip to the lipo step below. - -For x86_64: - -```sh -cd build-x86_64 -../configure \ - --arch=x86_64 \ - --disable-amd3dnow \ - --disable-debug \ - --disable-ffmpeg \ - --disable-ffplay \ - --disable-ffserver \ - --disable-network \ - --disable-static \ - --disable-vhook \ - --enable-gpl \ - --enable-pthreads \ - --enable-shared \ - --enable-swscale \ - --extra-cflags="-arch x86\_64" \ - --extra-ldflags="-arch x86_64" -``` - -Again, if you are building *on* a PPC Mac, you need to add the flag -"`--enable-cross-compile`" to the above configure command. - -`make` - -And ppc64: - -```sh -cd ../build-ppc64 -../configure \ - --arch=ppc64 \ - --disable-debug \ - --disable-ffmpeg \ - --disable-ffplay \ - --disable-ffserver \ - --disable-network \ - --disable-static \ - --disable-vhook \ - --enable-altivec \ - --enable-gpl \ - --enable-pthreads \ - --enable-shared \ - --enable-swscale \ - --extra-cflags="-arch ppc64" \ - --extra-ldflags="-arch ppc64" -``` - -Here, if you are building *on* an Intel Mac, you need to add the flag -"`--enable-cross-compile`" to the above configure command. Also, it -will fail to completely identify it as 64bit, so you need to fix 2 -files. - -In **config.h**, find the `HAVE_PPC64` define and set it to 1: - -`#define HAVE_PPC64 1` - -In **config.mak**, add this line to the end of the file: - -`HAVE_PPC64=yes` - -Then finish: - -`make` - -Now use lipo to merge them together, first drop down to the ffmpeg -source folder: - -`cd ..` - -For each of the libraries: - -libavcodec -libavdevice -libavformat -libavutil -libswscale - -run the following lipo command (fill in the **`[LIBNAME]`** in all -places). For Leopard: - -```sh -sudo lipo -create \ - build-ppc/[LIBNAME]/[LIBNAME].dylib \ - build-i386/[LIBNAME]/[LIBNAME].dylib \ - build-ppc64/[LIBNAME]/[LIBNAME].dylib \ - build-x86_64/[LIBNAME]/[LIBNAME].dylib \ - -output /usr/local/lib/[LIBNAME].dylib -``` - -For Tiger: - -```sh -sudo lipo -create \ - build-ppc/[LIBNAME]/[LIBNAME].dylib \ - build-i386/[LIBNAME]/[LIBNAME].dylib \ - -output /usr/local/lib/[LIBNAME].dylib -``` - -Done. - -© 2006-2008 by the GRASS Development Team - -This program is free software under the GNU General Public License (>=v2). - -\- William Kyngesburye - - - - diff --git a/macosx/app/English.lproj/MainMenu.nib/classes.nib b/macosx/app/English.lproj/MainMenu.nib/classes.nib deleted file mode 100644 index 96de588392f..00000000000 --- a/macosx/app/English.lproj/MainMenu.nib/classes.nib +++ /dev/null @@ -1,4 +0,0 @@ -{ - IBClasses = ({CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }); - IBVersion = 1; -} diff --git a/macosx/app/English.lproj/MainMenu.nib/data.dependency b/macosx/app/English.lproj/MainMenu.nib/data.dependency deleted file mode 100644 index 82ceab424f5..00000000000 --- a/macosx/app/English.lproj/MainMenu.nib/data.dependency +++ /dev/null @@ -1,10 +0,0 @@ - - - - - IBPaletteDependency - - ASKPalette - - - diff --git a/macosx/app/English.lproj/MainMenu.nib/info.nib b/macosx/app/English.lproj/MainMenu.nib/info.nib deleted file mode 100644 index 4dbdc3731c6..00000000000 --- a/macosx/app/English.lproj/MainMenu.nib/info.nib +++ /dev/null @@ -1,19 +0,0 @@ - - - - - IBDocumentLocation - 157 110 356 240 0 0 1280 778 - IBEditorPositions - - 29 - 319 537 269 44 0 0 1280 778 - - IBFramework Version - 446.1 - IBOldestOS - 3 - IBSystem Version - 8R2232 - - diff --git a/macosx/app/English.lproj/MainMenu.nib/keyedobjects.nib b/macosx/app/English.lproj/MainMenu.nib/keyedobjects.nib deleted file mode 100644 index 9f6db0def40..00000000000 Binary files a/macosx/app/English.lproj/MainMenu.nib/keyedobjects.nib and /dev/null differ diff --git a/macosx/app/GRASS.applescript b/macosx/app/GRASS.applescript deleted file mode 100644 index fccf61f86b7..00000000000 --- a/macosx/app/GRASS.applescript +++ /dev/null @@ -1,69 +0,0 @@ --- Created by William Kyngesburye on 2006-12-12. --- GRASS Applescript startup --- COPYRIGHT: (C) 2006-2008 by the GRASS Development Team --- This program is free software under the GPL (>=v2) --- Read the file COPYING that comes with GRASS for details. - --- note: handler order on launch: --- 1-will finish launching (good place to check prefs; no nibs loaded yet) --- 2-launched (good place to show initial windows or dialogs) --- 2.5-open? --- 3-idle (waits for user action) --- --- idle is supposed to be last, but open seems to cause it to think --- it's not idle, so we can process drag-n-drop before idling. No docs --- say explicitly that this is the case, so speed of Mac and process load --- could affect this. - -property grassMap : "" -property grassGui : "" -property grassLaunched : false - -on will finish launching theObject - set grassLaunched to false - -- eventually, catch modifier key here? to show gui choice -end will finish launching - ---on launch theObject ---end launch - -on open maps - --display dialog (count of maps) - if count of maps is 1 then - if (folder of (info for (item 1 of maps))) then - set grassMap to " " & (quoted form of (POSIX path of (item 1 of maps))) - end if - end if - launchgrass() -end open - -on idle theObject - if not grassLaunched then - launchgrass() - end if -end launched - -on launchgrass() - set grassLaunched to true - set grass_path to (posix path of (path to me as string)) & "Contents/MacOS/" - set grass_startup to (quoted form of (grass_path & "grass.sh")) - set grassRun to grass_startup & grassGui & grassMap & "; exit" - - set TerminalRunning to false - try - if ((do shell script "ps -axc | grep '\\bTerminal\\b'") is not null) then - set TerminalRunning to true - end if - end try - - tell application "Terminal" - activate - if TerminalRunning then - do script (grassRun) - else - do script (grassRun) in window 1 - end if - end tell - - tell me to quit -end launchgrass diff --git a/macosx/app/Makefile b/macosx/app/Makefile deleted file mode 100644 index 1299dcacad8..00000000000 --- a/macosx/app/Makefile +++ /dev/null @@ -1,108 +0,0 @@ -# Makefile for building Mac OS X app startup - -MODULE_TOPDIR = ../.. - -include $(MODULE_TOPDIR)/include/Make/Vars.make -MOD_OBJS = - -EXTRA_INC = -EXTRA_CFLAGS = -EXTRA_LDFLAGS = -framework Cocoa -framework AppleScriptKit -ifndef MACOSX_APP_NAME -MACOSX_APP_NAME = GRASS-${GRASS_VERSION_MAJOR}.${GRASS_VERSION_MINOR}.app -endif -APPDIR = $(ARCH_DISTDIR)/${MACOSX_APP_NAME}/Contents -MAKE_DIR_CMD = mkdir -p -m 755 -GRASS_VERSION_BUILD = `/bin/date "+%y%m%d"` - -BUNDLE_VERSION := ${GRASS_VERSION_NUMBER} -# add git commit hash to bundle version if it is a development release -ifneq (,$(findstring dev,$(GRASS_VERSION_RELEASE))) -BUNDLE_VERSION := ${GRASS_VERSION_NUMBER} (${GRASS_VERSION_GIT}) -endif - -# Figure out version for MACOSX_DEPLOYMENT_TARGET, with following order: -# - if MACOSX_SDK is set then it will be used -# - the result of `xcrun --show-sdk-path` -# - check if Command Line Tools' SDKs is present, and use the default version -# - use the MacOS version of building machine -SDK_CMD = "" -ifeq ($(strip $(MACOSX_SDK)),) -SDK_CMD = xcrun --show-sdk-path -else -SDK_CMD = echo "${MACOSX_SDK}" | cut -d \ -f 2 -endif -SDK := $(shell $(SDK_CMD)) - -ifeq ($(SDK),) -ifneq ($(wildcard /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/.*),) -SDK = /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -endif -endif - -ifneq ($(SDK),) -DEPLOYMENT_TARGET_CMD = plutil -extract DefaultProperties.MACOSX_DEPLOYMENT_TARGET \ - xml1 -o - $(SDK)/SDKSettings.plist | awk -F '[<>]' '/string/{print $$3}' -else -DEPLOYMENT_TARGET_CMD = sw_vers -productVersion | cut -d . -f 1,2 -endif - -DEPLOYMENT_TARGET := $(shell $(DEPLOYMENT_TARGET_CMD)) - -# ugly hack - assume gdal prefix starts with -L flag -ifndef GDAL_BIN -GDAL_BIN = `echo "${GDALLIBS}" | sed -e "s,-L,," -e "s,/lib.*$$,/bin:,"` -endif -# ugly hack - extract path to PROJ bin from nad2bin -ifndef PROJ_BIN -PROJ_BIN = `echo "${NAD2BIN}" | sed -e "s,.=,," -e "s,/nad2bin,:,"` -endif -# get python version - use python in path, should be config'd python -PYVER = `python -V 2>&1 | cut -d \ -f 2 | cut -d . -f 1,2` -ifneq ($(filter ppc64 x86_64,$(MACOSX_ARCHS_WXPYTHON)),) -WX64BIT = 1 -else -WX64BIT = 0 -endif - -FILES = \ - $(OBJDIR)/GRASS \ - $(OBJDIR)/Info.plist \ - $(OBJDIR)/GRASS.scpt \ - $(OBJDIR)/grass.sh - -default: $(FILES) - -$(OBJDIR)/main.o: main.m - $(MAKE_DIR_CMD) $(OBJDIR) - $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(EXTRA_INC) -o $@ -c $< - -$(OBJDIR)/GRASS: $(OBJDIR)/main.o - $(MAKE_DIR_CMD) $(OBJDIR) - $(CC) $(LDFLAGS) -o $@ $< $(EXTRA_LDFLAGS) - -$(OBJDIR)/Info.plist: Info.plist.in - $(MAKE_DIR_CMD) $(OBJDIR) - sed -e "s,@GRASS_VERSION_MAJOR@,$(GRASS_VERSION_MAJOR),g" \ - -e "s,@GRASS_VERSION_MINOR@,$(GRASS_VERSION_MINOR),g" \ - -e "s,@GRASS_VERSION_DATE@,$(GRASS_VERSION_DATE),g" \ - -e "s,@BUNDLE_VERSION@,$(BUNDLE_VERSION),g" \ - -e "s,@DEPLOYMENT_TARGET@,$(DEPLOYMENT_TARGET),g" \ - $< > $@ - -$(OBJDIR)/GRASS.scpt: GRASS.applescript - $(MAKE_DIR_CMD) $(OBJDIR) - osacompile -d -x -i /System/Library/Frameworks/AppleScriptKit.framework -o $@ $< - -$(OBJDIR)/grass.sh: grass.sh.in - $(MAKE_DIR_CMD) $(OBJDIR) - sed -e "s,@GDAL_BIN_PATH@,$(GDAL_BIN)," \ - -e "s,@PROJ_BIN_PATH@,$(PROJ_BIN)," \ - -e "s,@X11@,$(USE_X11),g" \ - -e "s,@PYVER@,$(PYVER),g" \ - -e "s,@WX64BIT@,$(WX64BIT),g" \ - -e "s,@GRASS_NAME@,$(GRASS_NAME),g" \ - $< > $@ - -clean: - -rm -rf $(OBJDIR) $(EXTRA_CLEAN_DIRS) $(EXTRA_CLEAN_FILES) diff --git a/macosx/app/PkgInfo b/macosx/app/PkgInfo deleted file mode 100644 index 6f749b0f376..00000000000 --- a/macosx/app/PkgInfo +++ /dev/null @@ -1 +0,0 @@ -APPL???? diff --git a/macosx/app/build_gui_user_menu.sh b/macosx/app/build_gui_user_menu.sh deleted file mode 100755 index e4bc6e7f250..00000000000 --- a/macosx/app/build_gui_user_menu.sh +++ /dev/null @@ -1,82 +0,0 @@ -#!/bin/sh - -# Build addon menu files, from the global /Library/GRASS/$GRASS_MMVER/Modules -# and the user's $HOME/Library/GRASS/$GRASS_MMVER/Modules. - -# test files to make sure they are appropriate for adding to the GUI menu. -# Using 'file', assume executable binaries OK. Check scripts to see if they -# have GRASS options configured - a simple grep for #%Module. -# Other script languages may need their own test. - -# addon commands can't have spaces in them or sh for loop messes up. -# may be my limited knowledge of sh scripting and there could be a way. - -GRASS_MMVER=`cut -d . -f 1-2 "$GISBASE/etc/VERSIONNUMBER"` -BINDIR="$GISBASE_USER/Modules/bin" -SCRIPTDIR="$GISBASE_USER/Modules/scripts" -BINDIRG="$GISBASE_SYSTEM/Modules/bin" -SCRIPTDIRG="$GISBASE_SYSTEM/Modules/scripts" -MENUDIR="$GISBASE_USER/Modules/etc" - -echo "Rebuilding Addon menu..." - -# just to make sure (startup should have created it) -mkdir -p "$MENUDIR" -echo "# generated by grass startup" > "$MENUDIR/xtnmenu.dat" - -# global addons: -if [ -d "$BINDIRG" ] ; then - cd "$BINDIRG" - CMDLISTG=`ls -1 2> /dev/null` -else - CMDLISTG="" -fi -if [ -d "$SCRIPTDIRG" ] ; then - cd "$SCRIPTDIRG" - CMDLISTG="$CMDLISTG"$'\n'"`ls -1 2> /dev/null`" -fi -CMDLISTG=`echo "$CMDLISTG" | sort -u` -CMDGFOUND="" - -if [ "$CMDLISTG" != "" ] ; then - for i in $CMDLISTG - do - if [ -f "$BINDIRG/$i" ] ; then - ftype="`file $BINDIRG/$i`" - else - ftype="`file $SCRIPTDIRG/$i`" - fi - if [ "`echo $ftype | grep 'Mach-O'`" ] || [ "`grep '#% *Module' $BINDIRG/$i 2> /dev/null`" ] || [ "`grep '#% *Module' $SCRIPTDIRG/$i 2> /dev/null`" ] ; then - echo "main:$i:$i:$i" >> "$MENUDIR/xtnmenu.dat" - CMDGFOUND="1" - fi - done -fi - -# user addons: -CMDFIRST="1" -cd "$BINDIR" -CMDLIST=`ls -1 2> /dev/null` -if [ -d "$SCRIPTDIR" ] ; then - cd "$SCRIPTDIR" - CMDLIST="$CMDLIST"$'\n'"`ls -1 2> /dev/null`" -fi -CMDLIST=`echo "$CMDLIST" | sort -u` - -if [ "$CMDLIST" != "" ] ; then - for i in $CMDLIST - do - if [ -f "$BINDIR/$i" ] ; then - ftype="`file $BINDIR/$i`" - else - ftype="`file $SCRIPTDIR/$i`" - fi - if [ "`echo $ftype | grep 'Mach-O'`" ] || [ "`grep '#% *Module' $BINDIR/$i 2> /dev/null`" ] || [ "`grep '#% *Module' $SCRIPTDIR/$i 2> /dev/null`" ] ; then - if [ "$CMDFIRST" ] && [ "$CMDGFOUND" ] ; then - echo "separator" >> "$MENUDIR/xtnmenu.dat" - CMDFIRST="" - fi - echo "main:$i:$i:$i" >> "$MENUDIR/xtnmenu.dat" - fi - done -fi diff --git a/macosx/app/build_html_user_index.sh b/macosx/app/build_html_user_index.sh deleted file mode 100755 index 0dd7271f0b0..00000000000 --- a/macosx/app/build_html_user_index.sh +++ /dev/null @@ -1,190 +0,0 @@ -#!/bin/sh - -#generates user addon HTML man pages docs/html/index.html -# Markus Neteler, 2003, 2004, 2005, 2006 - -# William Kyngesburye: -# This one builds the addon index, from both the global -# /Library/GRASS/$GRASS_MMVER/Modules and the user's -# $HOME/Library/$GRASS_MMVER/GRASS/Modules -# Each is in their own section, in the same index file. -# -# global help pages are symlinked to the user dir, so user doesn't need perms -# to write there for redirects. -# main and section indexes from GRASS.app are redirected from user dir, not -# symlinked, so relative paths stay valid. - -############# nothing to configure below ############ - -# $1 is current path to GRASS.app/Contents/MacOS, defaults to /Applications -if [ "$1" != "" ] ; then - GISBASE=$1 -else - GISBASE="/Applications/GRASS-$GRASS_MMVER.app/Contents/MacOS" -fi - -GRASS_MMVER=`cut -d . -f 1-2 "$GISBASE/etc/VERSIONNUMBER"` -GRASSVERSION=`cat "$GISBASE/etc/VERSIONNUMBER"` -HTMLDIR="$GISBASE_USER/Modules/docs/html" -HTMLDIRG="$GISBASE_SYSTEM/Modules/docs/html" - -write_html_header() -{ -# $1: filename -# $2: page title -# $3: is it main index - -echo " - - - $2 - - " > $1 -if [ "$3" ] ; then - echo " - - " >> $1 -fi -echo " - - - -\"GRASS -
      - -

      GRASS GIS $GRASSVERSION Reference Manual

      - -

      Geographic Resources Analysis Support System, commonly referred to as -GRASS, -is a Geographic Information System (GIS) used for geospatial data management -and analysis, image processing, graphics/maps production, spatial modeling, -and visualization. GRASS is currently used in academic and commercial settings -around the world, as well as by many governmental agencies and environmental -consulting companies.

      - -

      This reference manual details the use of modules distributed with -Geographic Resources Analysis Support System (GRASS), an open source (GNU -GPL'ed), image processing and geographic information system (GIS).

      - -" >> $1 -} - -write_html_footer() -{ -# $1: filename -echo "
      " >> $1 -echo "

      Help Index | Full Index | Addon Index
      " >> $1 -echo "© 2003-2008 GRASS Development Team

      " >> $1 -echo "" >> $1 -echo "" >> $1 -} - -FULLINDEX=addon_index.html - -################ -echo "Rebuilding Addon HTML manual pages index..." - -#copy over CSS: -cp -f "$GISBASE/docs/html/grassdocs.css" "$HTMLDIR/" -#copy over GRASS logo: -cp -f "$GISBASE/docs/html/grass_logo.png" "$HTMLDIR/" -cp -f "$GISBASE/docs/html/grass_icon.png" "$HTMLDIR/" - -#process all global HTML pages: -if [ -d "$HTMLDIRG" ] ; then -cd "$HTMLDIRG" - -#get list of available GRASS modules: -CMDLISTG=`ls -1 *.*.html 2> /dev/null | grep -v index.html | cut -d'.' -f1 | sort -u` -else -CMDLISTG="" -fi - -#process all user HTML pages: -cd "$HTMLDIR" - -# don't really need to delete these, as removed global modules won't get indexed, -# though old symlinks will accumulate. I'm just worried about wildcard deletes. -#rm -f global_*.html - -#get list of available GRASS modules: -CMDLIST=`ls -1 *.*.html 2> /dev/null | grep -v index.html | cut -d'.' -f1 | sort -u` - -#write main index: -#echo "Generating HTML manual pages index (help system)..." -write_html_header $FULLINDEX "GRASS GIS $GRASSVERSION Addon Reference Manual" 1 -echo "

      Command guide:

      " >> $FULLINDEX -echo "" >> $FULLINDEX -echo "" >> $FULLINDEX -echo "" >> $FULLINDEX -echo "" >> $FULLINDEX -echo "" >> $FULLINDEX -echo "" >> $FULLINDEX -echo "
        d.* display commands  ps.* postscript commands
        db.* database commands  r.* raster commands
        g.* general commands  r3.* raster3D commands
        i.* imagery commands  v.* vector commands
        m.* miscellaneous commands
      " >> $FULLINDEX - -# global commands: -echo "

      Global addon command index:

      " >> $FULLINDEX -echo "" >> $FULLINDEX -if [ "$CMDLISTG" = "" ] ; then - echo "" >> $FULLINDEX -else - for i in $CMDLISTG - do - cd "$HTMLDIRG" - CMDLISTI="`ls -1 $i.*.html`" - cd "$HTMLDIR" - for i in $CMDLISTI - do - BASENAME=`basename $i .html` - SHORTDESC="`cat "$HTMLDIRG/$i" | awk '/NAME/,/SYNOPSIS/' | grep '' | cut -d'-' -f2- | sed 's+^ ++g' | grep -vi 'SYNOPSIS' | head -n 1`" -# echo "" >> $FULLINDEX - # make them local to user to simplify page links - echo "" >> $FULLINDEX - ln -sf "$HTMLDIRG/$i" global_$i - done - done -fi -echo "
      [There are no global addon help pages.]
      $BASENAME $SHORTDESC
      $BASENAME $SHORTDESC
      " >> $FULLINDEX - -# user commands: -echo "

      User addon command index:

      " >> $FULLINDEX -echo "" >> $FULLINDEX -if [ "$CMDLIST" = "" ] ; then - echo "" >> $FULLINDEX -else - for i in $CMDLIST - do - for i in `ls -1 $i.*.html` - do - BASENAME=`basename $i .html` - SHORTDESC="`cat $i | awk '/NAME/,/SYNOPSIS/' | grep '' | cut -d'-' -f2- | sed 's+^ ++g' | grep -vi 'SYNOPSIS' | head -n 1`" - echo "" >> $FULLINDEX - done - done -fi -echo "
      [There are no user addon help pages.]
      $BASENAME $SHORTDESC
      " >> $FULLINDEX - -write_html_footer $FULLINDEX -# done full index - -# user redirects to app dir for main index files - -for i in index full_index display database general imagery misc postscript raster raster3D vector -do -echo " - - - - - - - - -" > $i.html -done - -# add Help Viewer links in user docs folder - -mkdir -p $HOME/Library/Documentation/Help/ -ln -sfh ../../GRASS/$GRASS_MMVER/Modules/docs/html $HOME/Library/Documentation/Help/GRASS-$GRASS_MMVER-addon -ln -sfh $GISBASE/docs/html $HOME/Library/Documentation/Help/GRASS-$GRASS_MMVER diff --git a/macosx/app/grass.sh.in b/macosx/app/grass.sh.in deleted file mode 100755 index 07bb58fdc83..00000000000 --- a/macosx/app/grass.sh.in +++ /dev/null @@ -1,222 +0,0 @@ -#! /bin/sh -############################################################################# -# -# MODULE: GRASS Initialization -# AUTHOR(S): Justin Hickey - Thailand - jhickey@hpcc.nectec.or.th -# William Kyngesburye - kyngchaos@kyngchaos.com -# PURPOSE: The source file for this shell script is in -# macosx/app/grass.sh.in and is the grass startup script for -# the Mac OS X application build. -# COPYRIGHT: (C) 2000-2008 by 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. -# -############################################################################# - -# Mac app only startup shell - complete rewrite for starting from a GRASS.app -# in Mac OS X. Sets defaults for unset env, adds some Mac-only config. - -trap "echo 'User break!' ; exit" 2 3 9 15 - -# dummy for now - just saying we're starting GRASS.app on OSX -GRASS_OS_STARTUP="Mac.app" -export GRASS_OS_STARTUP - -SYSARCH=`uname -p` -SYSVER=`uname -r | cut -d . -f 1` - -GISBASE=`dirname "$0"` -export GISBASE -grass_ver=`cut -d . -f 1-2 "$GISBASE/etc/VERSIONNUMBER"` - -#override config dir. -GRASS_CONFIG_DIR="Library/Preferences/GRASS/$grass_Ver" - -GISBASE_USER="$HOME/Library/GRASS/$grass_ver" -export GISBASE_USER -GISBASE_SYSTEM="/Library/GRASS/$grass_ver" -export GISBASE_SYSTEM - -# for extra utils -# ideally user should have these in their PATH, but make sure here -PATH="@GDAL_BIN_PATH@@PROJ_BIN_PATH@$PATH" -export PATH - -# add some OS X style app support paths, and create user one if missing. -mkdir -p "$GISBASE_USER/Modules/bin" -mkdir -p "$GISBASE_USER/Modules/scripts" -if [ ! "$GRASS_ADDON_BASE" ] ; then - GRASS_ADDON_BASE="$GISBASE_USER/Modules" -fi -export GRASS_ADDON_BASE - -mkdir -p "$GISBASE_USER/Modules/etc" -addpath="$GISBASE_USER/Modules/etc:$GISBASE_SYSTEM/Modules/etc" -if [ "$GRASS_ADDON_ETC" ] ; then - GRASS_ADDON_ETC="$GRASS_ADDON_ETC:$addpath" -else - GRASS_ADDON_ETC="$addpath" -fi -export GRASS_ADDON_ETC - -mkdir -p "$GISBASE_USER/Modules/lib" -addpath="$GISBASE_USER/Modules/lib:$GISBASE_SYSTEM/Modules/lib" -if [ "$DYLD_LIBRARY_PATH" ] ; then - DYLD_LIBRARY_PATH="$addpath:$DYLD_LIBRARY_PATH" -else - DYLD_LIBRARY_PATH="$addpath" -fi -export DYLD_LIBRARY_PATH - -mkdir -p "$GISBASE_USER/Modules/docs/html" - -# rebuild addon html index and gui menus -"$GISBASE/etc/build_html_user_index.sh" "$GISBASE" -"$GISBASE/etc/build_gui_user_menu.sh" - -# user fontcap files -if [ ! "$GRASS_FONT_CAP" ] ; then - GRASS_FONT_CAP="$GISBASE_USER/Modules/etc/fontcap" -fi -export GRASS_FONT_CAP - -# default xterm Terminal emulator -# note: only app startup defaults this way, CLI build still defaults to xterm -if [ ! "$GRASS_XTERM" ] ; then - GRASS_XTERM="$GISBASE/etc/grass-xterm-mac" - export GRASS_XTERM -fi - -# determine current term application (when grass.sh is run directly by the user) -# use osascript so we can get name of term app, not shell process -USERTERM=`osascript -e 'tell application "System Events" to item 1 of (get name of processes whose frontmost is true)'` -if [ ! "$USERTERM" ] ; then - USERTERM="Terminal" -fi - -### X11 stuff - -x11="" -if [ "@X11@" ] ; then - -# start X11 if not running -# some users may be annoyed by this, but X11 required for some stuff for now -if [ -d "/Applications/Utilities/X11.app" ] ; then - # no need to start Leopard X11.app - if [ ! -d "/usr/X11" ] ; then - # check if running anyways, even though not necessary with "open" - if [ ! "`ps -axc | grep 'X11$'`" ] ; then - open /Applications/Utilities/X11.app - fi - x11="tig" - else - x11="leo" - fi -fi - -# make sure X11 is in PATH -pathX11=`echo "$PATH" | grep '/usr/X11\(R6\)\?/bin'` -if [ ! "$pathX11" ] ; then - if [ -d "/usr/X11" ] ; then - PATH="$PATH:/usr/X11/bin" - export PATH - elif [ -d "/usr/X11R6" ] ; then - PATH="$PATH:/usr/X11R6/bin" - export PATH - fi -fi - -# make sure there is a DISPLAY set -if [ ! "$DISPLAY" ] ; then - DISPLAY=:0.0 - export DISPLAY -fi - -fi # X11 - -### end X11 block - -# for bundling wxpython -# change GRASS_WXBUNDLED and pyver_want here from bundle.make, or manually -GRASS_WXBUNDLED= -export GRASS_WXBUNDLED -pyexe="pythonw" -GRASS_PYTHONWX="$pyexe" -pyver_want="@PYVER@" -GRASS_WX64BIT="@WX64BIT@" -export GRASS_WX64BIT -# make sure python version used matches what wxpython wants -py="" -# did user set GRASS_PYTHON already? check it (must have pythonw) -if [ "$GRASS_PYTHON" ] ; then - py=`echo "$GRASS_PYTHON" | grep pythonw` - if [ "$py" ] ; then - case $GRASS_PYTHON in - /*) py="$GRASS_PYTHON" ;; - *) py=`type -p $GRASS_PYTHON` - esac - pyver=`$py -V 2>&1 | grep " $pyver_want"` - if [ ! "$pyver" ] ; then - py="" - fi - else - py="" - fi -fi # GRASS_PYTHON -# try in shell path -if [ ! "$py" ] ; then - py=`type -p pythonw$pyver_want` - if [ "$py" ] ; then - pyver=`$py -V 2>&1 | grep " $pyver_want"` - if [ ! "$pyver" ] ; then - py="" - fi - fi -fi -# try standard framework -if [ ! "$py" ] ; then - py="/Library/Frameworks/Python.framework/Versions/$pyver_want/bin/python" - if [ ! -x "$py" ] ; then - py="" - fi -fi -# try system (may be the same as in shell path) -if [ ! "$py" ] ; then - py="/usr/bin/pythonw$pyver_want" - pyver=`$py -V 2>&1 | grep " $pyver_want"` - if [ ! "$pyver" ] ; then - py="" - fi -fi - -if [ "$py" ] ; then - echo "$pyver found." - GRASS_PYTHONWX="$py" -else - echo "Warning: No Python $pyver_want found, needed by wxPython." - echo " The wxPython GUI may not work properly." -fi -export GRASS_PYTHONWX -# we will execute python scripts from the python wrapper script -GRASS_PYTHON="python" -export GRASS_PYTHON - -# if grassrc has text startup, switch back to Terminal (gotta duplicate some init.sh stuff) -# only applies to Tiger - Leopard X11 opens automatically as needed -GRASSRC="$HOME/$GRASS_CONFIG_DIR/rc" -if [ ! "$GRASS_GUI" ] ; then - if [ -f "$GRASSRC" ] ; then - GRASS_GUI=`awk '/GRASS_GUI/ {print $2}' "$GRASSRC"` - fi - if [ ! "$GRASS_GUI" ] ; then - GRASS_GUI="wxpython" - fi -fi -if [ "$GRASS_GUI" = "text" ] && [ "$x11" = "tig" ] ; then - osascript -e "tell application \"$USERTERM\" to activate" -fi - -# use the python wrapper to start grass -exec "$GISBASE/bin/python" "$GISBASE/@GRASS_NAME@" "$@" diff --git a/macosx/app/main.m b/macosx/app/main.m deleted file mode 100644 index 4192b270e7b..00000000000 --- a/macosx/app/main.m +++ /dev/null @@ -1,8 +0,0 @@ -extern void ASKInitialize(); -extern int NSApplicationMain(int argc, const char *argv[]); - -int main(int argc, const char *argv[]) { - ASKInitialize(); - - return NSApplicationMain(argc, argv); -} diff --git a/macosx/app/python_wrapper b/macosx/app/python_wrapper deleted file mode 100755 index 66655417f3a..00000000000 --- a/macosx/app/python_wrapper +++ /dev/null @@ -1,45 +0,0 @@ -#! /bin/sh -############################################################################# -# -# MODULE: python wrapper -# AUTHOR(S): William Kyngesburye - kyngchaos@kyngchaos.com -# PURPOSE: handle arch options on OSX for running python. -# COPYRIGHT: (C) 2000-2008 by 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. -# -############################################################################# - -# wxpython-based scripts must be started from pythonw. And depending on the -# installed wxpython, it may only be available in 32bits, while python may -# at the same time run 64bit by default. Newer systems may also reexec python -# as pythonw automatically as needed, except they don't respond to the arch -# command (and that's an Apple-only thing, and only when /usr/bin/python is -# used, yet /usr/bin/pythonw2.6 DOES respond to arch). The most universal -# and reliable method is probably to not depend on Apple's customizations and -# execute pythonw directly, 32bit if necessary. - -if [ -z "$GISBASE" ] ; then - echo "You must be in GRASS GIS to run this program." >&2 - exit 1 -fi - -SYSARCH=`uname -p` -SYSVER=`uname -r | cut -d . -f 1` - -if [ ! "$GRASS_PYTHONWX" ] ; then - GRASS_PYTHONWX="pythonw" -fi -# can't run python 64bit if wx not 64bit, assume OSX 10.5+ possible 64bit -if [ $(($SYSVER)) -gt 5 ] && [ "$GRASS_WX64BIT" = "0" ] ; then - case $SYSARCH in - powerpc) pyarch="-ppc" ;; - i386) pyarch="-i386" ;; - *) pyarch="" ;; - esac - exec /usr/bin/arch $pyarch "$GRASS_PYTHONWX" "$@" -else - exec "$GRASS_PYTHONWX" "$@" -fi diff --git a/macosx/bundle.make b/macosx/bundle.make deleted file mode 100644 index 4b3c26bb6ea..00000000000 --- a/macosx/bundle.make +++ /dev/null @@ -1,24 +0,0 @@ -# extra binaries and data to bundle into the GRASS.app package -# -# use ${INSTALL} to copy binaries. -# use ${INSTALL_DATA} for headers and other non-executables. -# use ${MAKE_DIR_CMD} to create a subfolder if needed. -# use ${LN} and ${LN_DIR} to symlink files and folders respectively. -# -# Destinations include: bin and lib and should be prefixed by -# ${INST_DIR_TARGET}/ -# Includes should not normally be needed. -# -# For libraries, make sure to make symlinks if the symlink is the linked name. -# ie, libfoo.1.0.dylib is the library file, but libfoo.1.dylib is the link name: -# ${LN} libfoo.1.0.dylib ${INST_DIR_TARGET}/lib/libfoo.1.dylib -# If you're not sure, make all symlinks for a library. -# -# use one line per file after the "bundle-macosx:" line, indented with a tab. -# ie: -# ${INSTALL} /usr/local/bin/gpsbabel ${INST_DIR_TARGET}/bin -# ${INSTALL} /usr/local/pgsql/lib/libpq.5.1.dylib ${INST_DIR_TARGET}/lib -# ${LN} libpq.5.1.dylib ${INST_DIR_TARGET}/lib/libpq.5.dylib - -bundle-macosx: - @# add custom bundle commands here: diff --git a/macosx/pkg/resources/Description.plist.in b/macosx/pkg/resources/Description.plist.in deleted file mode 100644 index a61f2fec235..00000000000 --- a/macosx/pkg/resources/Description.plist.in +++ /dev/null @@ -1,10 +0,0 @@ - - - - - IFPkgDescriptionDescription - GRASS GIS @GRASS_VER@ - IFPkgDescriptionTitle - GRASS-@GRASS_VER@ - - diff --git a/macosx/pkg/resources/Info.plist.in b/macosx/pkg/resources/Info.plist.in deleted file mode 100644 index becf807bc3b..00000000000 --- a/macosx/pkg/resources/Info.plist.in +++ /dev/null @@ -1,40 +0,0 @@ - - - - - CFBundleGetInfoString - GRASS-@GRASS_VERSION_MAJOR@.@GRASS_VERSION_MINOR@.@GRASS_VERSION_RELEASE@ - CFBundleIdentifier - org.osgeo.grass@GRASS_VERSION_MAJOR@_@GRASS_VERSION_MINOR@ - CFBundleShortVersionString - @GRASS_VERSION_MAJOR@.@VER_MINOR_REL@.@VER_DATE@ - IFMajorVersion - @GRASS_VERSION_MAJOR@ - IFMinorVersion - @GRASS_VERSION_MINOR@ - IFPkgFlagAllowBackRev - - IFPkgFlagAuthorizationAction - AdminAuthorization - IFPkgFlagBackgroundAlignment - topleft - IFPkgFlagBackgroundScaling - none - IFPkgFlagDefaultLocation - @PKG_INST_DIR@ - IFPkgFlagFollowLinks - - IFPkgFlagIsRequired - - IFPkgFlagOverwritePermissions - - IFPkgFlagRelocatable - - IFPkgFlagRestartAction - NoRestart - IFPkgFlagRootVolumeOnly - - IFPkgFlagUpdateInstalledLanguages - - - diff --git a/macosx/pkg/resources/License.rtf b/macosx/pkg/resources/License.rtf deleted file mode 100644 index 1da0cb14ce9..00000000000 --- a/macosx/pkg/resources/License.rtf +++ /dev/null @@ -1,374 +0,0 @@ -{\rtf1\ansi\ansicpg1252\cocoartf949 -{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\margl1440\margr1440\vieww9880\viewh8140\viewkind0 -\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\ri1440\ql\qnatural - -\f0\fs24 \cf0 Copyright and License Statement\ -\ -The Geographic Resources Analysis and Support System (GRASS) Geographic Information System (GIS) is Copyright by the GRASS Development Team headquartered at ITC-irst, in Trento, Italy.\ -\ -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.\ -\ -Parts of GRASS are not copyright by the GRASS development team. The original authors hold the copyrights and you have to abide to their licensing terms where noted. (Keep in mind that code linking into GRASS can only be distributed if compatible with the GPL.) \ -\ -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 (GPL) for more details.\ -\ -You should have received a copy of the GNU General Public License along with this program; if not, write to the\ -\ -\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\fi360\ri1440\ql\qnatural -\cf0 Free Software Foundation, Inc.,\ -51 Franklin Street, Fifth Floor\ -Boston, MA 02110-1301 USA\ -\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\ri1440\ql\qnatural -\cf0 \ -Questions regarding GRASS GIS should be directed to the GRASS Development Team at the following address:\ -\ -\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\fi360\ri1440\ql\qnatural -\cf0 GRASS Development Team\ - ITC-Irst\ - c/o M. Neteler\ - SSI/MPA\ - Via Sommarive, 18\ - 38050 Trento (Povo)\ - Italy\ - {\field{\*\fldinst{HYPERLINK "mailto:neteler@itc.it"}}{\fldrslt neteler@itc.it}}\ -\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\ri1440\ql\qnatural -\cf0 \ -Internet: {\field{\*\fldinst{HYPERLINK "https://grass.osgeo.org"}}{\fldrslt https://grass.osgeo.org}}\ -\ -\ -\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\ri1456\ql\qnatural -\cf0 GNU GENERAL PUBLIC LICENSE\ - Version 2, June 1991\ -\ - Copyright (C) 1989, 1991 Free Software Foundation, Inc.\ - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\ - Everyone is permitted to copy and distribute verbatim copies\ - of this license document, but changing it is not allowed.\ -\ - Preamble\ -\ - The licenses for most software are designed to take away your\ -freedom to share and change it. By contrast, the GNU General Public\ -License is intended to guarantee your freedom to share and change free\ -software--to make sure the software is free for all its users. This\ -General Public License applies to most of the Free Software\ -Foundation's software and to any other program whose authors commit\ -to using it. (Some other Free Software Foundation software is covered\ -by the GNU Library General Public License instead.) You can apply it to\ -your programs, too.\ -\ - When we speak of free software, we are referring to freedom, not\ -price. Our General Public Licenses are designed to make sure that you\ -have the freedom to distribute copies of free software (and charge for\ -this service if you wish), that you receive source code or can get it\ -if you want it, that you can change the software or use pieces of it\ -in new free programs; and that you know you can do these things.\ -\ - To protect your rights, we need to make restrictions that forbid\ -anyone to deny you these rights or to ask you to surrender the rights.\ -These restrictions translate to certain responsibilities for you if you\ -distribute copies of the software, or if you modify it.\ -\ - For example, if you distribute copies of such a program, whether\ -gratis or for a fee, you must give the recipients all the rights that\ -you have. You must make sure that they, too, receive or can get the\ -source code. And you must show them these terms so they know their\ -rights.\ -\ - We protect your rights with two steps: (1) copyright the software, and\ -(2) offer you this license which gives you legal permission to copy,\ -distribute and/or modify the software.\ -\ - Also, for each author's protection and ours, we want to make certain\ -that everyone understands that there is no warranty for this free\ -software. If the software is modified by someone else and passed on, we\ -want its recipients to know that what they have is not the original, so\ -that any problems introduced by others will not reflect on the original\ -authors' reputations.\ -\ - Finally, any free program is threatened constantly by software\ -patents. We wish to avoid the danger that redistributors of a free\ -program will individually obtain patent licenses, in effect making the\ -program proprietary. To prevent this, we have made it clear that any\ -patent must be licensed for everyone's free use or not licensed at all.\ -\ - The precise terms and conditions for copying, distribution and\ -modification follow.\ -\ -\ - GNU GENERAL PUBLIC LICENSE\ - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND\ - MODIFICATION\ -\ - 0. This License applies to any program or other work which contains\ -a notice placed by the copyright holder saying it may be distributed\ -under the terms of this General Public License. The "Program", below,\ -refers to any such program or work, and a "work based on the Program"\ -means either the Program or any derivative work under copyright law:\ -that is to say, a work containing the Program or a portion of it,\ -either verbatim or with modifications and/or translated into another\ -language. (Hereinafter, translation is included without limitation in\ -the term "modification".) Each licensee is addressed as "you".\ -\ -Activities other than copying, distribution and modification are not\ -covered by this License; they are outside its scope. The act of\ -running the Program is not restricted, and the output from the Program\ -is covered only if its contents constitute a work based on the\ -Program (independent of having been made by running the Program).\ -Whether that is true depends on what the Program does.\ -\ - 1. You may copy and distribute verbatim copies of the Program's\ -source code as you receive it, in any medium, provided that you\ -conspicuously and appropriately publish on each copy an appropriate\ -copyright notice and disclaimer of warranty; keep intact all the\ -notices that refer to this License and to the absence of any warranty;\ -and give any other recipients of the Program a copy of this License\ -along with the Program.\ -\ -You may charge a fee for the physical act of transferring a copy, and\ -you may at your option offer warranty protection in exchange for a fee.\ -\ - 2. You may modify your copy or copies of the Program or any portion\ -of it, thus forming a work based on the Program, and copy and\ -distribute such modifications or work under the terms of Section 1\ -above, provided that you also meet all of these conditions:\ -\ - a) You must cause the modified files to carry prominent notices\ - stating that you changed the files and the date of any change.\ -\ - b) You must cause any work that you distribute or publish, that in\ - whole or in part contains or is derived from the Program or any\ - part thereof, to be licensed as a whole at no charge to all third\ - parties under the terms of this License.\ -\ - c) If the modified program normally reads commands interactively\ - when run, you must cause it, when started running for such\ - interactive use in the most ordinary way, to print or display an\ - announcement including an appropriate copyright notice and a\ - notice that there is no warranty (or else, saying that you provide\ - a warranty) and that users may redistribute the program under\ - these conditions, and telling the user how to view a copy of this\ - License. (Exception: if the Program itself is interactive but\ - does not normally print such an announcement, your work based on\ - the Program is not required to print an announcement.)\ -\ -\ -These requirements apply to the modified work as a whole. If\ -identifiable sections of that work are not derived from the Program,\ -and can be reasonably considered independent and separate works in\ -themselves, then this License, and its terms, do not apply to those\ -sections when you distribute them as separate works. But when you\ -distribute the same sections as part of a whole which is a work based\ -on the Program, the distribution of the whole must be on the terms of\ -this License, whose permissions for other licensees extend to the\ -entire whole, and thus to each and every part regardless of who wrote it.\ -\ -Thus, it is not the intent of this section to claim rights or contest\ -your rights to work written entirely by you; rather, the intent is to\ -exercise the right to control the distribution of derivative or\ -collective works based on the Program.\ -\ -In addition, mere aggregation of another work not based on the Program\ -with the Program (or with a work based on the Program) on a volume of\ -a storage or distribution medium does not bring the other work under\ -the scope of this License.\ -\ - 3. You may copy and distribute the Program (or a work based on it,\ -under Section 2) in object code or executable form under the terms of\ -Sections 1 and 2 above provided that you also do one of the following:\ -\ - a) Accompany it with the complete corresponding machine-readable\ - source code, which must be distributed under the terms of Sections\ - 1 and 2 above on a medium customarily used for software\ - interchange; or,\ -\ - b) Accompany it with a written offer, valid for at least three\ - years, to give any third party, for a charge no more than your\ - cost of physically performing source distribution, a complete\ - machine-readable copy of the corresponding source code, to be\ - distributed under the terms of Sections 1 and 2 above on a medium\ - customarily used for software interchange; or,\ -\ - c) Accompany it with the information you received as to the offer\ - to distribute corresponding source code. (This alternative is\ - allowed only for noncommercial distribution and only if you\ - received the program in object code or executable form with such\ - an offer, in accord with Subsection b above.)\ -\ -The source code for a work means the preferred form of the work for\ -making modifications to it. For an executable work, complete source\ -code means all the source code for all modules it contains, plus any\ -associated interface definition files, plus the scripts used to\ -control compilation and installation of the executable. However, as a\ -special exception, the source code distributed need not include\ -anything that is normally distributed (in either source or binary\ -form) with the major components (compiler, kernel, and so on) of the\ -operating system on which the executable runs, unless that component\ -itself accompanies the executable.\ -\ -If distribution of executable or object code is made by offering\ -access to copy from a designated place, then offering equivalent\ -access to copy the source code from the same place counts as\ -distribution of the source code, even though third parties are not\ -compelled to copy the source along with the object code.\ -\ -\ - 4. You may not copy, modify, sublicense, or distribute the Program\ -except as expressly provided under this License. Any attempt\ -otherwise to copy, modify, sublicense or distribute the Program is\ -void, and will automatically terminate your rights under this License.\ -However, parties who have received copies, or rights, from you under\ -this License will not have their licenses terminated so long as such\ -parties remain in full compliance.\ -\ - 5. You are not required to accept this License, since you have not\ -signed it. However, nothing else grants you permission to modify or\ -distribute the Program or its derivative works. These actions are\ -prohibited by law if you do not accept this License. Therefore, by\ -modifying or distributing the Program (or any work based on the\ -Program), you indicate your acceptance of this License to do so, and\ -all its terms and conditions for copying, distributing or modifying\ -the Program or works based on it.\ -\ - 6. Each time you redistribute the Program (or any work based on the\ -Program), the recipient automatically receives a license from the\ -original licensor to copy, distribute or modify the Program subject to\ -these terms and conditions. You may not impose any further\ -restrictions on the recipients' exercise of the rights granted herein.\ -You are not responsible for enforcing compliance by third parties to\ -this License.\ -\ - 7. If, as a consequence of a court judgment or allegation of patent\ -infringement or for any other reason (not limited to patent issues),\ -conditions are imposed on you (whether by court order, agreement or\ -otherwise) that contradict the conditions of this License, they do not\ -excuse you from the conditions of this License. If you cannot\ -distribute so as to satisfy simultaneously your obligations under this\ -License and any other pertinent obligations, then as a consequence you\ -may not distribute the Program at all. For example, if a patent\ -license would not permit royalty-free redistribution of the Program by\ -all those who receive copies directly or indirectly through you, then\ -the only way you could satisfy both it and this License would be to\ -refrain entirely from distribution of the Program.\ -\ -If any portion of this section is held invalid or unenforceable under\ -any particular circumstance, the balance of the section is intended to\ -apply and the section as a whole is intended to apply in other\ -circumstances.\ -\ -It is not the purpose of this section to induce you to infringe any\ -patents or other property right claims or to contest validity of any\ -such claims; this section has the sole purpose of protecting the\ -integrity of the free software distribution system, which is\ -implemented by public license practices. Many people have made\ -generous contributions to the wide range of software distributed\ -through that system in reliance on consistent application of that\ -system; it is up to the author/donor to decide if he or she is willing\ -to distribute software through any other system and a licensee cannot\ -impose that choice.\ -\ -This section is intended to make thoroughly clear what is believed to\ -be a consequence of the rest of this License.\ -\ -\ - 8. If the distribution and/or use of the Program is restricted in\ -certain countries either by patents or by copyrighted interfaces, the\ -original copyright holder who places the Program under this License\ -may add an explicit geographical distribution limitation excluding\ -those countries, so that distribution is permitted only in or among\ -countries not thus excluded. In such case, this License incorporates\ -the limitation as if written in the body of this License.\ -\ - 9. The Free Software Foundation may publish revised and/or new\ -versions of the General Public License from time to time. Such new\ -versions will be similar in spirit to the present version, but may differ in\ -detail to address new problems or concerns.\ -\ -Each version is given a distinguishing version number. If the Program\ -specifies a version number of this License which applies to it and "any\ -later version", you have the option of following the terms and conditions\ -either of that version or of any later version published by the Free\ -Software Foundation. If the Program does not specify a version number\ -of this License, you may choose any version ever published by the Free\ -Software Foundation.\ -\ - 10. If you wish to incorporate parts of the Program into other free\ -programs whose distribution conditions are different, write to the author\ -to ask for permission. For software which is copyrighted by the Free\ -Software Foundation, write to the Free Software Foundation; we\ -sometimes make exceptions for this. Our decision will be guided by the\ -two goals of preserving the free status of all derivatives of our free\ -software and of promoting the sharing and reuse of software generally.\ -\ - NO WARRANTY\ -\ - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\ -\ - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\ -\ - END OF TERMS AND CONDITIONS\ -\ -\ - How to Apply These Terms to Your New Programs\ -\ - If you develop a new program, and you want it to be of the greatest\ -possible use to the public, the best way to achieve this is to make it\ -free software which everyone can redistribute and change under these terms.\ -\ - To do so, attach the following notices to the program. It is safest\ -to attach them to the start of each source file to most effectively\ -convey the exclusion of warranty; and each file should have at least\ -the "copyright" line and a pointer to where the full notice is found.\ -\ - \ - Copyright (C) \ -\ - 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.\ -\ - You should have received a copy of the GNU General Public License\ - along with this program; if not, write to the Free Software\ - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA \ - 02110-1301 USA\ -\ -\ -Also add information on how to contact you by electronic and paper mail.\ -\ -If the program is interactive, make it output a short notice like this\ -when it starts in an interactive mode:\ -\ - Gnomovision version 69, Copyright (C) year name of author\ - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\ - This is free software, and you are welcome to redistribute it\ - under certain conditions; type `show c' for details.\ -\ -The hypothetical commands `show w' and `show c' should show the\ -appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits\ -your program.\ -\ -You should also get your employer (if you work as a programmer) or your\ -school, if any, to sign a "copyright disclaimer" for the program, if\ -necessary. Here is a sample; alter the names:\ -\ - Yoyodyne, Inc., hereby disclaims all copyright interest in the program\ - `Gnomovision' (which makes passes at compilers) written by James\ - Hacker.\ -\ - , 1 April 1989\ - Ty Coon, President of Vice\ -\ -This General Public License does not permit incorporating your program\ -into proprietary programs. If your program is a subroutine library, you\ -may consider it more useful to permit linking proprietary applications with\ -the library. If this is what you want to do, use the GNU Library General\ -Public License instead of this License.\ -} diff --git a/macosx/pkg/resources/ReadMe.rtf b/macosx/pkg/resources/ReadMe.rtf deleted file mode 100644 index 38cd111f3b1..00000000000 --- a/macosx/pkg/resources/ReadMe.rtf +++ /dev/null @@ -1,258 +0,0 @@ -{\rtf1\ansi\deff3\adeflang1025 -{\fonttbl{\f0\froman\fprq2\fcharset0 Times New Roman;}{\f1\froman\fprq2\fcharset2 Symbol;}{\f2\fswiss\fprq2\fcharset0 Arial;}{\f3\froman\fprq2\fcharset0 Liberation Serif{\*\falt Times New Roman};}{\f4\fswiss\fprq2\fcharset0 Liberation Sans{\*\falt Arial};}{\f5\froman\fprq0\fcharset1 Helvetica{\*\falt Arial};}{\f6\froman\fprq0\fcharset1 Monaco;}{\f7\fnil\fprq2\fcharset0 WenQuanYi Zen Hei Sharp;}{\f8\fnil\fprq2\fcharset0 DejaVu Sans;}{\f9\fswiss\fprq0\fcharset1 DejaVu Sans;}} -{\colortbl;\red0\green0\blue0;\red0\green0\blue128;\red128\green128\blue128;} -{\stylesheet{\s0\snext0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033 Normal;} -{\*\cs15\snext15\cf2\ul\ulc0\langfe255\alang255\lang255 Internet Link;} -{\s16\sbasedon0\snext17\sb240\sa120\keepn\dbch\af7\dbch\af8\afs28\loch\f4\fs28 Heading;} -{\s17\sbasedon0\snext17\sl288\slmult1\sb0\sa140 Text Body;} -{\s18\sbasedon17\snext18\sl288\slmult1\sb0\sa140\dbch\af9 List;} -{\s19\sbasedon0\snext19\sb120\sa120\noline\i\dbch\af9\afs24\ai\fs24 Caption;} -{\s20\sbasedon0\snext20\noline\dbch\af9 Index;} -}{\info{\creatim\yr0\mo0\dy0\hr0\min0}{\revtim\yr0\mo0\dy0\hr0\min0}{\printim\yr0\mo0\dy0\hr0\min0}{\comment LibreOffice}{\vern67240962}}\deftab720 -\viewscale120 -{\*\pgdsctbl -{\pgdsc0\pgdscuse451\pgwsxn12240\pghsxn15840\marglsxn1440\margrsxn1440\margtsxn1440\margbsxn1440\pgdscnxt0 Default Style;}} -\formshade{\*\pgdscno0}\paperh15840\paperw12240\margl1440\margr1440\margt1440\margb1440\sectd\sbknone\sectunlocked1\pgndec\pgwsxn12240\pghsxn15840\marglsxn1440\margrsxn1440\margtsxn1440\margbsxn1440\ftnbj\ftnstart1\ftnrstcont\ftnnar\aenddoc\aftnrstcont\aftnstart1\aftnnrlc -\pgndec\pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\qc\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\b\rtlch \ltrch\loch\fs48\loch\f5 -GRASS GIS} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\ul\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -Starting GRASS}{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - - there are a few different ways to start GRASS:} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -- Double-click GRASS.app.} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -- Drag a mapset folder onto GRASS.app to start GRASS in that mapset.} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -- In a Terminal, type (substitute the correct application name if a different version is installed), the mapset path is optional:} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs24\loch\f6 -open -a "GRASS-}{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs24\loch\f6 -7}{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs24\loch\f6 -.}{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs24\loch\f6 -1}{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs24\loch\f6 -.app" \{/path/to/db/location/mapset\}} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -GRASS.app opens a Terminal window to start GRASS in the normal commandline way. After starting the Terminal it quits, leaving GRASS running in the Terminal. While a GRASS session is running you can run GRASS.app again to start another session.} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -On Panther and Tiger, X11 is also started regardless of the GUI you use. On Leopard and above, X11 is started when it's needed, not immediately.} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\ulnone\ulc0\b\rtlch \ltrch\loch\fs28\loch\f5 -Setup} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -See GRASS }{{\field{\*\fldinst HYPERLINK "http://grass.gdf-hannover.de/" }{\fldrslt \cf2\ul\ulc0\langfe255\alang255\lang255\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -documentation}{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -}} for details on setup and use. See Apple's }{\cf1\ul\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -Help Viewer}{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - (from most any application's Help menu) for command documentation.} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -By default the startup uses the included X11 Tcl/Tk for the GUI. If you have used GRASS before, }{\cf1\ulnone\ulc0\b\rtlch \ltrch\loch\fs28\loch\f5 -it will pick up your preferences for GUI/Text}{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -, so you should make sure they are set correctly. So if the GUI doesn't start, check your GRASS_GUI setting.} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\ulnone\ulc0\b\rtlch \ltrch\loch\fs28\loch\f5 -Note:}{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - if you use the Apple default }{\cf1\ulnone\ulc0\b\rtlch \ltrch\loch\fs28\loch\f5 -bash}{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - shell, make sure you don't have a }{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs24\loch\f6 -.cshrc}{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - init hanging around. This could be from a previous version of OS X, or installed software might add one for some reason. Some commands might not run correctly if this file exists, yet you are using bash, so delete it if it exists.} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -If you don't like the }{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs24\loch\f6 -xterm}{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - that opens when you start X11 (you don't need it since GRASS starts in the Terminal), do the following:} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -On Panther or Tiger, edit }{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs24\loch\f6 -/etc/X11/xinit/xinitrc}{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - and near the bottom add a }{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs24\loch\f6 -#}{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - to the beginning of the }{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs24\loch\f6 -xterm &}{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - line.} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -On Leopard and above, type this in a Terminal:} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs20\loch\f6 -defaults write org.x.X11_launcher app_to_run /usr/X11/bin/xlsclients} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -Something must be set for app_to_run, or X11 will not function properly, and xlsclients is regarded as safe and inconspicuous.} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -After running GRASS the first time, you can change your preference for whether the GUI or Terminal-only starts by using g.gisenv.} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs24\loch\f6 -g.gisenv set="GRASS_GUI=val"} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -Where }{\cf1\i\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -val}{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - is '}{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs24\loch\f6 -tcltk}{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -' for the GUI or '}{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs24\loch\f6 -text}{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -' for a plain Terminal.} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\i0\ulnone\ulc0\b\rtlch \ltrch\loch\fs28\loch\f5 -Addon Modules} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -To install extra modules and user scripts, see the }{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs24\loch\f6 -modbuild}{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - folder in }{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs24\loch\f6 -/Library/GRASS/$VERSION}{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -. (}{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs24\loch\f6 -$VERSION}{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - is the major.minor GRASS version.)} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\i0\ulnone\ulc0\b\rtlch \ltrch\loch\fs28\loch\f5 -R} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -Using R with GRASS.app needs a bit of simple setup. Install R for Mac OS X, at least v2.3.0. Install the GDAL R package from the GDAL framework (there are install instructions included with that), not from a repository. Remember to use a user level install location for extra packages.} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -Next, install the }{\cf1\i0\ulnone\ulc0\b\rtlch \ltrch\loch\fs28\loch\f5 -maptools}{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - package from CRAN binary repository with the R.app Package Installer.} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -Finally, use the Package Installer to install the }{\cf1\i0\ulnone\ulc0\b\rtlch \ltrch\loch\fs28\loch\f5 -spgrass6}{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - package from the CRAN source repository.} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -See the GRASS }{{\field{\*\fldinst HYPERLINK "http://grass.itc.it/statsgrass/grass_geostats.html" }{\fldrslt \cf2\ul\ulc0\langfe255\alang255\lang255\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -stats}{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -}} page for info on how to use R with GRASS.} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\i0\ulnone\ulc0\b\rtlch \ltrch\loch\fs28\loch\f5 -MPEG} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -The OGSF GRASS library can be built with mpeg support using the ffmpeg libraries, this is used only in NVIZ. ffmpeg is a difficult one to build on any platform because it is in constant flux, and none of the binaries available include the libraries. Without ffmpeg, NVIZ can only generate a series of images which can then be run through any mpeg program. This package may have ffmpeg support.} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -The last option built into GRASS, r.out.mpeg, starts with rasters and generates an mpeg1 video using an external program mpeg-encode (aka ppmtompeg). The two problems with this are: mpeg1 is low quality, and you can't use it on an NVIZ image sequence, just rasters. A copy of ppmtompeg may be included in the GRASS application package.} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -There is also a completely external option. There are many mpeg encoding programs available. Pick one of your choice and feed the NVIZ image sequence to that outside of GRASS. }{{\field{\*\fldinst HYPERLINK "http://ffmpegx.com/" }{\fldrslt \cf2\ul\ulc0\langfe255\alang255\lang255\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -ffmpegX}{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -}} is a handy package ready-made for Mac OS X. It includes commandline binaries for ffmpeg AND many other encoders, including the popular mencoder. You can either use the GUI, or run them from the Terminal - all the individual programs are in the app package's Resources folder, except a couple external ones in the ffmpegX Application Support.} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\i0\ulnone\ulc0\b\rtlch \ltrch\loch\fs28\loch\f5 -GPS} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -A copy of gpsbabel may be included for v.in.gpsbabel to use. If not, you need to install a separate GPSBabel application. Serial ports (both old-style and USB) on OS X are named a little different than most 'nix systems, and they will all go thru USB, including old RS-232 serial. You need to use the }{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs24\loch\f6 -cu.*}{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - ports. All ports are in }{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs24\loch\f6 -/dev}{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -.} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -Old RS-232 serial will have to go thru a Serial-USB converter, like the Keyspan USA-19W. These will usually have a name like }{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs24\loch\f6 -USA19QW3d1P1.1}{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -, or }{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs24\loch\f6 -/dev/cu.USA19QW3d1P1.1}{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -.} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -To figure out your GPS port, you can try the following. With the GPS (including a serial-USB adaptor, if needed for your GPS) }{\cf1\i\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -not}{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - connected, type this in a Terminal:} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs24\loch\f6 -ls /dev/cu.*} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -Then connect the GPS (with the serial-USB adaptor, if needed), and type that }{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs24\loch\f6 -ls}{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - command again. Note what was added, that should be the port to use (with }{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs24\loch\f6 -/dev/}{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - prefixed to it).} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs24\loch\f5 -\u169\'a9 2006-20}{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs24\loch\f5 -14}{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs24\loch\f5 - by the GRASS Development Team} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs24\loch\f5 -This program is free software under the GNU General Public License (>=v2).} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 - -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -- William Kyngesburye} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{{\field{\*\fldinst HYPERLINK "mailto:kyngchaos@kyngchaos.com" }{\fldrslt \cf2\ul\ulc0\langfe255\alang255\lang255\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -kyngchaos@kyngchaos.com}}} -\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\dbch\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1033\ql\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640{{\field{\*\fldinst HYPERLINK "http://www.kyngchaos.com" }{\fldrslt \cf2\ul\ulc0\langfe255\alang255\lang255\cf1\i0\ulnone\ulc0\b0\rtlch \ltrch\loch\fs28\loch\f5 -http://www.kyngchaos.com/}}} -\par } diff --git a/macosx/pkg/resources/postflight.in b/macosx/pkg/resources/postflight.in deleted file mode 100755 index 1a25c22a891..00000000000 --- a/macosx/pkg/resources/postflight.in +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/sh - -# configured - -INST_DIR="@INST_DIR@" -MACOSX_SUPPDIR="@MACOSX_SUPPDIR@" -GRASS_VER="@GRASS_VER@" - -# support folder - -mkdir -p "$MACOSX_SUPPDIR" -mkdir -p "$MACOSX_SUPPDIR/Modules/bin" -mkdir -p "$MACOSX_SUPPDIR/Modules/lib" -mkdir -p "$MACOSX_SUPPDIR/Modules/etc" -mkdir -p "$MACOSX_SUPPDIR/Modules/docs/html" - -# compile python - use python version GRASS was compiled for - -pyver_want="@PYVER@" -py=`type -p python` -if [ "$py" ] ; then - pyver=`python -V 2>&1 | grep " $pyver_want"` - if [ ! "$pyver" ] ; then - py="" - else - pyconf="python-config" - fi -fi -# try standard framework -if [ ! "$py" ] ; then - py="/Library/Frameworks/Python.framework/Versions/$pyver_want/bin/python" - if [ ! -x "$py" ] ; then - py="" - else - pyconf="/Library/Frameworks/Python.framework/Versions/$pyver_want/bin/python-config" - fi -fi -# try system (may be the same as in shell path) -if [ ! "$py" ] ; then - py="/usr/bin/python" - pyver=`$py -V 2>&1 | grep " $pyver_want"` - if [ ! "$pyver" ] ; then - py="" - else - pyconf="/usr/bin/python-config" - fi -fi -if [ "$py" ] ; then - pycompile=`$pyconf --prefix`/lib/python$pyver_want/compileall.py - if [ ! -e "$pycompile" ] ; then - pycompile=`$pyconf --prefix`/lib/python$pyver_want/compileall.pyc - fi - $py $pycompile "$INST_DIR" -fi - -# readme -# needs a better home? - -cp -f "$PACKAGE_PATH/Contents/Resources/ReadMe.rtf" "/Users/Shared/GRASS-$GRASS_VER-ReadMe.rtf" - -exit 0 diff --git a/man/build.py b/man/build.py index 9c394a8749d..9cc47446a2a 100644 --- a/man/build.py +++ b/man/build.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # utilities for generating HTML indices -# (C) 2003-2024 Markus Neteler and the GRASS Development Team +# (C) 2003-2025 Markus Neteler and the GRASS Development Team # Authors: # Markus Neteler # Glynn Clements diff --git a/man/build_graphical_index.py b/man/build_graphical_index.py index d46574af0a8..bc7dd5299d9 100755 --- a/man/build_graphical_index.py +++ b/man/build_graphical_index.py @@ -5,7 +5,7 @@ # MODULE: build_graphical_index # AUTHOR(S): Vaclav Petras # PURPOSE: Build graphical index -# COPYRIGHT: (C) 2015-2024 by Vaclav Petras and the GRASS Development Team +# COPYRIGHT: (C) 2015-2025 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 diff --git a/man/build_index.py b/man/build_index.py index ef4dbc0dcc1..b51c0807275 100644 --- a/man/build_index.py +++ b/man/build_index.py @@ -44,7 +44,6 @@ def build_index(ext): if __name__ == "__main__": - build_index("html") build_index("md") diff --git a/man/build_keywords.py b/man/build_keywords.py index 546eed62f37..63173c3f254 100644 --- a/man/build_keywords.py +++ b/man/build_keywords.py @@ -158,7 +158,7 @@ def build_keywords(ext): if ext == "html": keyword_line += f' {value.replace(f".{ext}", "")},' # noqa: E501 else: - keyword_line += f' [{value.rsplit(".", 1)[0]}]({man_file_path}),' + keyword_line += f" [{value.rsplit('.', 1)[0]}]({man_file_path})," keyword_line = keyword_line.rstrip(",") if ext == "html": keyword_line += "" diff --git a/man/build_rest.py b/man/build_rest.py index 168a5ba6127..18554ad5216 100644 --- a/man/build_rest.py +++ b/man/build_rest.py @@ -9,7 +9,7 @@ """ # utilities for generating REST indices # utilities for generating HTML indices -# (C) 2003-2024 by Luca Delucchi and the GRASS Development Team +# (C) 2003-2025 by Luca Delucchi and the GRASS Development Team import os import string @@ -171,7 +171,7 @@ -------------- :doc:`Manual main page ` \| :doc:`Full Index ` - 2003-2024 `GRASS Development Team `_, GRASS GIS ${grass_version} Reference Manual + 2003-2025 `GRASS Development Team `_, GRASS GIS ${grass_version} Reference Manual """ # noqa: E501 ) diff --git a/man/build_topics.py b/man/build_topics.py index 484f093cc3e..2ef3e8c57df 100644 --- a/man/build_topics.py +++ b/man/build_topics.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # generates topics.html and topic_*.html -# (c) 2012-2024 by the GRASS Development Team +# (c) 2012-2025 by the GRASS Development Team import os import glob diff --git a/man/sphinx/conf.py b/man/sphinx/conf.py index dbe8fb9022b..16f67074a80 100644 --- a/man/sphinx/conf.py +++ b/man/sphinx/conf.py @@ -38,7 +38,7 @@ # General information about the project. project = "GRASS GIS" -copyright = "2024, GRASS Development Team" +copyright = "2025, GRASS Development Team" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/misc/m.cogo/m.cogo.html b/misc/m.cogo/m.cogo.html index 28fcc6e4703..4135ed91ecc 100644 --- a/misc/m.cogo/m.cogo.html +++ b/misc/m.cogo/m.cogo.html @@ -80,7 +80,7 @@

      EXAMPLES

      m.cogo -l in=cogo.dat -Where the cogo.dat input file looks like: +Where the cogo.dat input file looks like:
       # Sample COGO input file -- This defines an area.
       # <label> <bearing> <distance>
      @@ -135,7 +135,7 @@ 

      EXAMPLES

      If necessary, snap the boundary closed with the v.clean module. -Use tool=snap and thresh=0.0001, or some small value. +Use tool=snap and thresh=0.0001, or some small value.

      SEE ALSO

      diff --git a/misc/m.transform/m.transform.html b/misc/m.transform/m.transform.html index e2f237cc832..b80490b39e7 100644 --- a/misc/m.transform/m.transform.html +++ b/misc/m.transform/m.transform.html @@ -5,7 +5,7 @@

      DESCRIPTION

      If coordinates are given with the input file option or fed from -stdin, both the input and the output format is "x y" with one +stdin, both the input and the output format is "x y" with one coordinate pair per line. Reverse transform is performed with the -r flag. @@ -21,44 +21,46 @@

      NOTES

      The transformations are:

      order=1:

      -    e = [E0 E1][1].[1]
      +    e = [E0 E1][1]·[1]
               [E2  0][e] [n]
       
      -    n = [N0 N1][1].[1]
      +    n = [N0 N1][1]·[1]
               [N2  0][e] [n]
       
      order=2:
           e = [E0 E1 E3][1 ] [1 ]
      -        [E2 E4  0][e ].[n ]
      +        [E2 E4  0][e ]·[n ]
               [E5  0  0][e²] [n²]
       
           n = [N0 N1 N3][1 ] [1 ]
      -        [N2 N4  0][e ].[n ]
      +        [N2 N4  0][e ]·[n ]
               [N5  0  0][e²] [n²]
       
      order=3:
           e = [E0 E1 E3 E6][1 ] [1 ]
      -        [E2 E4 E7  0][e ].[n ]
      +        [E2 E4 E7  0][e ]·[n ]
               [E5 E8  0  0][e²] [n²]
               [E9  0  0  0][e³] [n³]
       
           n = [N0 N1 N3 N6][1 ] [1 ]
      -        [N2 N4 N7  0][e ].[n ]
      +        [N2 N4 N7  0][e ]·[n ]
               [N5 N8  0  0][e²] [n²]
               [N9  0  0  0][e³] [n³]
       
      -["." = dot-product, (AE).N = N'EA.] -

      In other words, order=1 and order=2 are equivalent to order=3 with -the higher coefficients equal to zero. +["·" = dot-product, (AE)·N = N'EA] + +

      In other words, order=1 and order=2 are equivalent +to order=3 with the higher coefficients equal to zero.

      SEE ALSO

      +i.ortho.transform, i.rectify, v.rectify, v.transform diff --git a/mswindows/osgeo4w/build_osgeo4w.sh b/mswindows/osgeo4w/build_osgeo4w.sh index 98ed16aac1e..3a8c767a563 100755 --- a/mswindows/osgeo4w/build_osgeo4w.sh +++ b/mswindows/osgeo4w/build_osgeo4w.sh @@ -21,6 +21,13 @@ export C_INCLUDE_PATH=".:${OSGEO4W_ROOT_MSYS}/include:${SRC}/dist.${ARCH}/includ export PYTHONHOME=${OSGEO4W_ROOT_MSYS}/apps/Python312 export ARCH=x86_64-w64-mingw32 + +mkdir -p mswindows/osgeo4w/lib +rm -f $OSGEO4W_ROOT_MSYS/lib/libpq.a +cp -uv $OSGEO4W_ROOT_MSYS/lib/libpq.lib mswindows/osgeo4w/lib/libpq.lib + +CFLAGS="$CFLAGS -pipe" \ +CXXFLAGS="$CXXFLAGS -pipe" \ ./configure \ --bindir=${OSGEO4W_ROOT_MSYS}/bin \ --enable-largefile \ @@ -34,7 +41,7 @@ export ARCH=x86_64-w64-mingw32 --with-cairo \ --with-cairo-includes=${OSGEO4W_ROOT_MSYS}/include \ --with-cairo-ldflags="-L${SRC}/mswindows/osgeo4w/lib -lcairo" \ - --with-cairo-libs=$OSGEO4W_ROOT_MSYS/lib \ + --with-cairo-libs=${OSGEO4W_ROOT_MSYS}/lib \ --with-cxx \ --with-fftw \ --with-freetype \ @@ -44,6 +51,7 @@ export ARCH=x86_64-w64-mingw32 --with-includes=${OSGEO4W_ROOT_MSYS}/include \ --with-lapack \ --with-liblas=${SRC}/mswindows/osgeo4w/liblas-config \ + --with-libpng=${SRC}/mswindows/osgeo4w/libpng-config \ --with-libs="${OSGEO4W_ROOT_MSYS}/lib ${OSGEO4W_ROOT_MSYS}/bin" \ --with-netcdf=${OSGEO4W_ROOT_MSYS}/bin/nc-config \ --with-nls \ @@ -52,7 +60,7 @@ export ARCH=x86_64-w64-mingw32 --with-openmp \ --with-postgres \ --with-postgres-includes=${OSGEO4W_ROOT_MSYS}/include \ - --with-postgres-libs=${OSGEO4W_ROOT_MSYS}/lib \ + --with-postgres-libs=${SRC}/mswindows/osgeo4w/lib \ --with-proj-includes=${OSGEO4W_ROOT_MSYS}/include \ --with-proj-libs=${OSGEO4W_ROOT_MSYS}/lib \ --with-proj-share=${OSGEO4W_ROOT_MSYS}/share/proj \ diff --git a/mswindows/osgeo4w/env.bat.tmpl b/mswindows/osgeo4w/env.bat.tmpl index 200c86f2687..04065161a78 100644 --- a/mswindows/osgeo4w/env.bat.tmpl +++ b/mswindows/osgeo4w/env.bat.tmpl @@ -8,6 +8,8 @@ REM Uncomment if you want to use Bash instead of Cmd REM Note that msys package must be also installed REM set GRASS_SH=%OSGEO4W_ROOT%\apps\msys\bin\sh.exe +set PYTHONPATH=%OSGEO4W_ROOT%\apps\grass\grass@POSTFIX@\etc\python;%PYTHONPATH% +set GRASS_COMPATIBILITY_TEST=0 set GRASS_PYTHON=%OSGEO4W_ROOT%\bin\python3.exe set GRASS_PROJSHARE=%OSGEO4W_ROOT%\share\proj diff --git a/mswindows/osgeo4w/libpng-config b/mswindows/osgeo4w/libpng-config index f00a136be1e..e8d8be2f671 100755 --- a/mswindows/osgeo4w/libpng-config +++ b/mswindows/osgeo4w/libpng-config @@ -14,7 +14,7 @@ prefix="${OSGEO4W_ROOT_MSYS}" version="$(sed '/^#define PNG_LIBPNG_VER_STRING/!d; s/^[^"]*"\|"//g' ${prefix}/include/libpng*/png.h)" -dll_version="$(sed '/^#define PNG_LIBPNG_VER_DLLNUM/!d; s/^[^0-9]*\|[^0-9]*$//g' ${prefix}/include/libpng*/png.h)" +dll_version="$(sed '/^#define PNG_LIBPNG_VER_SHAREDLIB/!d; s/^[^0-9]*\|[^0-9]*$//g' ${prefix}/include/libpng*/png.h)" exec_prefix="${prefix}" libdir="${prefix}/lib" includedir="${prefix}/include/libpng${dll_version}" diff --git a/mswindows/osgeo4w/package.sh b/mswindows/osgeo4w/package.sh index 643d6735362..011ddec8254 100755 --- a/mswindows/osgeo4w/package.sh +++ b/mswindows/osgeo4w/package.sh @@ -1,8 +1,9 @@ -#!/usr/bin/bash +#!/bin/bash set -e -PWD="$(pwd)" +export ARCH=x86_64-w64-mingw32 +export SRC=$PWD if ! [ -d mswindows ]; then echo Start from GRASS toplevel dir @@ -106,34 +107,6 @@ fi exec 3>&1 > >(tee mswindows/osgeo4w/package.log) 2>&1 -DLLS=" - /mingw64/bin/libbrotlicommon.dll - /mingw64/bin/libbrotlidec.dll - /mingw64/bin/libbz2-1.dll - /mingw64/bin/libcairo-2.dll - /mingw64/bin/libfftw3-3.dll - /mingw64/bin/libfontconfig-1.dll - /mingw64/bin/libfreetype-6.dll - /mingw64/bin/libgcc_s_seh-1.dll - /mingw64/bin/libgfortran-5.dll - /mingw64/bin/libglib-2.0-0.dll - /mingw64/bin/libgomp-1.dll - /mingw64/bin/libgraphite2.dll - /mingw64/bin/libharfbuzz-0.dll - /mingw64/bin/libiconv-2.dll - /mingw64/bin/libintl-8.dll - /mingw64/bin/libpcre-1.dll - /mingw64/bin/libpixman-1-0.dll - /mingw64/bin/libpng16-16.dll - /mingw64/bin/libquadmath-0.dll - /mingw64/bin/libstdc++-6.dll - /mingw64/bin/libsystre-0.dll - /mingw64/bin/libtre-5.dll - /mingw64/bin/libwinpthread-1.dll - /mingw64/bin/zlib1.dll - /mingw64/bin/libopenblas.dll -" - if ! [ -f mswindows/osgeo4w/configure-stamp ]; then if [ -e include/Make/Platform.make ] ; then log make distclean @@ -143,52 +116,56 @@ if ! [ -f mswindows/osgeo4w/configure-stamp ]; then log remove old logs rm -f mswindows/osgeo4w/package.log.* - mkdir -p dist.x86_64-w64-mingw32/bin - cp -uv $DLLS dist.x86_64-w64-mingw32/bin - mkdir -p mswindows/osgeo4w/lib - cp -uv $OSGEO4W_ROOT_MSYS/lib/libpq.lib mswindows/osgeo4w/lib/pq.lib + rm -f $OSGEO4W_ROOT_MSYS/lib/libpq.a + cp -uv $OSGEO4W_ROOT_MSYS/lib/libpq.lib mswindows/osgeo4w/lib/libpq.lib cp -uv $OSGEO4W_ROOT_MSYS/lib/sqlite3_i.lib mswindows/osgeo4w/lib/sqlite3.lib + log configure + CFLAGS="$CFLAGS -pipe" \ + CXXFLAGS="$CXXFLAGS -pipe" \ ./configure \ - --bindir=$OSGEO4W_ROOT_MSYS/bin \ + --bindir=${OSGEO4W_ROOT_MSYS}/bin \ --enable-largefile \ --enable-shared \ - --host=x86_64-w64-mingw32 \ - --includedir=$OSGEO4W_ROOT_MSYS/include \ - --libexecdir=$OSGEO4W_ROOT_MSYS/bin \ - --prefix=$OSGEO4W_ROOT_MSYS/apps/grass \ + --host=${ARCH} \ + --includedir=${OSGEO4W_ROOT_MSYS}/include \ + --libexecdir=${OSGEO4W_ROOT_MSYS}/bin \ + --prefix=${OSGEO4W_ROOT_MSYS}/apps/grass \ --with-blas \ --with-bzlib \ --with-cairo \ - --with-cairo-includes=$OSGEO4W_ROOT_MSYS/include \ - --with-cairo-ldflags="-L$PWD/mswindows/osgeo4w/lib -lcairo -lfontconfig" \ + --with-cairo-includes=${OSGEO4W_ROOT_MSYS}/include \ + --with-cairo-ldflags="-L${SRC}/mswindows/osgeo4w/lib -lcairo" \ + --with-cairo-libs=${OSGEO4W_ROOT_MSYS}/lib \ --with-cxx \ --with-fftw \ --with-freetype \ - --with-freetype-includes=/mingw64/include/freetype2 \ - --with-gdal=$PWD/mswindows/osgeo4w/gdal-config \ - --with-geos=$PWD/mswindows/osgeo4w/geos-config \ - --with-includes=$OSGEO4W_ROOT_MSYS/include \ + --with-freetype-includes=${OSGEO4W_ROOT_MSYS}/include/freetype2 \ + --with-gdal=${SRC}/mswindows/osgeo4w/gdal-config \ + --with-geos=${SRC}/mswindows/osgeo4w/geos-config \ + --with-includes=${OSGEO4W_ROOT_MSYS}/include \ --with-lapack \ - --with-liblas=$PWD/mswindows/osgeo4w/liblas-config \ - --with-libs="$OSGEO4W_ROOT_MSYS/lib" \ + --with-liblas=${SRC}/mswindows/osgeo4w/liblas-config \ + --with-libpng=${SRC}/mswindows/osgeo4w/libpng-config \ + --with-libs="${OSGEO4W_ROOT_MSYS}/lib ${OSGEO4W_ROOT_MSYS}/bin" \ --with-netcdf=${OSGEO4W_ROOT_MSYS}/bin/nc-config \ --with-nls \ --with-odbc \ --with-opengl=windows \ --with-openmp \ --with-postgres \ - --with-postgres-includes=$OSGEO4W_ROOT_MSYS/include \ - --with-postgres-libs=$PWD/mswindows/osgeo4w/lib \ - --with-proj-includes=$OSGEO4W_ROOT_MSYS/include \ - --with-proj-libs=$OSGEO4W_ROOT_MSYS/lib \ - --with-proj-share=$OSGEO4W_ROOT_MSYS/share/proj \ + --with-postgres-includes=${OSGEO4W_ROOT_MSYS}/include \ + --with-postgres-libs=${SRC}/mswindows/osgeo4w/lib \ + --with-proj-includes=${OSGEO4W_ROOT_MSYS}/include \ + --with-proj-libs=${OSGEO4W_ROOT_MSYS}/lib \ + --with-proj-share=${OSGEO4W_ROOT_MSYS}/share/proj \ + --with-readline \ --with-regex \ --with-sqlite \ - --with-sqlite-includes=$OSGEO4W_ROOT_MSYS/include \ - --with-sqlite-libs=$PWD/mswindows/osgeo4w/lib \ + --with-sqlite-includes=${OSGEO4W_ROOT_MSYS}/include \ + --with-sqlite-libs=${OSGEO4W_ROOT_MSYS}/lib \ --with-zstd \ --without-pdal \ --without-x @@ -244,9 +221,11 @@ if [ -n "$PACKAGE_PATCH" ]; then unix2dos etc/postinstall/grass${PACKAGE_POSTFIX}.bat unix2dos etc/preremove/grass${PACKAGE_POSTFIX}.bat - # copy dependencies (TODO: to be reduced) - cp -uv $DLLS apps/grass/grass$POSTFIX/bin - cp -uv /mingw64/etc/fonts/fonts.conf apps/grass/grass$POSTFIX/etc + # copy dependencies + cp -uv $(/usr/bin/find apps/grass/grass$POSTFIX -iname "*.dll" -o -iname "*.exe" | PATH=$PWD/apps/grass/grass$POSTFIX/lib:$PWD/bin:/mingw64/bin:/usr/bin /usr/bin/xargs /usr/bin/ldd | /usr/bin/sed -ne 's#^.* => \(/mingw64/bin/.*\) (.*)$#\1#p' | /usr/bin/sort -u) apps/grass/grass$POSTFIX/bin + + # copy R batch files + cp -uv $SRC/mswindows/external/rbatch/* apps/grass/grass$POSTFIX/bin # creating grass package /bin/tar -cjf $PDIR/grass$PACKAGE_POSTFIX-$VERSION-$PACKAGE_PATCH.tar.bz2 \ diff --git a/ps/ps.map/ps.map.html b/ps/ps.map/ps.map.html index 8245e909c21..c4d1c5a00b6 100644 --- a/ps/ps.map/ps.map.html +++ b/ps/ps.map/ps.map.html @@ -14,7 +14,7 @@

      NOTES

      vpoints entry first. Raster maps are always drawn first, and only a single raster map (or 3 if part of a RGB group) may be used. -

      The hash character ('#') may be used at the beginning of a line +

      The hash character ('#') may be used at the beginning of a line to indicate that the line is a comment. Blank lines will also be ignored.

      Be aware that some mapping instructions require the end command @@ -29,7 +29,7 @@

      NOTES

      US-Letter sized paper at 600dpi, with 1" margins and the raster filling the entire page, the usable area on the page will be 6.5" x 9", which at 600 dots/inch is equivalent to a region of 3900 columns x 5400 rows (see -"g.region -p"). Any higher resolution settings will make the +"g.region -p"). Any higher resolution settings will make the output file larger, but with a consumer printer you probably won't be able to resolve any better detail in the hardcopy. @@ -41,11 +41,11 @@

      NOTES

      One point ("pixel") is 1/72 of an inch.

      For users wanting to use special characters (such as accented characters) it -is important to note that ps.map uses ISO-8859-1 encoding. +is important to note that ps.map uses ISO-8859-1 encoding. This means that your instructions file will have to be encoded in this encoding. If you normally work in a different encoding environment (such as -UTF-8), you have to transform your file to the ISO-8859-1 -encoding, for example by using the iconv utility: +UTF-8), you have to transform your file to the ISO-8859-1 +encoding, for example by using the iconv utility:

       iconv -f UTF-8 -t ISO_8859-1 utf_file > iso_file
      @@ -116,7 +116,7 @@ 

      Common instructions

      The name of the PostScript font. Fonts present in all PostScript implementations are: - + Times-Roman, Times-Italic, Times-Bold, @@ -129,7 +129,7 @@

      Common instructions

      Courier-Oblique, Courier-Bold, and -Courier-BoldOblique
      . +Courier-BoldOblique.
      The default is Helvetica.
      @@ -145,7 +145,7 @@

      Common instructions

      color name
      The following colors names are accepted by ps.map: - + aqua, black, blue, @@ -162,10 +162,10 @@

      Common instructions

      violet, white, yellow -
      . +.

      For vectors and some plotting commands you can also specify -'none' or 'R:G:B' (e.g '255:0:0'). +'none' or 'R:G:B' (e.g '255:0:0').
      yes|no @@ -197,7 +197,7 @@

      border

      in which case it is measured in inches. The default is a black border box of width 1 point.

      The border can be turned off completely with the -"border n" instruction. In this case +"border n" instruction. In this case the end command should not be given as the main command will be treated as a single line instruction.

      @@ -245,7 +245,7 @@

      colortable

      The default text color is black.

      Omitting the colortable instruction would result in no color table. -If the colortable is turned off with a "colortable N" +If the colortable is turned off with a "colortable N" instruction the end command should not be given as the main command will be treated as a single line instruction.

      @@ -321,7 +321,7 @@

      comments

      starting at the left margin. The default text color is black.

      If you wish to use parentheses spanning multiple lines you will need to quote them with a backslash to prevent the PostScript interpreter from -getting confused. e.g. '\(' and '\)' +getting confused. e.g. '\(' and '\)'

      This example prints in blue @@ -527,17 +527,17 @@

      header

      of the text in the text file specified, with some special formatting keys:
        -
      • %% - a literal %
      • -
      • %n - ? newline ?
      • -
      • %_ - horizontal bar
      • -
      • %c - "<raster name> in mapset <mapset name>"
      • -
      • %d - today's date
      • -
      • %l - project name
      • -
      • %L - project's text description
      • -
      • %m - mapset name
      • -
      • %u - user name
      • -
      • %x - mask info
      • -
      • %- - advance to this character column number (see example below)
      • +
      • %% - a literal %
      • +
      • %n - ? newline ?
      • +
      • %_ - horizontal bar
      • +
      • %c - "<raster name> in mapset <mapset name>"
      • +
      • %d - today's date
      • +
      • %l - project name
      • +
      • %L - project's text description
      • +
      • %m - mapset name
      • +
      • %u - user name
      • +
      • %x - mask info
      • +
      • %- - advance to this character column number (see example below)
      Example header file: @@ -556,7 +556,7 @@

      header

      This example prints (in red) whatever is in the file soils.hdr above -the map, using a 20/72 inch Courier font. +the map, using a 20/72 inch Courier font.

       EXAMPLE:
           header
      @@ -657,7 +657,7 @@ 

      mapinfo

      This example prints (in brown) the scale, grid and region information immediately below the map and starting 1.5 inches from the left edge -of the page, using a 12/72 inch Courier font. +of the page, using a 12/72 inch Courier font.

       EXAMPLE:
      @@ -1097,8 +1097,8 @@ 

      setcolor

      can be requested more than once to override the default color for additional categories. More than one category can be changed for each request by listing all the category values separated by commas (but with no spaces). Also ranges -can be included, for example "1,2,6-10,12". Colors for "null" and the -"default" (i.e. out-of-range) color may also be reassigned. +can be included, for example "1,2,6-10,12". Colors for "null" and the +"default" (i.e. out-of-range) color may also be reassigned.

      @@ -1133,7 +1133,7 @@

      text

      The user can then specify various text features:

      font: the PostScript font. Common possibilities are listed at the start of this -help page. The default is Helvetica. +help page. The default is Helvetica.

      color (see NAMED COLORS);

      width @@ -1227,7 +1227,7 @@

      vareas

      for more information on the mask)

      cats - which categories should be plotted (default is all);

      where - select features using a SQL where statement. -For example: vlastnik = 'Cimrman'; +For example: vlastnik = 'Cimrman';

      label - for description in vlegend. Default is: map(mapset);

      lpos - position vector is plotted in legend. If lpos is @@ -1242,7 +1242,7 @@

      vareas

      until overwritten in the pattern file -->. Color of the boundaries remain set by the color instruction. Pattern may be scaled with the scale command. Several standard hatching -patterns are provided in $GISBASE/etc/paint/patterns/. +patterns are provided in $GISBASE/etc/paint/patterns/. Demonstrative images can be found on the GRASS Wiki site. diff --git a/ps/ps.map/ps_vpoints.c b/ps/ps.map/ps_vpoints.c index cd320b46f11..8e00a2ad3de 100644 --- a/ps/ps.map/ps_vpoints.c +++ b/ps/ps.map/ps_vpoints.c @@ -269,5 +269,7 @@ int PS_vpoints_plot(struct Map_info *P_map, int vec) fprintf(PS.fp, "\n"); Vect_destroy_cats_struct(Cats); + Vect_destroy_line_struct(Points); + G_free(Symb); return 0; } diff --git a/pyproject.toml b/pyproject.toml index f14ec703ce2..b3bdd21a8d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,11 @@ [project] name = "grass" requires-python = ">=3.9" +# *GRASS TODO: keep in sync with MIN_PYTHON_VERSION supported* [tool.black] -required-version = '24' line-length = 88 +required-version = '25' target-version = ['py39', 'py310', 'py311', 'py312', 'py313'] # 'extend-exclude' excludes files or directories in addition to the defaults extend-exclude = ''' @@ -33,8 +34,13 @@ select = [ "C4", # flake8-comprehensions (C4) "COM", # flake8-commas (COM) "D", # pydocstyle (D) + "D202", # pydocstyle (D) blank-line-after-function + "D209", # pydocstyle (D) new-line-after-last-paragraph + "D211", # pydocstyle (D) blank-line-before-class + "D212", # pydocstyle (D) multi-line-summary-first-line "DTZ", # flake8-datetimez (DTZ) "E", # pycodestyle (E, W) + "EM", # flake8-errmsg (EM) "F", # Pyflakes (F) "FA", # flake8-future-annotations (FA) "FBT", # flake8-boolean-trap (FBT) @@ -101,19 +107,22 @@ ignore = [ "COM812", # missing-trailing-comma "COM818", # trailing-comma-on-bare-tuple "D1", - "D2", - "D300", # triple-single-quotes + "D200", # unnecessary-multiline-docstring + "D202", # blank-line-after-function (selected) + "D203", # incorrect-blank-line-before-class (ignored, use D211) + "D205", # missing-blank-line-after-summary + "D209", # new-line-after-last-paragraph (selected) + "D212", # multi-line-summary-first-line (selected) + "D213", # multi-line-summary-second-line (ignored, use D212) + "D214", # overindented-section "D301", # escape-sequence-in-docstring "D400", # ends-in-period "D401", # non-imperative-mood - "D402", # no-signature "D403", # first-line-capitalized "D404", # docstring-starts-with-this "D405", # capitalize-section-name - "D406", # new-line-after-section-name "D407", # dashed-underline-after-section "D409", # section-underline-matches-section-length - "D411", # no-blank-line-before-section "D412", # blank-lines-between-header-and-content "D413", # blank-line-after-last-section "D415", # ends-in-punctuation @@ -125,11 +134,9 @@ ignore = [ "DTZ006", # call-datetime-fromtimestamp "DTZ007", # call-datetime-strptime-without-zone "DTZ011", # call-date-today - "E402", # module-import-not-at-top-of-file "E501", # line-too-long "E721", # type-comparison "E722", # bare-except - "E731", # lambda-assignment "E741", # ambiguous-variable-name "F403", # undefined-local-with-import-star "F405", # undefined-local-with-import-star-usage @@ -147,7 +154,6 @@ ignore = [ "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 @@ -218,7 +224,6 @@ ignore = [ "S108", # hardcoded-temp-file "S110", # try-except-pass "S112", # try-except-continue - "S113", # request-without-timeout "S202", # tarfile-unsafe-members "S307", # suspicious-eval-usage "S310", # suspicious-url-open-usage @@ -238,12 +243,10 @@ ignore = [ "SIM102", # collapsible-if "SIM105", # suppressible-exception "SIM113", # enumerate-for-loop - "SIM116", # if-else-block-instead-of-dict-lookup "SIM118", # in-dict-keys "SIM223", # expr-and-false "SLF001", # private-member-access "TRY002", # raise-vanilla-class - "TRY003", # raise-vanilla-args "TRY004", # type-check-without-type-error "TRY201", # verbose-raise "TRY300", # try-consider-else @@ -260,49 +263,42 @@ ignore = [ # "A005", # builtin-module-shadowing # "PLW0108", # unnecessary-lambda # "SIM115", # open-file-with-context-handler -# Ignore `E402` (import violations) in all `__init__.py` files +# Ignored after reverting: +"gui/**" = ["PLW0108"] # See https://github.com/OSGeo/grass/issues/4124 +# Other ignores: "**.py" = ["PYI066"] "*/testsuite/**.py" = ["PT009", "PT027"] -"__init__.py" = ["E402"] -"display/d.mon/render_cmd.py" = ["SIM115"] -"gui/**" = ["PLW0108"] # See https://github.com/OSGeo/grass/issues/4124 -"gui/wxpython/animation/temporal_manager.py" = ["SIM115"] -"gui/wxpython/core/*.py" = ["SIM115"] "gui/wxpython/core/globalvar.py" = ["PTH208"] -"gui/wxpython/core/settings.py" = ["PTH208"] +"gui/wxpython/core/render.py" = ["SIM115"] +"gui/wxpython/core/settings.py" = ["PTH208", "SIM115"] +"gui/wxpython/core/utils.py" = ["SIM115"] +"gui/wxpython/core/workspace.py" = ["SIM115"] "gui/wxpython/datacatalog/catalog.py" = ["PTH208"] "gui/wxpython/dbmgr/base.py" = ["SIM115"] -"gui/wxpython/gcp/manager.py" = ["PTH208", "SIM115"] -"gui/wxpython/gmodeler/*.py" = ["SIM115"] -"gui/wxpython/gui_core/*.py" = ["SIM115"] +"gui/wxpython/gcp/manager.py" = ["PTH208"] +"gui/wxpython/gmodeler/panels.py" = ["SIM115"] "gui/wxpython/gui_core/dialogs.py" = ["PTH208"] -"gui/wxpython/iclass/frame*.py" = ["SIM115"] -"gui/wxpython/iclass/frame.py" = ["FLY002"] +"gui/wxpython/gui_core/forms.py" = ["SIM115"] +"gui/wxpython/gui_core/goutput.py" = ["SIM115"] +"gui/wxpython/gui_core/widgets.py" = ["SIM115"] +"gui/wxpython/iclass/frame.py" = ["FLY002", "SIM115"] "gui/wxpython/iclass/statistics.py" = ["A005"] "gui/wxpython/icons/grass_icons.py" = ["PTH208"] -"gui/wxpython/image2target/*.py" = ["SIM115"] -"gui/wxpython/image2target/ii2t_manager.py" = ["PTH208"] -"gui/wxpython/iscatt/plots.py" = ["PLW0108"] -"gui/wxpython/lmgr/workspace.py" = ["SIM115"] -"gui/wxpython/location_wizard/wizard.py" = ["SIM115"] +"gui/wxpython/image2target/ii2t_manager.py" = ["PTH208", "SIM115"] "gui/wxpython/mapdisp/main.py" = ["SIM115"] "gui/wxpython/modules/colorrules.py" = ["SIM115"] "gui/wxpython/modules/mcalc_builder.py" = ["SIM115"] -"gui/wxpython/photo2image/*.py" = ["SIM115"] -"gui/wxpython/psmap/*.py" = ["SIM115"] +"gui/wxpython/photo2image/ip2i_manager.py" = ["SIM115"] "gui/wxpython/psmap/dialogs.py" = ["PTH208"] +"gui/wxpython/psmap/instructions.py" = ["SIM115"] "gui/wxpython/psmap/utils.py" = ["PGH004"] -"gui/wxpython/rdigit/controller.py" = ["SIM115"] -"gui/wxpython/rlisetup/*.py" = ["SIM115"] "gui/wxpython/rlisetup/frame.py" = ["PTH208"] "gui/wxpython/timeline/frame.py" = ["FLY002"] "gui/wxpython/tools/update_menudata.py" = ["SIM115"] "gui/wxpython/tplot/frame.py" = ["FLY002"] -"gui/wxpython/vdigit/mapwindow.py" = ["SIM115"] -"gui/wxpython/vnet/*.py" = ["SIM115"] +"gui/wxpython/vnet/vnet_data.py" = ["SIM115"] "gui/wxpython/web_services/dialogs.py" = ["SIM115"] -"gui/wxpython/wxplot/profile*.py" = ["SIM115"] -"gui/wxpython/wxplot/profile.py" = ["A005"] +"gui/wxpython/wxplot/profile.py" = ["A005", "SIM115"] "imagery/i.atcorr/create_iwave.py" = ["SIM115"] "lib/imagery/testsuite/test_imagery_signature_management.py" = ["SIM115"] "lib/imagery/testsuite/test_imagery_sigsetfile.py" = ["FURB152"] @@ -321,7 +317,6 @@ ignore = [ "python/grass/gunittest/multireport.py" = ["PYI024"] "python/grass/gunittest/testsu*/d*/s*/s*/subsub*/t*/test_segfaut.py" = ["B018"] "python/grass/gunittest/testsuite/test_assertions_rast3d.py" = ["FLY002"] -"python/grass/imaging/images2*.py" = ["SIM115"] "python/grass/imaging/images2ims.py" = ["PTH208"] "python/grass/jupyter/testsuite/interactivemap_test.py" = ["PGH004"] "python/grass/jupyter/testsuite/map_test.py" = ["PGH004"] @@ -339,14 +334,14 @@ ignore = [ "python/grass/pygrass/vector/sql.py" = ["FLY002"] "python/grass/pygrass/vector/testsuite/test_table.py" = ["PLW0108"] "python/grass/script/array.py" = ["A005"] -"python/grass/script/core.py" = ["PTH208", "SIM115"] -"python/grass/script/db.py" = ["SIM115"] -"python/grass/script/raster.py" = ["SIM115"] -"python/grass/script/utils.py" = ["FURB189", "SIM115"] +"python/grass/script/core.py" = ["PTH208"] +"python/grass/script/utils.py" = ["FURB189"] "python/grass/temporal/aggregation.py" = ["SIM115"] "python/grass/temporal/register.py" = ["SIM115"] "python/grass/temporal/stds_export.py" = ["SIM115"] "python/grass/temporal/stds_import.py" = ["SIM115"] +"python/grass/temporal/temporal_algebra.py" = ["D300"] +"python/grass/temporal/temporal_operator.py" = ["D300"] "python/grass/temporal/univar_statistics.py" = ["SIM115"] "python/grass/utils/download.py" = ["PTH208", "SIM115"] "raster/r.*/testsuite/*.py" = ["SIM115"] @@ -385,19 +380,17 @@ ignore = [ "temporal/t.register/testsuite/test_t_register_raster.py" = ["FLY002"] "temporal/t.register/testsuite/test_t_register_raster_file.py" = ["FLY002"] "temporal/t.remove/t.remove.py" = ["SIM115"] -"temporal/t.unregister/t.unregister.py" = ["SIM115"] -"utils/**.py" = ["SIM115"] "utils/generate_release_notes.py" = ["PGH004"] +"utils/gitlog2changelog.py" = ["SIM115"] "utils/thumbnails.py" = ["PTH208"] "vector/v.fill.holes/examples.ipynb" = ["PTH201"] [tool.ruff.lint.flake8-import-conventions.extend-aliases] # Declare a custom aliases, checked with rule ICN001 "grass.script" = "gs" +"grass.temporal" = "tgis" [tool.pytest.ini_options] -minversion = "6.0" -python_files = "*/tests/*_test.py" addopts = """ --ignore-glob='dist.*' --ignore-glob='bin.*' @@ -406,11 +399,13 @@ addopts = """ --doctest-glob='*doctest*.txt' --ignore='raster/r.category/test_rcategory_doctest.txt' """ -timeout = 300 markers = [ "slow: marks tests as slow (deselect with '-m \"not slow\"')", "needs_solo_run: marks tests that must be run without any other tests running in parallel", ] +minversion = "6.0" +python_files = "*/tests/*_test.py */tests/test_*.py" +timeout = 300 [tool.bandit] @@ -421,3 +416,337 @@ exclude_dirs = [ "utils/test_generate_last_commit_file.py", ] skips = ["B324", "B110", "B101", "B112", "B311", "B404", "B603"] + + +[tool.pylint.main] + +# Files or directories to be skipped. They should be base names, not paths. +ignore = ["CVS", ".git", ".ruff_cache", ".pytest_cache"] + +# Add files or directories matching the regular expressions patterns to the +# ignore-list. The regex matches against paths and can be in Posix or Windows +# format. Because '\\' represents the directory delimiter on Windows systems, it +# can't be used as an escape character. +ignore-paths = [ + "bin[.].*", + "dist[.].*", + "gui/wxpython/menustrings.py", + "python/libgrass_interface_generator", +] + +# Files or directories matching the regular expression patterns are skipped. The +# regex matches against base names, not paths. The default value ignores Emacs +# file locks +ignore-patterns = ["^\\.#", "OBJ[.].*", "Makefile"] + + +# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the +# number of processors available to use, and will cap the count on Windows to +# avoid hangs. +jobs = 0 + +# Control the amount of potential inferred values when inferring a single object. +# This can help the performance when dealing with large functions or complex, +# nested conditions. +# *GRASS modified: default 'limit-inference-results = 100'* +limit-inference-results = 10 + +# List of plugins (as comma separated values of python module names) to load, +# usually to register additional checkers. +load-plugins = [ + "pylint.extensions.bad_builtin", + "pylint.extensions.no_self_use", + "pylint.extensions.redefined_variable_type", +] + + +# Minimum Python version to use for version dependent checks. Will default to the +# version used to run pylint. +# *GRASS modified* +# *GRASS TODO: keep in sync with MIN_PYTHON_VERSION supported* +py-version = "3.9" + +# Add paths to the list of the source roots. Supports globbing patterns. The +# source root is an absolute path or a path relative to the current working +# directory used to determine a package namespace for modules located under the +# source root. +source-roots = ["python", "gui/wxpython"] + + +[tool.pylint.design] +# List of regular expressions of class ancestor names to ignore when counting +# public methods (see R0903) +# exclude-too-few-public-methods = + +# List of qualified class names to ignore when counting class parents (see R0901) +# ignored-parents = + +# Maximum number of arguments for function / method. +# *GRASS modified: default 5, (see R0913)* +# *GRASS TODO: Try to reduce this value* +max-args = 15 + +# Maximum number of attributes for a class (see R0902). +# *GRASS modified: default 7* +# *GRASS TODO: Try to reduce this value* +max-attributes = 25 + +# Maximum number of boolean expressions in an if statement (see R0916). +# *GRASS modified: default 5* +# *GRASS TODO: Try to reduce this value* +max-bool-expr = 8 + +# Maximum number of branch for function / method body. +# *GRASS modified: default 12, (see R0912)* +# *GRASS TODO: Try to reduce this value* +max-branches = 44 + +# Maximum number of locals for function / method body. +# *GRASS modified: default 15, (see R0914)* +# *GRASS TODO: Try to reduce this value* +max-locals = 50 + +# # Maximum number of parents for a class (see R0901). +# max-parents = 7 + +# Maximum number of positional arguments for function / method. +# *GRASS modified: default 5, (see R0917)* +# *GRASS TODO: Try to reduce this value* +max-positional-arguments = 7 + +# Maximum number of public methods for a class (see R0904). +# max-public-methods = 20 + +# Maximum number of return / yield for function / method body. +# *GRASS modified: default 6, (see R0911)* +# *GRASS TODO: Try to reduce this value* +max-returns = 9 + +# Maximum number of statements in function / method body. +# *GRASS modified: default 50, (see R0915)* +# *GRASS TODO: Try to reduce this value* +max-statements = 220 + +# Minimum number of public methods for a class (see R0903). +# *GRASS modified: default 2, (see R0915)* +# *GRASS TODO: Try to increase this value* +min-public-methods = 1 + + +[tool.pylint.format] + +# Maximum number of lines in a module. +# *GRASS modified: default 1000, (see C0302)* +# *GRASS TODO: Try to reduce this value* +max-module-lines = 2000 + + +[tool.pylint."messages control"] +# Disable the message, report, category or checker with the given id(s). You can +# either give multiple identifiers separated by comma (,) or put this option +# multiple times (only on the command line, not in the configuration file where +# it should appear only once). You can also use "--disable=all" to disable +# everything first and then re-enable specific checks. For example, if you want +# to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use "--disable=all --enable=classes +# --disable=W". +disable = [ + # *GRASS: The 10 following rules are disabled by default* + "bad-inline-option", + "deprecated-pragma", + "file-ignored", + "locally-disabled", + "raw-checker-failed", + "suppressed-message", + "use-implicit-booleaness-not-comparison-to-string", + "use-implicit-booleaness-not-comparison-to-zero", + "use-symbolic-message-instead", + "useless-suppression", + # *GRASS modified: The following rules are added to the disabled list* + # *GRASS TODO: Try to ignore less rules* + "C0103", # Constant name "%s" doesn't conform to UPPER_CASE naming style (invalid-name) + "C0104", # Disallowed name "foo" (disallowed-name) + "C0112", # Empty %s docstring (empty-docstring) + "C0114", # Missing module docstring (missing-module-docstring) + "C0115", # Missing class docstring (missing-class-docstring) + "C0116", # Missing function or method docstring (missing-function-docstring) + "C0200", # Consider using enumerate instead of iterating with range and len (consider-using-enumerate) + "C0201", # Consider iterating the dictionary directly instead of calling .keys() (consider-iterating-dictionary) + "C0204", # Metaclass class method %s should have %s as first argument (bad-mcs-classmethod-argument) + "C0206", # Consider iterating with .items() (consider-using-dict-items) + "C0207", # Use str(self.start).split(' ', maxsplit=1)[0] instead (use-maxsplit-arg) + "C0209", # Formatting a regular string which could be an f-string (consider-using-f-string) + "C0301", # Line too long (line-too-long) + "C0302", # Too many lines in module (%s/%s) (too-many-lines) + "C0325", # Unnecessary parens after %r keyword (superfluous-parens) + "C0411", # Standard library/third-party library import should be placed before local imports (wrong-import-order) + "C0412", # Imports from package %s are not grouped (ungrouped-imports) + "C0413", # Import "%s" should be placed at the top of the module (wrong-import-position) + "C0415", # (import-outside-toplevel) + "C1802", # Do not use `len(SEQUENCE)` without comparison to determine if a sequence is empty (use-implicit-booleaness-not-len) + "C1803", # "%s" can be simplified to "%s", if it is strictly a sequence, as an empty %s is falsey (use-implicit-booleaness-not-comparison) + "C2801", # Unnecessarily calls dunder method %s. %s. (unnecessary-dunder-call) + "E0102", # %s already defined line %s (function-redefined) + "E0203", # Access to member %r before its definition line %s (access-member-before-definition) + "E0213", # Method %r should have "self" as first argument (no-self-argument) + "E0401", # Unable to import %r (import-error) + "E0601", # Using variable %r before assignment (used-before-assignment) + "E0602", # Undefined variable %r (undefined-variable) + "E0606", # Possibly using variable %r before assignment (possibly-used-before-assignment) + "E0611", # No name %r in module %r (no-name-in-module) + "E0633", # Attempting to unpack a non-sequence%s (unpacking-non-sequence) + "E0704", # The raise statement is not inside an except clause (misplaced-bare-raise) + "E1003", # Bad first argument %r given to super() (bad-super-call) + "E1101", # Instance of %r has no %r member (no-member) + "E1102", # %s is not callable (not-callable) + "E1111", # Assigning result of a function call, where the function has no return (assignment-from-no-return) + "E1120", # No value for argument %r in constructor call (no-value-for-parameter) + "E1121", # (too-many-function-args) + "E1123", # Unexpected keyword argument %r in %s call (unexpected-keyword-arg) + "E1124", # Argument %r passed by position and keyword in %s call (redundant-keyword-arg) + "E1126", # Sequence index is not an int, slice, or instance with __index__ (invalid-sequence-index) + "E1128", # Assigning result of a function call, where the function returns None (assignment-from-none) + "E1133", # Non-iterable value %s is used in an iterating context (not-an-iterable) + "E1136", # Value '%s' is unsubscriptable (unsubscriptable-object) + "E1137", # %r does not support item assignment (unsupported-assignment-operation) + "E1307", # Argument %r does not match format type %r (bad-string-format-type) + "R0204", # Redefinition of %s type from %s to %s (redefined-variable-type) + "R0401", # Cyclic import (%s) (cyclic-import) + "R0801", # Similar lines in %s files %s (duplicate-code) + "R0901", # (too-many-ancestors) + "R0902", # (too-many-instance-attributes) + "R0903", # (too-few-public-methods) + "R0904", # (too-many-public-methods) + "R0911", # (too-many-return-statements) + "R0912", # (too-many-branches) + "R0913", # (too-many-arguments) + "R0914", # (too-many-locals) + "R0915", # (too-many-statements) + "R0916", # (too-many-boolean-expressions) + "R0917", # (too-many-positional-arguments) + "R1702", # (too-many-nested-blocks) + "R1704", # Redefining argument with the local name %r (redefined-argument-from-local) + "R1705", # Unnecessary "else" after "return", remove the "else" and de-indent the code inside it (no-else-return) + "R1710", # Either all return statements in a function should return an expression, or none of them should. (inconsistent-return-statements) + "R1712", # Consider using tuple unpacking for swapping variables (consider-swap-variables) + "R1713", # Consider using str.join(sequence) for concatenating strings from an iterable (consider-using-join) + "R1714", # Consider merging these comparisons with 'in' by using '%s %sin (%s)'. Use a set instead if elements are hashable. (consider-using-in) + "R1715", # Consider using dict.get for getting values from a dict if a key is present or a default if not (consider-using-get) + "R1724", # Unnecessary "%s" after "continue", %s (no-else-continue) + "R1727", # Boolean condition '%s' will always evaluate to '%s' (condition-evals-to-constant) + "R1732", # Consider using 'with' for resource-allocating operations (consider-using-with) + "R1733", # Unnecessary dictionary index lookup, use '%s' instead (unnecessary-dict-index-lookup) + "R1735", # Consider using '{**item}' instead of a call to 'dict'. (use-dict-literal) + "R6301", # Method could be a function (no-self-use) + "RP0401", # Report: Imports checker: External dependencies + "RP0801", # Report: Similarities: Duplication + "W0101", # Unreachable code (unreachable) + "W0102", # Dangerous default value %s as argument (dangerous-default-value) + "W0104", # Statement seems to have no effect (pointless-statement) + "W0106", # Expression "%s" is assigned to nothing (expression-not-assigned) + "W0108", # Lambda may not be necessary (unnecessary-lambda) + "W0123", # Use of eval (eval-used) + "W0125", # Using a conditional statement with a constant value (using-constant-test) + "W0133", # Exception statement has no effect (pointless-exception-statement) + "W0141", # Used builtin function %s (bad-builtin) + "W0201", # Attribute %r defined outside __init__(attribute-defined-outside-init) + "W0212", # Access to a protected member %s of a client class (protected-access) + "W0221", # (arguments-differ) + "W0222", # Signature differs from %s %r method (signature-differs) + "W0223", # Method %r is abstract in class %r but is not overridden in child class %r (abstract-method) + "W0237", # (arguments-renamed) + "W0401", # Wildcard import %s (wildcard-import) + "W0404", # Reimport %r (imported line %s) (reimported) + "W0511", # (fixme) + "W0601", # Global variable %r undefined at the module level (global-variable-undefined) + "W0602", # Using global for %r but no assignment is done (global-variable-not-assigned) + "W0603", # Using the global statement (global-statement) + "W0604", # Using the global statement at the module level (global-at-module-level) + "W0611", # (unused-import) + "W0612", # Unused variable %r (unused-variable) + "W0613", # Unused argument %r (unused-argument) + "W0614", # Unused import(s) %s from wildcard import of %s (unused-wildcard-import) + "W0621", # Redefining name %r from outer scope (line %s) (redefined-outer-name) + "W0621", # Redefining name %r from outer scope (line %s) (redefined-outer-name) + "W0622", # Redefining built-in %r (redefined-builtin) + "W0631", # Using possibly undefined loop variable %r (undefined-loop-variable) + "W0632", # Possible unbalanced tuple unpacking with sequence %s: left side has %d label%s, right side has %d value%s (unbalanced-tuple-unpacking) + "W0640", # Cell variable %s defined in loop (cell-var-from-loop) + "W0641", # Possibly unused variable %r (possibly-unused-variable) + "W0702", # No exception type(s) specified (bare-except) + "W0707", # Consider explicitly re-raising using %s'%s from %s' (raise-missing-from) + "W0716", # Invalid exception operation. %s (wrong-exception-operation) + "W0718", # Catching too general exception Exception (broad-exception-caught) + "W0719", # Raising too general exception: %s (broad-exception-raised) + "W1113", # Keyword argument before variable positional arguments list in the definition of %s function (keyword-arg-before-vararg) + "W1114", # Positional arguments appear to be out of order (arguments-out-of-order) + "W1308", # Duplicate string formatting argument %r, consider passing as named argument (duplicate-string-formatting-argument) + "W1503", # Redundant use of %s with constant value %r (redundant-unittest-assert) + "W1508", # %s default type is %s. Expected str or None. (invalid-envvar-default) + "W1510", # 'subprocess.run' used without explicitly defining the value for 'check'. (subprocess-run-check) + "W1514", # Using open without explicitly specifying an encoding (unspecified-encoding) + "W4701", # Iterated list '%s' is being modified inside for loop body, consider iterating through a copy of it instead. (modified-iterating-list) + "W4901", # Deprecated module %r (deprecated-module) + "W4902", # Using deprecated method %s (deprecated-method) + # "RP0402", # Report: Imports checker: Modules dependencies graph + + "similarities", # too long to run +] + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where it +# should appear only once). See also the "--disable" option for examples. +enable = ["deprecated-pragma"] + + +[tool.pylint.refactoring] +# Maximum number of nested blocks for function / method body +# *GRASS modified: default 5, (see R1702)* +# *GRASS TODO: Try to reduce this value* +max-nested-blocks = 7 + + +[tool.pylint.reports] +# Set the output format. Available formats are: text, parseable, colorized, json2 +# (improved json format), json (old json format) and msvs (visual studio). You +# can also give a reporter class, e.g. mypackage.mymodule.MyReporterClass. +output-format = "colorized" + +# Tells whether to display a full report or only the messages. +reports = true + +[tool.pylint.similarities] +# Minimum lines number of a similarity. +# *GRASS modified: default 4, (see R0801)* +# *GRASS TODO: Try to reduce this value* +# *GRASS justification: Matching only larger chunks of code, not the default 4 lines.* +min-similarity-lines = 50 + +[tool.pylint.variables] +# List of additional names supposed to be defined in builtins. Remember that you +# should avoid defining new builtins when possible. +# *GRASS modified: default '# additional-builtins ='* +# *GRASS justification: Translation function is (unfortunately) defined as a builtin.* +# *GRASS TODO: Find another pattern for translation that allows to use _ as a discard variable and other* +additional-builtins = ["_"] + + +[tool.pyright] +exclude = ["**/__pycache__", "**/OBJ.*", "bin.*/**", "dist.*/**"] +include = ["python"] + +# Set extraPath to the output of $(grass --config python_path) + +# *GRASS modified* +# *GRASS TODO: keep in sync with MIN_PYTHON_VERSION supported* +pythonVersion = "3.9" + +[tool.pyrefact] +line_length = 88 + +[tool.isort] +profile = "black" +# *GRASS TODO: keep in sync with MIN_PYTHON_VERSION supported* +py_version = 39 diff --git a/python/.pylintrc b/python/.pylintrc deleted file mode 100644 index 0df20a6e3ba..00000000000 --- a/python/.pylintrc +++ /dev/null @@ -1,608 +0,0 @@ -[MASTER] - -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code. -extension-pkg-allow-list= - -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code. (This is an alternative name to extension-pkg-allow-list -# for backward compatibility.) -extension-pkg-whitelist= - -# Return non-zero exit code if any of these messages/categories are detected, -# even if score is above --fail-under value. Syntax same as enable. Messages -# specified are enabled, while categories only check already-enabled messages. -fail-on= - -# Specify a score threshold to be exceeded before program exits with error. -fail-under=10.0 - -# Files or directories to be skipped. They should be base names, not paths. -ignore=CVS - -# Add files or directories matching the regex patterns to the ignore-list. The -# regex matches against paths and can be in Posix or Windows format. -ignore-paths=grass/temporal/.*, - grass/script/.*, - grass/gunittest/.*, - grass/pydispatch/.*, - grass/pygrass/.*, - grass/imaging/.*, - grass/semantic_label/.*, - grass/grassdb/.*, - grass/utils/.*, - grass/exceptions/.*, - grass/app/.*, - - -# Files or directories matching the regex patterns are skipped. The regex -# matches against base names, not paths. -ignore-patterns= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -#init-hook= - -# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the -# number of processors available to use. -jobs=1 - -# Control the amount of potential inferred values when inferring a single -# object. This can help the performance when dealing with large functions or -# complex, nested conditions. -limit-inference-results=100 - -# List of plugins (as comma separated values of python module names) to load, -# usually to register additional checkers. -load-plugins= - -# Pickle collected data for later comparisons. -persistent=yes - -# Minimum Python version to use for version dependent checks. Will default to -# the version used to run pylint. -py-version=3.8 - -# When enabled, pylint would attempt to guess common misconfiguration and emit -# user-friendly hints instead of false-positive error messages. -suggestion-mode=yes - -# Allow loading of arbitrary C extensions. Extensions are imported into the -# active Python interpreter and may run arbitrary code. -unsafe-load-any-extension=no - - -[MESSAGES CONTROL] - -# Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. -confidence= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once). You can also use "--disable=all" to -# disable everything first and then re-enable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use "--disable=all --enable=classes -# --disable=W". -disable=raw-checker-failed, - bad-inline-option, - locally-disabled, - file-ignored, - suppressed-message, - deprecated-pragma, - use-symbolic-message-instead, - duplicate-code - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time (only on the command line, not in the configuration file where -# it should appear only once). See also the "--disable" option for examples. -enable=c-extension-no-member - - -[REPORTS] - -# Python expression which should return a score less than or equal to 10. You -# have access to the variables 'error', 'warning', 'refactor', and 'convention' -# which contain the number of messages in each category, as well as 'statement' -# which is the total number of statements analyzed. This score is used by the -# global evaluation report (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details. -#msg-template= - -# Set the output format. Available formats are text, parseable, colorized, json -# and msvs (visual studio). You can also give a reporter class, e.g. -# mypackage.mymodule.MyReporterClass. -output-format=text - -# Tells whether to display a full report or only the messages. -reports=no - -# Activate the evaluation score. -score=yes - - -[REFACTORING] - -# Maximum number of nested blocks for function / method body -max-nested-blocks=5 - -# Complete name of functions that never returns. When checking for -# inconsistent-return-statements if a never returning function is called then -# it will be considered as an explicit return statement and no message will be -# printed. -never-returning-functions=sys.exit,argparse.parse_error - - -[SPELLING] - -# Limits count of emitted suggestions for spelling mistakes. -max-spelling-suggestions=4 - -# Spelling dictionary name. Available dictionaries: fr_MC (myspell), fr_CA -# (myspell), fr_BE (myspell), fr_LU (myspell), fr_CH (myspell), fr_FR -# (myspell), ar (myspell), es_CR (myspell), de_CH_frami (myspell), es_EC -# (myspell), ar_YE (myspell), en_CA (myspell), ar_BH (myspell), ar_IN -# (myspell), ar_TN (myspell), en_ZA (myspell), de_DE_frami (myspell), ar_SY -# (myspell), ar_IQ (myspell), ar_LB (myspell), ar_KW (myspell), ru_RU -# (myspell), es_BO (myspell), en_GB (myspell), ar_SD (myspell), de_DE -# (myspell), es_CU (myspell), es_PA (myspell), ar_EG (myspell), es_HN -# (myspell), de_CH (myspell), es_NI (myspell), es_AR (myspell), es_ES -# (myspell), ar_SA (myspell), es_VE (myspell), de_AT_frami (myspell), it_IT -# (myspell), ar_OM (myspell), ar_DZ (myspell), it_CH (myspell), es_MX -# (myspell), es_PY (myspell), en_AU (myspell), es_DO (myspell), es_SV -# (myspell), es_PR (myspell), es_GT (myspell), ar_LY (myspell), ar_JO -# (myspell), en_US (myspell), de_AT (myspell), es_PE (myspell), ar_QA -# (myspell), es_CL (myspell), pt_BR (myspell), ar_AE (myspell), pt_PT -# (myspell), es_CO (myspell), es_UY (myspell), ar_MA (myspell), fr (myspell), -# es_US (myspell), en (aspell). -spelling-dict= - -# List of comma separated words that should be considered directives if they -# appear and the beginning of a comment and should not be checked. -spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy: - -# List of comma separated words that should not be checked. -spelling-ignore-words= - -# A path to a file that contains the private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to the private dictionary (see the -# --spelling-private-dict-file option) instead of raising a message. -spelling-store-unknown-words=no - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=FIXME, - XXX, - TODO - -# Regular expression of note tags to take in consideration. -#notes-rgx= - - -[STRING] - -# This flag controls whether inconsistent-quotes generates a warning when the -# character used as a quote delimiter is used inconsistently within a module. -check-quote-consistency=no - -# This flag controls whether the implicit-str-concat should generate a warning -# on implicit string concatenation in sequences defined over several lines. -check-str-concat-over-line-jumps=no - - -[TYPECHECK] - -# List of decorators that produce context managers, such as -# contextlib.contextmanager. Add to this list to register other decorators that -# produce valid context managers. -contextmanager-decorators=contextlib.contextmanager - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E1101 when accessed. Python regular -# expressions are accepted. -generated-members= - -# Tells whether missing members accessed in mixin class should be ignored. A -# class is considered mixin if its name matches the mixin-class-rgx option. -ignore-mixin-members=yes - -# Tells whether to warn about missing members when the owner of the attribute -# is inferred to be None. -ignore-none=yes - -# This flag controls whether pylint should warn about no-member and similar -# checks whenever an opaque object is returned when inferring. The inference -# can return multiple potential results while evaluating a Python object, but -# some branches might not be evaluated, which results in partial inference. In -# that case, it might be useful to still emit no-member and other checks for -# the rest of the inferred objects. -ignore-on-opaque-inference=yes - -# List of class names for which member attributes should not be checked (useful -# for classes with dynamically set attributes). This supports the use of -# qualified names. -ignored-classes=optparse.Values,thread._local,_thread._local - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis). It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules= - -# Show a hint with possible names when a member name was not found. The aspect -# of finding the hint is based on edit distance. -missing-member-hint=yes - -# The minimum edit distance a name should have in order to be considered a -# similar match for a missing member name. -missing-member-hint-distance=1 - -# The total number of similar names that should be taken in consideration when -# showing a hint for a missing member. -missing-member-max-choices=1 - -# Regex pattern to define which classes are considered mixins ignore-mixin- -# members is set to 'yes' -mixin-class-rgx=.*[Mm]ixin - -# List of decorators that change the signature of a decorated function. -signature-mutators= - - -[VARIABLES] - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid defining new builtins when possible. -# Translation function is (unfortunately) defined as a buildin. -additional-builtins=_ - -# Tells whether unused global variables should be treated as a violation. -allow-global-unused-variables=yes - -# List of names allowed to shadow builtins -allowed-redefined-builtins= - -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_, - _cb - -# A regular expression matching the name of dummy variables (i.e. expected to -# not be used). -# On top of the defaults, simple unused is also permissible. -dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore. -ignored-argument-names=_.*|^ignored_|^unused_ - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# List of qualified module names which can have objects that can redefine -# builtins. -redefining-builtins-modules=past.builtins,future.builtins,builtins,io - - -[SIMILARITIES] - -# Comments are removed from the similarity computation -ignore-comments=yes - -# Docstrings are removed from the similarity computation -ignore-docstrings=yes - -# Imports are removed from the similarity computation -ignore-imports=no - -# Signatures are removed from the similarity computation -ignore-signatures=no - -# Minimum lines number of a similarity. -# Matching only larger chunks of code, not the default 4 lines. -min-similarity-lines=10 - - -[LOGGING] - -# The type of string formatting that logging methods do. `old` means using % -# formatting, `new` is for `{}` formatting. -logging-format-style=old - -# Logging modules to check that the string format arguments are in logging -# function parameter format. -logging-modules=logging - - -[BASIC] - -# Naming style matching correct argument names. -argument-naming-style=snake_case - -# Regular expression matching correct argument names. Overrides argument- -# naming-style. -#argument-rgx= - -# Naming style matching correct attribute names. -attr-naming-style=snake_case - -# Regular expression matching correct attribute names. Overrides attr-naming- -# style. -#attr-rgx= - -# Bad variable names which should always be refused, separated by a comma. -bad-names=foo, - bar, - baz, - toto, - tutu, - tata - -# Bad variable names regexes, separated by a comma. If names match any regex, -# they will always be refused -bad-names-rgxs= - -# Naming style matching correct class attribute names. -class-attribute-naming-style=any - -# Regular expression matching correct class attribute names. Overrides class- -# attribute-naming-style. -#class-attribute-rgx= - -# Naming style matching correct class constant names. -class-const-naming-style=UPPER_CASE - -# Regular expression matching correct class constant names. Overrides class- -# const-naming-style. -#class-const-rgx= - -# Naming style matching correct class names. -class-naming-style=PascalCase - -# Regular expression matching correct class names. Overrides class-naming- -# style. -#class-rgx= - -# Naming style matching correct constant names. -const-naming-style=UPPER_CASE - -# Regular expression matching correct constant names. Overrides const-naming- -# style. -#const-rgx= - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=-1 - -# Naming style matching correct function names. -function-naming-style=snake_case - -# Regular expression matching correct function names. Overrides function- -# naming-style. -#function-rgx= - -# Good variable names which should always be accepted, separated by a comma. -good-names=i, - j, - k, - x, - y, - z, - ex, - Run, - _ - -# Good variable names regexes, separated by a comma. If names match any regex, -# they will always be accepted -good-names-rgxs= - -# Include a hint for the correct naming format with invalid-name. -include-naming-hint=no - -# Naming style matching correct inline iteration names. -inlinevar-naming-style=any - -# Regular expression matching correct inline iteration names. Overrides -# inlinevar-naming-style. -#inlinevar-rgx= - -# Naming style matching correct method names. -method-naming-style=snake_case - -# Regular expression matching correct method names. Overrides method-naming- -# style. -#method-rgx= - -# Naming style matching correct module names. -module-naming-style=snake_case - -# Regular expression matching correct module names. Overrides module-naming- -# style. -#module-rgx= - -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= - -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=^_ - -# List of decorators that produce properties, such as abc.abstractproperty. Add -# to this list to register other decorators that produce valid properties. -# These decorators are taken in consideration only for invalid-name. -property-classes=abc.abstractproperty - -# Naming style matching correct variable names. -variable-naming-style=snake_case - -# Regular expression matching correct variable names. Overrides variable- -# naming-style. -#variable-rgx= - - -[FORMAT] - -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -expected-line-ending-format= - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=^\s*(# )??$ - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - -# Maximum number of characters on a single line. -max-line-length=100 - -# Maximum number of lines in a module. -max-module-lines=1000 - -# Allow the body of a class to be on the same line as the declaration if body -# contains single statement. -single-line-class-stmt=no - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=no - - -[IMPORTS] - -# List of modules that can be imported at any level, not just the top level -# one. -allow-any-import-level= - -# Allow wildcard imports from modules that define __all__. -allow-wildcard-with-all=no - -# Analyse import fallback blocks. This can be used to support both Python 2 and -# 3 compatible code, which means that the block might have code that exists -# only in one or another interpreter, leading to false positives when analysed. -analyse-fallback-blocks=no - -# Deprecated modules which should not be used, separated by a comma. -deprecated-modules= - -# Output a graph (.gv or any supported image format) of external dependencies -# to the given file (report RP0402 must not be disabled). -ext-import-graph= - -# Output a graph (.gv or any supported image format) of all (i.e. internal and -# external) dependencies to the given file (report RP0402 must not be -# disabled). -import-graph= - -# Output a graph (.gv or any supported image format) of internal dependencies -# to the given file (report RP0402 must not be disabled). -int-import-graph= - -# Force import order to recognize a module as part of the standard -# compatibility libraries. -known-standard-library= - -# Force import order to recognize a module as part of a third party library. -known-third-party=enchant - -# Couples of modules and preferred modules, separated by a comma. -preferred-modules= - - -[CLASSES] - -# Warn about protected attribute access inside special methods -check-protected-access-in-special-methods=no - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__, - __new__, - setUp, - __post_init__ - -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict, - _fields, - _replace, - _source, - _make - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=cls - - -[DESIGN] - -# List of regular expressions of class ancestor names to ignore when counting -# public methods (see R0903) -exclude-too-few-public-methods= - -# List of qualified class names to ignore when counting class parents (see -# R0901) -ignored-parents= - -# Maximum number of arguments for function / method. -# We tend to have function with more arguments than the default 5 -# and that doesn't seem to be the problem of our code. -max-args=12 - -# Maximum number of attributes for a class (see R0902). -# We tend to have classes with more attributes than the default 7. -max-attributes=15 - -# Maximum number of boolean expressions in an if statement (see R0916). -max-bool-expr=5 - -# Maximum number of branch for function / method body. -# We add 3 more to the default to make it easier to adopt the check quickly. -# (Many branches may or may not be an issue, but in any case this is potential -# place to improve.) -max-branches=15 - -# Maximum number of locals for function / method body. -# Since we allow many more arguments, we leave some space for locals as well, -# although the default 15 should be high enough. -max-locals=17 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=20 - -# Maximum number of return / yield for function / method body. -max-returns=6 - -# Maximum number of statements in function / method body. -max-statements=50 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=1 - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "BaseException, Exception". -overgeneral-exceptions=BaseException, - Exception diff --git a/python/grass/app/runtime.py b/python/grass/app/runtime.py index 27f35760b8f..d135ed6987d 100644 --- a/python/grass/app/runtime.py +++ b/python/grass/app/runtime.py @@ -36,16 +36,17 @@ def get_grass_config_dir(major_version, minor_version, env): config_dir = env.get(env_dirname) if config_dir is None: - raise RuntimeError( - f"The {env_dirname} variable is not set, ask your operating" - " system support" + msg = ( + f"The {env_dirname} variable is not set, ask your operating system support" ) + raise RuntimeError(msg) if not os.path.isdir(config_dir): - raise NotADirectoryError( + msg = ( f"The {env_dirname} variable points to directory which does" " not exist, ask your operating system support" ) + raise NotADirectoryError(msg) if WINDOWS: config_dirname = f"GRASS{major_version}" diff --git a/python/grass/benchmark/app.py b/python/grass/benchmark/app.py index 835fc0d544b..fb8262df17c 100644 --- a/python/grass/benchmark/app.py +++ b/python/grass/benchmark/app.py @@ -39,10 +39,11 @@ class CliUsageError(ValueError): def join_results_cli(args): """Translate CLI parser result to API calls.""" if args.prefixes and len(args.results) != len(args.prefixes): - raise CliUsageError( + msg = ( f"Number of prefixes ({len(args.prefixes)}) needs to be the same" f" as the number of input result files ({len(args.results)})" ) + raise CliUsageError(msg) def select_only(result): return result.label == args.only diff --git a/python/grass/benchmark/plots.py b/python/grass/benchmark/plots.py index 24afcbcfbae..575463e662e 100644 --- a/python/grass/benchmark/plots.py +++ b/python/grass/benchmark/plots.py @@ -72,10 +72,9 @@ def nprocs_plot(results, filename=None, title=None, metric="time"): ylabel = metric.title() plt.plot(x, getattr(result, metric), label=result.label) else: - raise ValueError( - f"Invalid metric '{metric}' in result, it should be:\ + msg = f"Invalid metric '{metric}' in result, it should be:\ 'time', 'speedup' or 'efficiency'" - ) + raise ValueError(msg) plt.legend() # If there is not many x values, show ticks for each, but use default # ticks when there is a lot of x values. diff --git a/python/grass/benchmark/results.py b/python/grass/benchmark/results.py index 123e5dd8458..3815a47d7f8 100644 --- a/python/grass/benchmark/results.py +++ b/python/grass/benchmark/results.py @@ -103,9 +103,7 @@ def join_results_from_files( source_filenames, prefixes=None, select=None, prefixes_as_labels=False ): """Join multiple files into one results object.""" - to_merge = [] - for result_file in source_filenames: - to_merge.append(load_results_from_file(result_file)) + to_merge = [load_results_from_file(result_file) for result_file in source_filenames] return join_results( to_merge, prefixes=prefixes, diff --git a/python/grass/benchmark/testsuite/test_benchmark.py b/python/grass/benchmark/testsuite/test_benchmark.py index 4eaf766859a..5d883caa1e9 100644 --- a/python/grass/benchmark/testsuite/test_benchmark.py +++ b/python/grass/benchmark/testsuite/test_benchmark.py @@ -52,14 +52,10 @@ def test_resolutions(self): }, ] resolutions = [300, 200, 100] - results = [] - for benchmark in benchmarks: - results.append( - benchmark_resolutions( - **benchmark, - resolutions=resolutions, - ) - ) + results = [ + benchmark_resolutions(**benchmark, resolutions=resolutions) + for benchmark in benchmarks + ] plot_file = "test_res_plot.png" num_cells_plot(results, filename=plot_file) self.assertTrue(Path(plot_file).is_file()) @@ -76,9 +72,9 @@ def test_single(self): "label": label, } ] - results = [] - for benchmark in benchmarks: - results.append(benchmark_single(**benchmark, repeat=repeat)) + results = [ + benchmark_single(**benchmark, repeat=repeat) for benchmark in benchmarks + ] self.assertEqual(len(results), len(benchmarks)) for result in results: self.assertTrue(hasattr(result, "all_times")) @@ -100,9 +96,10 @@ def test_nprocs(self): "max_nprocs": 4, } ] - results = [] - for benchmark in benchmarks: - results.append(benchmark_nprocs(**benchmark, repeat=repeat, shuffle=True)) + results = [ + benchmark_nprocs(**benchmark, repeat=repeat, shuffle=True) + for benchmark in benchmarks + ] self.assertEqual(len(results), len(benchmarks)) for result in results: self.assertTrue(hasattr(result, "times")) diff --git a/python/grass/benchmark/testsuite/test_benchmark_cli.py b/python/grass/benchmark/testsuite/test_benchmark_cli.py index 8e963245250..a50192c2d1d 100644 --- a/python/grass/benchmark/testsuite/test_benchmark_cli.py +++ b/python/grass/benchmark/testsuite/test_benchmark_cli.py @@ -43,8 +43,7 @@ class TestBenchmarkCLI(TestCase): """Tests that benchmarkin CLI works""" json_filename = "plot_test.json" - png_filenames = [f"plot_test1_{i}.png" for i in range(4)] - png_filenames.append("plot_test2.png") + png_filenames = [*[f"plot_test1_{i}.png" for i in range(4)], "plot_test2.png"] def tearDown(self): """Remove test files""" diff --git a/python/grass/docs/conf.py b/python/grass/docs/conf.py index 2d6a564cac0..cccd2e213ac 100644 --- a/python/grass/docs/conf.py +++ b/python/grass/docs/conf.py @@ -128,7 +128,7 @@ # General information about the project. project = "Python library documentation" -copyright = "2024, GRASS Development Team" +copyright = "2025, GRASS Development Team" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/python/grass/docs/src/gunittest_testing.rst b/python/grass/docs/src/gunittest_testing.rst index 62f258f99c8..20df0245e7e 100644 --- a/python/grass/docs/src/gunittest_testing.rst +++ b/python/grass/docs/src/gunittest_testing.rst @@ -387,7 +387,7 @@ in different locations. .. todo:: Add example of assertions of key-value results. -Especially if a module module has a lot of different parameters allowed +Especially if a module has a lot of different parameters allowed in different combinations, you should test the if the wrong ones are really disallowed and proper error messages are provided (in addition, you can test things such as creation and removal of maps in error states). diff --git a/python/grass/exceptions/__init__.py b/python/grass/exceptions/__init__.py index deec48ffd96..c7024958097 100644 --- a/python/grass/exceptions/__init__.py +++ b/python/grass/exceptions/__init__.py @@ -1,5 +1,4 @@ -"""GRASS GIS interface to Python exceptions -""" +"""GRASS GIS interface to Python exceptions""" import subprocess diff --git a/python/grass/experimental/create.py b/python/grass/experimental/create.py index afe8f9c7b69..aeaf704928c 100644 --- a/python/grass/experimental/create.py +++ b/python/grass/experimental/create.py @@ -39,18 +39,19 @@ def require_create_ensure_mapset( ) exists = mapset_exists(path) if create and exists: - if overwrite: - delete_mapset(path.directory, path.location, path.mapset) - else: - raise ValueError( + if not overwrite: + msg = ( f"Mapset '{path.mapset}' already exists, " "use a different name, overwrite, or ensure" ) + raise ValueError(msg) + delete_mapset(path.directory, path.location, path.mapset) if create or (ensure and not exists): create_mapset(path.directory, path.location, path.mapset) elif not exists or not is_mapset_valid(path): reason = get_mapset_invalid_reason(path.directory, path.location, path.mapset) - raise ValueError(f"Mapset {path.mapset} is not valid: {reason}") + msg = f"Mapset {path.mapset} is not valid: {reason}" + raise ValueError(msg) def create_temporary_mapset(path, location=None) -> MapsetPath: diff --git a/python/grass/experimental/mapset.py b/python/grass/experimental/mapset.py index e6b818f0a2c..a39bb661618 100644 --- a/python/grass/experimental/mapset.py +++ b/python/grass/experimental/mapset.py @@ -1,4 +1,4 @@ -"Session or subsession for mapsets (subprojects)" +"""Session or subsession for mapsets (subprojects)""" import shutil import os @@ -92,7 +92,8 @@ def finish(self): the environment obtained from this object. """ if not self.active: - raise ValueError("Attempt to finish an already finished session") + msg = "Attempt to finish an already finished session" + raise ValueError(msg) os.remove(self._session_file) self._active = False @@ -104,9 +105,8 @@ def __enter__(self): :returns: reference to the object (self) """ if not self.active: - raise ValueError( - "Attempt to use inactive (finished) session as a context manager" - ) + msg = "Attempt to use inactive (finished) session as a context manager" + raise ValueError(msg) return self def __exit__(self, type, value, traceback): # pylint: disable=redefined-builtin @@ -211,7 +211,8 @@ def finish(self): the environment obtained from this object. """ if not self.active: - raise ValueError("Attempt to finish an already finished session") + msg = "Attempt to finish an already finished session" + raise ValueError(msg) self._active = False os.remove(self._session_file) shutil.rmtree(self._path.path, ignore_errors=True) @@ -224,9 +225,8 @@ def __enter__(self): :returns: reference to the object (self) """ if not self.active: - raise ValueError( - "Attempt to use inactive (finished) session as a context manager" - ) + msg = "Attempt to use inactive (finished) session as a context manager" + raise ValueError(msg) return self def __exit__(self, type, value, traceback): # pylint: disable=redefined-builtin diff --git a/python/grass/experimental/tests/conftest.py b/python/grass/experimental/tests/conftest.py index f19d9d39d9e..1dbbc95fcd7 100644 --- a/python/grass/experimental/tests/conftest.py +++ b/python/grass/experimental/tests/conftest.py @@ -40,9 +40,7 @@ def unique_id(): @pytest.fixture -def xy_mapset_session( - xy_session_for_module, unique_id -): # pylint: disable=redefined-outer-name +def xy_mapset_session(xy_session_for_module, unique_id): # pylint: disable=redefined-outer-name """Active session in a mapset of an XY location Mapset scope is function, while the location scope is module. diff --git a/python/grass/experimental/tests/grass_script_mapset_session_test.py b/python/grass/experimental/tests/grass_script_mapset_session_test.py index 740770937dd..9cdd9aa3826 100644 --- a/python/grass/experimental/tests/grass_script_mapset_session_test.py +++ b/python/grass/experimental/tests/grass_script_mapset_session_test.py @@ -155,9 +155,9 @@ def test_create_multiple(xy_session): assert sorted(collected) == sorted(create_names) existing_mapsets = get_mapset_names(env=xy_session.env) assert sorted(existing_mapsets) == sorted(create_names + original_mapsets) - assert ( - len(set(top_level_collected)) == 1 - ), f"Top level mapset changed: {top_level_collected}" + assert len(set(top_level_collected)) == 1, ( + f"Top level mapset changed: {top_level_collected}" + ) def test_nested_top_env(xy_session): diff --git a/python/grass/grassdb/checks.py b/python/grass/grassdb/checks.py index 9024ac33517..052addac248 100644 --- a/python/grass/grassdb/checks.py +++ b/python/grass/grassdb/checks.py @@ -20,6 +20,7 @@ import grass.grassdb.config as cfg import grass.script as gs from grass.script import gisenv +from itertools import starmap def mapset_exists(path: str | os.PathLike[str], location=None, mapset=None) -> bool: @@ -283,19 +284,13 @@ def get_mapset_invalid_reason(database, location, mapset, none_for_no_reason=Fal return _("<%s> is not a GRASS Mapset because it is not a directory") % mapset if not (mapset_path / "WIND").is_file(): return ( - _( - "<%s> is not a valid GRASS Mapset" - " because it does not have a WIND file" - ) + _("<%s> is not a valid GRASS Mapset because it does not have a WIND file") % mapset ) # based on the is_mapset_valid() function if not os.access(mapset_path / "WIND", os.R_OK): return ( - _( - "<%s> is not a valid GRASS Mapset" - " because its WIND file is not readable" - ) + _("<%s> is not a valid GRASS Mapset because its WIND file is not readable") % mapset ) # no reason for invalidity found (might be valid) @@ -335,18 +330,12 @@ def get_location_invalid_reason( # permanent mapset if not permanent_path.exists(): return ( - _( - "<%s> is not a valid GRASS Location" - " because PERMANENT Mapset is missing" - ) + _("<%s> is not a valid GRASS Location because PERMANENT Mapset is missing") % location_path ) if not permanent_path.is_dir(): return ( - _( - "<%s> is not a valid GRASS Location" - " because PERMANENT is not a directory" - ) + _("<%s> is not a valid GRASS Location because PERMANENT is not a directory") % location_path ) # partially based on the is_location_valid() function @@ -536,10 +525,7 @@ def get_reasons_locations_not_removable(locations): Returns messages as list if there were any failed checks, otherwise empty list. """ - messages = [] - for grassdb, location in locations: - messages += get_reasons_location_not_removable(grassdb, location) - return messages + return list(starmap(get_reasons_location_not_removable, locations)) def get_reasons_location_not_removable(grassdb, location): @@ -570,9 +556,7 @@ def get_reasons_location_not_removable(grassdb, location): ) # Append to the list of tuples - mapsets = [] - for g_mapset in g_mapsets: - mapsets.append((grassdb, location, g_mapset)) + mapsets = [(grassdb, location, g_mapset) for g_mapset in g_mapsets] # Concentenate both checks messages += get_reasons_mapsets_not_removable(mapsets, check_permanent=False) @@ -601,9 +585,7 @@ def get_reasons_grassdb_not_removable(grassdb): g_locations = get_list_of_locations(grassdb) # Append to the list of tuples - locations = [] - for g_location in g_locations: - locations.append((grassdb, g_location)) + locations = [(grassdb, g_location) for g_location in g_locations] return get_reasons_locations_not_removable(locations) @@ -614,12 +596,11 @@ def get_list_of_locations(dbase): :return: list of locations (sorted) """ - locations = [] - for location in glob.glob(os.path.join(dbase, "*")): - if os.path.join(location, "PERMANENT") in glob.glob( - os.path.join(location, "*") - ): - locations.append(os.path.basename(location)) + locations = [ + os.path.basename(location) + for location in glob.glob(os.path.join(dbase, "*")) + if os.path.join(location, "PERMANENT") in glob.glob(os.path.join(location, "*")) + ] locations.sort(key=lambda x: x.lower()) diff --git a/python/grass/grassdb/history.py b/python/grass/grassdb/history.py index d7c22eb36fc..1821e2ffbc1 100644 --- a/python/grass/grassdb/history.py +++ b/python/grass/grassdb/history.py @@ -257,13 +257,14 @@ def get_initial_command_info(env_run): # Execution timestamp in ISO 8601 format exec_time = datetime.now().isoformat() - # 2D raster MASK presence + # 2D raster mask presence + mask2d_status = gs.parse_command("r.mask.status", format="json", env=env_run) + + # 3D raster mask presence env = gs.gisenv(env_run) mapset_path = Path(env["GISDBASE"]) / env["LOCATION_NAME"] / env["MAPSET"] - mask2d_present = (mapset_path / "cell" / "MASK").exists() - - # 3D raster MASK presence mask3d_present = (mapset_path / "grid3" / "RASTER3D_MASK").exists() + mask3d_name = f"RASTER3D_MASK@{env['MAPSET']}" # Computational region settings region_settings = gs.region(env=env_run) @@ -271,8 +272,10 @@ def get_initial_command_info(env_run): # Finalize the command info dictionary return { "timestamp": exec_time, - "mask2d": mask2d_present, + "mask2d": mask2d_status["present"], + "mask2d_name": mask2d_status["name"], "mask3d": mask3d_present, + "mask3d_name": mask3d_name, "region": region_settings, "status": Status.RUNNING.value, } @@ -310,10 +313,10 @@ def add_entry(history_path, entry): :param str history_path: path to the history log file :param dict entry: entry consisting of 'command' and 'command_info' keys """ - if get_history_file_extension(history_path) == ".json": - _add_entry_to_JSON(history_path, entry) - else: - raise ValueError("Adding entries is supported only for JSON format.") + if get_history_file_extension(history_path) != ".json": + msg = "Adding entries is supported only for JSON format." + raise ValueError(msg) + _add_entry_to_JSON(history_path, entry) def _update_entry_in_JSON(history_path, command_info, index=None): @@ -357,10 +360,10 @@ def update_entry(history_path, command_info, index=None): :param dict command_info: command info entry for update :param int|None index: index of the command to be updated """ - if get_history_file_extension(history_path) == ".json": - _update_entry_in_JSON(history_path, command_info, index) - else: - raise ValueError("Updating entries is supported only for JSON format.") + if get_history_file_extension(history_path) != ".json": + msg = "Updating entries is supported only for JSON format." + raise ValueError(msg) + _update_entry_in_JSON(history_path, command_info, index) def copy(history_path, target_path): diff --git a/python/grass/gunittest/case.py b/python/grass/gunittest/case.py index 49a378d6133..5c4e81b3c1d 100644 --- a/python/grass/gunittest/case.py +++ b/python/grass/gunittest/case.py @@ -48,6 +48,7 @@ class TestCase(unittest.TestCase): Be especially careful and always use keyword argument syntax for *msg* parameter. """ + longMessage = True # to get both standard and custom message maxDiff = None # we can afford long diffs _temp_region = None # to control the temporary region @@ -133,14 +134,17 @@ def del_temp_region(cls): name = os.environ.pop("WIND_OVERRIDE") if name != cls._temp_region: # be strict about usage of region - raise RuntimeError( + msg = ( "Inconsistent use of" " TestCase.use_temp_region, WIND_OVERRIDE" " or temporary region in general\n" "Region to which should be now deleted ({n})" " by TestCase class" "does not correspond to currently set" - " WIND_OVERRIDE ({c})", + " WIND_OVERRIDE ({c})" + ) + raise RuntimeError( + msg, n=cls._temp_region, c=name, ) @@ -255,20 +259,21 @@ def assertModuleKeyValue( " provided in reference" ": %s\n" % (module, ", ".join(missing)) ) - if mismatch: - stdMsg = "%s difference:\n" % module - stdMsg += "mismatch values" - stdMsg += " (key, reference, actual): %s\n" % mismatch - stdMsg += "command: %s %s" % (module, parameters) - else: + if not mismatch: # we can probably remove this once we have more tests # of keyvalue_equals and diff_keyvalue against each other - raise RuntimeError( + msg = ( "keyvalue_equals() showed difference but" " diff_keyvalue() did not. This can be" " a bug in one of them or in the caller" " (assertModuleKeyValue())" ) + raise RuntimeError(msg) + stdMsg = "%s difference:\n" % module + stdMsg += "mismatch values" + stdMsg += " (key, reference, actual): %s\n" % mismatch + stdMsg += "command: %s %s" % (module, parameters) + self.fail(self._formatMessage(msg, stdMsg)) def assertRasterFitsUnivar(self, raster, reference, precision=None, msg=None): @@ -1331,7 +1336,7 @@ def runModule(cls, module, expecting_stdout=False, **kwargs): module.name, module.get_python(), module.returncode, errors=errors ) # TODO: use this also in assert and apply when appropriate - if expecting_stdout and not module.outputs.stdout.strip(): + if expecting_stdout and (not module.outputs.stdout.strip()): if module.outputs.stderr: errors = " The errors are:\n" + module.outputs.stderr else: @@ -1441,9 +1446,11 @@ def assertModuleFail(self, module, msg=None, **kwargs): def _module_from_parameters(module, **kwargs): if kwargs: if not isinstance(module, str): - raise ValueError("module can be only string or PyGRASS Module") + msg = "module can be only string or PyGRASS Module" + raise ValueError(msg) if isinstance(module, Module): - raise ValueError("module can be only string if other parameters are given") + msg = "module can be only string if other parameters are given" + raise ValueError(msg) # allow passing all parameters in one dictionary called parameters if list(kwargs.keys()) == ["parameters"]: kwargs = kwargs["parameters"] @@ -1454,20 +1461,24 @@ def _module_from_parameters(module, **kwargs): def _check_module_run_parameters(module): # in this case module already run and we would start it again if module.run_: - raise ValueError("Do not run the module manually, set run_=False") + msg = "Do not run the module manually, set run_=False" + raise ValueError(msg) if not module.finish_: - raise ValueError( + msg = ( "This function will always finish module run," " set finish_=None or finish_=True." ) + raise ValueError(msg) # we expect most of the usages with stdout=PIPE # TODO: in any case capture PIPE always? if module.stdout_ is None: module.stdout_ = subprocess.PIPE elif module.stdout_ != subprocess.PIPE: - raise ValueError("stdout_ can be only PIPE or None") + msg = "stdout_ can be only PIPE or None" + raise ValueError(msg) if module.stderr_ is None: module.stderr_ = subprocess.PIPE elif module.stderr_ != subprocess.PIPE: - raise ValueError("stderr_ can be only PIPE or None") + msg = "stderr_ can be only PIPE or None" + raise ValueError(msg) # because we want to capture it diff --git a/python/grass/gunittest/checkers.py b/python/grass/gunittest/checkers.py index c63f6d08ff0..57148c77019 100644 --- a/python/grass/gunittest/checkers.py +++ b/python/grass/gunittest/checkers.py @@ -9,14 +9,20 @@ :authors: Vaclav Petras, Soeren Gebbert """ -import os -import sys -import re +from __future__ import annotations + import doctest import hashlib +import os +import re +import sys +from typing import TYPE_CHECKING, Any from grass.script.utils import encode +if TYPE_CHECKING: + from collections.abc import Callable, Mapping + try: from grass.script.core import KeyValue except (ImportError, AttributeError): @@ -218,19 +224,17 @@ def text_to_keyvalue( if kvdict: # key is the one from previous line msg = ( - "Empty line in the parsed text." - " Previous line's key is <%s>" + "Empty line in the parsed text. Previous line's key is <%s>" ) % key raise ValueError(msg) else: # noqa: PLR5501 # line contains something but not separator if not skip_invalid: # TODO: here should go _ for translation - raise ValueError( - ("Line <{l}> does not contain separator <{s}>.").format( - l=line, s=sep - ) + msg = ("Line <{l}> does not contain separator <{s}>.").format( + l=line, s=sep ) + raise ValueError(msg) # if we get here we are silently ignoring the line # because it is invalid (does not contain key-value separator) or # because it is empty @@ -255,7 +259,7 @@ def text_to_keyvalue( # TODO: define standard precisions for DCELL, FCELL, CELL, mm, ft, cm, ... # TODO: decide if None is valid, and use some default or no compare # TODO: is None a valid value for precision? -def values_equal(value_a, value_b, precision=0.000001): +def values_equal(value_a, value_b, precision: float = 0.000001) -> bool: """ >>> values_equal(1.022, 1.02, precision=0.01) True @@ -267,7 +271,7 @@ def values_equal(value_a, value_b, precision=0.000001): True >>> values_equal("Hello", "hello") False - """ + """ # noqa: D402; Add a summary # each if body needs to handle only not equal state if isinstance(value_a, float) and isinstance(value_b, float): @@ -277,9 +281,8 @@ def values_equal(value_a, value_b, precision=0.000001): # in Python 3 None < 3 raises TypeError precision = float(precision) if precision < 0: - raise ValueError( - "precision needs to be greater than or equal to zero: {precision} < 0" - ) + msg = "precision needs to be greater than or equal to zero: {precision} < 0" + raise ValueError(msg) if abs(value_a - value_b) > precision: return False @@ -318,8 +321,13 @@ def values_equal(value_a, value_b, precision=0.000001): def keyvalue_equals( - dict_a, dict_b, precision, def_equal=values_equal, key_equal=None, a_is_subset=False -): + dict_a: Mapping, + dict_b: Mapping, + precision: float, + def_equal: Callable = values_equal, + key_equal: Mapping[Any, Callable] | None = None, + a_is_subset: bool = False, +) -> bool: """Compare two dictionaries. .. note:: @@ -350,8 +358,8 @@ def keyvalue_equals( :param dict_b: second dictionary :param precision: precision with which the floating point values are compared (passed to equality functions) - :param callable def_equal: function used for comparison by default - :param dict key_equal: dictionary of functions used for comparison + :param def_equal: function used for comparison by default + :param key_equal: dictionary of functions used for comparison of specific keys, `def_equal` is used for the rest, keys in dictionary are keys in `dict_a` and `dict_b` dictionaries, values are the functions used to comapare the given key @@ -360,7 +368,7 @@ def keyvalue_equals( :return: `True` if identical, `False` if different - Use `diff_keyvalue()` to get information about differeces. + Use `diff_keyvalue()` to get information about differences. You can use this function to find out if there is a difference and then use `diff_keyvalue()` to determine all the differences between dictionaries. @@ -369,7 +377,7 @@ def keyvalue_equals( if not a_is_subset and sorted(dict_a.keys()) != sorted(dict_b.keys()): return False - b_keys = dict_b.keys() if a_is_subset else None + b_keys = dict_b.keys() if a_is_subset else set() # iterate over subset or just any if not a_is_subset # check for missing keys in superset @@ -398,7 +406,7 @@ def diff_keyvalue( (['d'], ['a'], [('c', 2, 1)]) You can provide only a subset of values in dict_a, in this case - first item in tuple is an emptu list:: + first item in tuple is an empty list:: >>> diff_keyvalue(a, b, a_is_subset=True, precision=0) ([], ['a'], [('c', 2, 1)]) @@ -533,7 +541,7 @@ def check_text_ellipsis(reference, actual) -> bool: >>> check_text_ellipsis("Result: [569] (...)", "Result: 9 (too high)") False - """ + """ # noqa: D402; Add a summary ref_escaped = re.escape(reference) exp = re.compile(r"\\\.\\\.\\\.") # matching escaped ... ref_regexp = exp.sub(".+", ref_escaped) + "$" @@ -592,7 +600,7 @@ def check_text_ellipsis_doctest(reference, actual): ... optionflags=doctest.ELLIPSIS, ... ) False - """ + """ # noqa: D402; Add a summary # this can be also global checker = doctest.OutputChecker() return checker.check_output(reference, actual, optionflags=doctest.ELLIPSIS) diff --git a/python/grass/gunittest/invoker.py b/python/grass/gunittest/invoker.py index 29b62830e25..b6e3e5bca99 100644 --- a/python/grass/gunittest/invoker.py +++ b/python/grass/gunittest/invoker.py @@ -286,10 +286,11 @@ def run_in_location(self, gisdbase, location, location_type, results_dir, exclud not to one file as these will simply contain the last executed file. """ if os.path.abspath(results_dir) == os.path.abspath(self.start_dir): - raise RuntimeError( + msg = ( "Results root directory should not be the same" " as discovery start directory" ) + raise RuntimeError(msg) self.reporter = GrassTestFilesMultiReporter( reporters=[ GrassTestFilesTextReporter(stream=sys.stderr), diff --git a/python/grass/gunittest/loader.py b/python/grass/gunittest/loader.py index 50fa368f62a..b59e607a341 100644 --- a/python/grass/gunittest/loader.py +++ b/python/grass/gunittest/loader.py @@ -113,87 +113,88 @@ def discover_modules( for skip in to_skip: dirs.remove(skip) - if testsuite_dir in dirs: - dirs.remove(testsuite_dir) # do not recurse to testsuite - full = os.path.join(root, testsuite_dir) - - files = os.listdir(full) - if file_pattern: - files = fnmatch.filter(files, file_pattern) - if file_regexp: - files = [f for f in files if re.match(file_regexp, f)] - if exclude: - files = fnmatch_exclude_with_base(files, full, exclude) - files = sorted(files) - # get test/module name without .py - # expecting all files to end with .py - # this will not work for invoking bat files but it works fine - # as long as we handle only Python files (and using Python - # interpreter for invoking) - - # TODO: warning about no tests in a testsuite - # (in what way?) - for file_name in files: - # TODO: add also import if requested - # (see older versions of this file) - # TODO: check if there is some main in .py - # otherwise we can have successful test just because - # everything was loaded into Python - # TODO: check if there is set -e or exit !0 or ? - # otherwise we can have successful because nothing was reported - abspath = os.path.abspath(full) - abs_file_path = os.path.join(abspath, file_name) - if file_name.endswith(".py"): - if file_name == "__init__.py": - # we always ignore __init__.py - continue - file_type = "py" - name = file_name[:-3] - elif file_name.endswith(".sh"): - file_type = "sh" - name = file_name[:-3] + if testsuite_dir not in dirs: + continue + dirs.remove(testsuite_dir) # do not recurse to testsuite + full = os.path.join(root, testsuite_dir) + + files = os.listdir(full) + if file_pattern: + files = fnmatch.filter(files, file_pattern) + if file_regexp: + files = [f for f in files if re.match(file_regexp, f)] + if exclude: + files = fnmatch_exclude_with_base(files, full, exclude) + files = sorted(files) + # get test/module name without .py + # expecting all files to end with .py + # this will not work for invoking bat files but it works fine + # as long as we handle only Python files (and using Python + # interpreter for invoking) + + # TODO: warning about no tests in a testsuite + # (in what way?) + for file_name in files: + # TODO: add also import if requested + # (see older versions of this file) + # TODO: check if there is some main in .py + # otherwise we can have successful test just because + # everything was loaded into Python + # TODO: check if there is set -e or exit !0 or ? + # otherwise we can have successful because nothing was reported + abspath = os.path.abspath(full) + abs_file_path = os.path.join(abspath, file_name) + if file_name.endswith(".py"): + if file_name == "__init__.py": + # we always ignore __init__.py + continue + file_type = "py" + name = file_name[:-3] + elif file_name.endswith(".sh"): + file_type = "sh" + name = file_name[:-3] + else: + file_type = None # alternative would be '', now equivalent + name = file_name + + add = False + try: + if grass_location == all_locations_value: + add = True else: - file_type = None # alternative would be '', now equivalent - name = file_name - - add = False - try: - if grass_location == all_locations_value: - add = True - else: - try: - locations = ["nc", "stdmaps", "all"] - except AttributeError: - add = True # test is universal - else: - if universal_location_value in locations: - add = True # cases when it is explicit - if grass_location in locations: - add = True # standard case with given location - if not locations: - add = True # count not specified as universal - except ImportError as e: - if add_failed_imports: - add = True + try: + locations = ["nc", "stdmaps", "all"] + except AttributeError: + add = True # test is universal else: - raise ImportError( - "Cannot import module named" - " %s in %s (%s)" % (name, full, e.message) - ) - # alternative is to create TestClass which will raise - # see unittest.loader - if add: - modules.append( - GrassTestPythonModule( - name=name, - module=None, - tested_dir=root, - file_dir=full, - abs_file_path=abs_file_path, - file_path=os.path.join(full, file_name), - file_type=file_type, - ) + if universal_location_value in locations: + add = True # cases when it is explicit + if grass_location in locations: + add = True # standard case with given location + if not locations: + add = True # count not specified as universal + except ImportError as e: + if add_failed_imports: + add = True + else: + raise ImportError( + "Cannot import module named %s in %s (%s)" + % (name, full, e.message) + ) + # alternative is to create TestClass which will raise + # see unittest.loader + if add: + modules.append( + GrassTestPythonModule( + name=name, + module=None, + tested_dir=root, + file_dir=full, + abs_file_path=abs_file_path, + file_path=os.path.join(full, file_name), + file_type=file_type, ) + ) # in else with some verbose we could tell about skipped test return modules @@ -228,9 +229,7 @@ def discover(self, start_dir, pattern="test*.py", top_level_dir=None): universal_location_value=self.universal_tests_value, import_modules=True, ) - tests = [] - for module in modules: - tests.append(self.loadTestsFromModule(module.module)) + tests = [self.loadTestsFromModule(module.module) for module in modules] return self.suiteClass(tests) diff --git a/python/grass/gunittest/main.py b/python/grass/gunittest/main.py index 741db5e43d2..fe39e7ca23b 100644 --- a/python/grass/gunittest/main.py +++ b/python/grass/gunittest/main.py @@ -147,7 +147,8 @@ def get_config(start_directory, config_file): # Does not check presence of the file config_parser.read(config_file) else: - raise ValueError("Either start_directory or config_file must be set") + msg = "Either start_directory or config_file must be set" + raise ValueError(msg) if "gunittest" not in config_parser: # Create an empty section if file is not available or section is not present. config_parser.read_dict({"gunittest": {}}) diff --git a/python/grass/gunittest/multireport.py b/python/grass/gunittest/multireport.py index 5a0350f1786..9deb0528ada 100644 --- a/python/grass/gunittest/multireport.py +++ b/python/grass/gunittest/multireport.py @@ -413,13 +413,13 @@ def main_page( ) ) page.write("") - for image, caption in itertools.izip(images, captions): - page.write( - "

      {caption}

      " - '{caption}'.format( - image=image, caption=caption - ) + page.writelines( + "

      {caption}

      " + '{caption}'.format( + image=image, caption=caption ) + for image, caption in itertools.izip(images, captions) + ) page.write("") diff --git a/python/grass/gunittest/multirunner.py b/python/grass/gunittest/multirunner.py index ebb9e80049a..dd5411d66fc 100644 --- a/python/grass/gunittest/multirunner.py +++ b/python/grass/gunittest/multirunner.py @@ -111,16 +111,16 @@ def main(): # we assume that the start script is available and in the PATH # the shell=True is here because of MS Windows? (code taken from wiki) startcmd = grass_executable + " --config path" - p = subprocess.Popen( + with subprocess.Popen( startcmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE - ) - out, err = p.communicate() - if p.returncode != 0: - print( - "ERROR: Cannot find GRASS GIS start script (%s):\n%s" % (startcmd, err), - file=sys.stderr, - ) - return 1 + ) as p: + out, err = p.communicate() + if p.returncode != 0: + print( + "ERROR: Cannot find GRASS GIS start script (%s):\n%s" % (startcmd, err), + file=sys.stderr, + ) + return 1 gisbase = decode(out.strip()) # set GISBASE environment variable @@ -151,7 +151,7 @@ def main(): # including also type to make it unique and preserve it for sure report = "report_for_" + location + "_" + location_type absreport = os.path.abspath(report) - p = subprocess.Popen( + with subprocess.Popen( [ sys.executable, "-tt", @@ -167,23 +167,24 @@ def main(): absreport, ], cwd=grasssrc, - ) - returncode = p.wait() - reports.append(report) + ) as p2: + returncode = p2.wait() + reports.append(report) if main_report: # TODO: solve the path to source code (work now only for grass source code) arguments = [ sys.executable, - grasssrc + "/python/grass/gunittest/" + "multireport.py", - "--timestapms", + grasssrc + "/python/grass/gunittest/multireport.py", + "--timestamps", + *reports, ] - arguments.extend(reports) - p = subprocess.Popen(arguments) - returncode = p.wait() - if returncode != 0: - print("ERROR: Creation of main report failed.", file=sys.stderr) - return 1 + + with subprocess.Popen(arguments) as p3: + returncode = p3.wait() + if returncode != 0: + print("ERROR: Creation of main report failed.", file=sys.stderr) + return 1 return 0 diff --git a/python/grass/gunittest/reporters.py b/python/grass/gunittest/reporters.py index 7c93872a656..cef2ee5a0b8 100644 --- a/python/grass/gunittest/reporters.py +++ b/python/grass/gunittest/reporters.py @@ -52,8 +52,9 @@ def replace_in_file(file_path, pattern, repl): # using tmp file to store the replaced content tmp_file_path = file_path + ".tmp" with open(file_path) as old_file, open(tmp_file_path, "w") as new_file: - for line in old_file: - new_file.write(re.sub(pattern=pattern, string=line, repl=repl)) + new_file.writelines( + re.sub(pattern=pattern, string=line, repl=repl) for line in old_file + ) # remove old file since it must not exist for rename/move os.remove(file_path) # replace old file by new file @@ -160,19 +161,19 @@ def get_svn_revision(): """ # TODO: here should be starting directory # but now we are using current as starting - p = subprocess.Popen( + with subprocess.Popen( ["svnversion", "."], stdout=subprocess.PIPE, stderr=subprocess.PIPE - ) - stdout, stderr = p.communicate() - rc = p.poll() - if not rc: + ) as p: + stdout, stderr = p.communicate() + rc = p.poll() + if rc: + return None stdout = stdout.strip() stdout = stdout.removesuffix("M") if ":" in stdout: # the first one is the one of source code stdout = stdout.split(":")[0] return stdout - return None def get_svn_info(): @@ -277,9 +278,7 @@ def get_html_test_authors_table(directory, tests_authors): not_testing_authors = tested_dir_authors - tests_authors else: no_svn_text = ( - '' - "Authors cannot be obtained using SVN." - "" + 'Authors cannot be obtained using SVN.' ) not_testing_authors = tested_dir_authors = [no_svn_text] if not not_testing_authors: @@ -448,8 +447,7 @@ def wrap_stdstream_to_html(infile, outfile, module, stream): after = "

      " with open(outfile, "w") as html, open(infile) as text: html.write(before) - for line in text: - html.write(color_error_line(html_escape(line))) + html.writelines(color_error_line(html_escape(line)) for line in text) html.write(after) @@ -516,8 +514,7 @@ def success_to_html_text(total, successes): # alternatives: SUCCEEDED, passed, OK return 'succeeded' return ( - '' - "? more successes than total ?" + '? more successes than total ?' ) @@ -527,10 +524,8 @@ def success_to_html_text(total, successes): def success_to_html_percent(total, successes): if total: pass_per = 100 * (float(successes) / total) - pass_per = percent_to_html(pass_per) - else: - pass_per = UNKNOWN_NUMBER_HTML - return pass_per + return percent_to_html(pass_per) + return UNKNOWN_NUMBER_HTML class GrassTestFilesHtmlReporter(GrassTestFilesCountingReporter): @@ -561,9 +556,7 @@ def start(self, results_dir): svn_info = get_svn_info() if not svn_info: svn_text = ( - '' - "SVN revision cannot be obtained" - "" + 'SVN revision cannot be obtained' ) else: url = get_source_url( @@ -631,9 +624,9 @@ def format_percentage(percentage): ) self.main_index.write( - "{tfoot}" - "

      {summary}

      " - "".format(tfoot=tfoot, summary=summary_sentence) + "{tfoot}

      {summary}

      ".format( + tfoot=tfoot, summary=summary_sentence + ) ) self.main_index.close() @@ -760,7 +753,7 @@ def end_file_test( # TODO: replace by better handling of potential lists when parsing # TODO: create link to module if running in grass or in addons # alternatively a link to module test summary - if type(modules) is not list: + if not isinstance(modules, list): modules = [modules] # here we would have also links to coverage, profiling, ... @@ -797,8 +790,10 @@ def end_file_test( file_index.write(files_section) if supplementary_files: - for f in supplementary_files: - file_index.write('
    • {f}
    • '.format(f=f)) + file_index.writelines( + '
    • {f}
    • '.format(f=f) + for f in supplementary_files + ) file_index.write("
    ") @@ -853,41 +848,34 @@ def finish(self): svn_info = get_svn_info() svn_revision = "" if not svn_info else svn_info["revision"] - summary = {} - summary["files_total"] = self.test_files - summary["files_successes"] = self.files_pass - summary["files_failures"] = self.files_fail - - summary["names"] = self.names - summary["tested_dirs"] = self.tested_dirs - # TODO: we don't have a general mechanism for storing any type in text - summary["files_returncodes"] = [str(item) for item in self.files_returncodes] - - # let's use seconds as a universal time delta format - # (there is no standard way how to store time delta as string) - summary["time"] = self.main_time.total_seconds() - - status = "failed" if self.files_fail else "succeeded" - summary["status"] = status - - summary["total"] = self.total - summary["successes"] = self.successes - summary["failures"] = self.failures - summary["errors"] = self.errors - summary["skipped"] = self.skipped - summary["expected_failures"] = self.expected_failures - summary["unexpected_successes"] = self.unexpected_success - - summary["test_files_authors"] = self.test_files_authors - summary["tested_modules"] = self.modules - summary["svn_revision"] = svn_revision - # ignoring issues with time zones - summary["timestamp"] = self.main_start_time.strftime("%Y-%m-%d %H:%M:%S") - # TODO: add some general metadata here (passed in constructor) - - # add additional information - for key, value in self._info.items(): - summary[key] = value + summary = { + "files_total": self.test_files, + "files_successes": self.files_pass, + "files_failures": self.files_fail, + "names": self.names, + "tested_dirs": self.tested_dirs, + # TODO: we don't have a general mechanism for storing any type in text + "files_returncodes": [str(item) for item in self.files_returncodes], + # let's use seconds as a universal time delta format + # (there is no standard way how to store time delta as string) + "time": self.main_time.total_seconds(), + "status": "failed" if self.files_fail else "succeeded", + "total": self.total, + "successes": self.successes, + "failures": self.failures, + "errors": self.errors, + "skipped": self.skipped, + "expected_failures": self.expected_failures, + "unexpected_successes": self.unexpected_success, + "test_files_authors": self.test_files_authors, + "tested_modules": self.modules, + "svn_revision": svn_revision, + # ignoring issues with time zones + "timestamp": self.main_start_time.strftime("%Y-%m-%d %H:%M:%S"), + # TODO: add some general metadata here (passed in constructor) + # add additional information + **dict(self._info.items()), + } summary_filename = os.path.join(self.result_dir, "test_keyvalue_result.txt") text = keyvalue_to_text(summary, sep="=", vsep="\n", isep=",") @@ -959,9 +947,6 @@ def __init__(self, stream): super().__init__() self._stream = stream - def start(self, results_dir): - super().start(results_dir) - def finish(self): super().finish() @@ -1129,7 +1114,7 @@ def report_for_dir(self, root, directory, test_files): test_file_authors = summary.get("test_file_authors") if not test_file_authors: test_file_authors = [] - if type(test_file_authors) is not list: + if not isinstance(test_file_authors, list): test_file_authors = [test_file_authors] test_files_authors.extend(test_file_authors) diff --git a/python/grass/gunittest/runner.py b/python/grass/gunittest/runner.py index 0bbd0f361b9..e27305cacbc 100644 --- a/python/grass/gunittest/runner.py +++ b/python/grass/gunittest/runner.py @@ -26,8 +26,8 @@ class _WritelnDecorator: def __init__(self, stream): self.stream = stream - def __getattr__(self, attr): - if attr in ("stream", "__getstate__"): + def __getattr__(self, attr: str): + if attr in {"stream", "__getstate__"}: raise AttributeError(attr) return getattr(self.stream, attr) @@ -396,7 +396,7 @@ def addUnexpectedSuccess(self, test): raise def printErrors(self): - "Called by TestRunner after test run" + """Called by TestRunner after test run""" super().printErrors() for result in self._results: try: @@ -470,7 +470,7 @@ def __init__( self._result = result def run(self, test): - "Run the given test case or test suite." + """Run the given test case or test suite.""" result = self._result unittest.registerResult(result) result.failfast = self.failfast diff --git a/python/grass/gunittest/testsuite/data/samplecode/submodule_errors/subsubmodule_errors/testsuite/test_error.py b/python/grass/gunittest/testsuite/data/samplecode/submodule_errors/subsubmodule_errors/testsuite/test_error.py index 0be341770a6..d0a6b609479 100644 --- a/python/grass/gunittest/testsuite/data/samplecode/submodule_errors/subsubmodule_errors/testsuite/test_error.py +++ b/python/grass/gunittest/testsuite/data/samplecode/submodule_errors/subsubmodule_errors/testsuite/test_error.py @@ -6,7 +6,8 @@ class TestError(TestCase): # pylint: disable=R0904 def test_something(self): - raise RuntimeError("Error in test function") + msg = "Error in test function" + raise RuntimeError(msg) self.assertTrue(True) @@ -14,7 +15,8 @@ class TestErrorSetUp(TestCase): # pylint: disable=R0904 def setUp(self): - raise RuntimeError("Error in setUp") + msg = "Error in setUp" + raise RuntimeError(msg) def test_something(self): self.assertTrue(True) @@ -24,7 +26,8 @@ class TestErrorTearDown(TestCase): # pylint: disable=R0904 def tearDown(self): - raise RuntimeError("Error in tearDown") + msg = "Error in tearDown" + raise RuntimeError(msg) def test_something(self): self.assertTrue(True) @@ -35,7 +38,8 @@ class TestErrorClassSetUp(TestCase): @classmethod def setUpClass(cls): - raise RuntimeError("Error in setUpClass") + msg = "Error in setUpClass" + raise RuntimeError(msg) def test_something(self): self.assertTrue(True) @@ -46,7 +50,8 @@ class TestErrorClassTearDown(TestCase): @classmethod def tearDownClass(cls): - raise RuntimeError("Error in tearDownClass") + msg = "Error in tearDownClass" + raise RuntimeError(msg) def test_something(self): self.assertTrue(True) diff --git a/python/grass/gunittest/testsuite/data/samplecode/testsuite/test_good_and_bad.py b/python/grass/gunittest/testsuite/data/samplecode/testsuite/test_good_and_bad.py index ef3f36b89bd..d9e207d6359 100644 --- a/python/grass/gunittest/testsuite/data/samplecode/testsuite/test_good_and_bad.py +++ b/python/grass/gunittest/testsuite/data/samplecode/testsuite/test_good_and_bad.py @@ -15,7 +15,8 @@ def test_something_failing(self): self.assertTrue(False, msg="This failed in test_good_and_bad") def test_something_erroring(self): - raise RuntimeError("Some error which was raised") + msg = "Some error which was raised" + raise RuntimeError(msg) self.assertTrue(True, msg="This should not fail in test_good_and_bad") diff --git a/python/grass/gunittest/testsuite/test_checkers.py b/python/grass/gunittest/testsuite/test_checkers.py index b11b25778a3..f79598aa165 100644 --- a/python/grass/gunittest/testsuite/test_checkers.py +++ b/python/grass/gunittest/testsuite/test_checkers.py @@ -369,17 +369,14 @@ class TestMd5Sums(TestCase): @classmethod def setUpClass(cls): with open(cls.correct_file_name_platform_nl, "w") as f: - for line in CORRECT_LINES: - # \n should be converted to platform newline - f.write(line + "\n") + # \n should be converted to platform newline + f.writelines(line + "\n" for line in CORRECT_LINES) with open(cls.correct_file_name_unix_nl, "w") as f: - for line in CORRECT_LINES: - # binary mode will write pure \n - f.write(line + "\n") + # binary mode will write pure \n + f.writelines(line + "\n" for line in CORRECT_LINES) with open(cls.wrong_file_name, "w") as f: - for line in INCORRECT_LINES: - # \n should be converted to platform newline - f.write(line + "\n") + # \n should be converted to platform newline + f.writelines(line + "\n" for line in INCORRECT_LINES) @classmethod def tearDownClass(cls): diff --git a/python/grass/imaging/images2avi.py b/python/grass/imaging/images2avi.py index 9776b9c0db2..27bdd2c15ca 100644 --- a/python/grass/imaging/images2avi.py +++ b/python/grass/imaging/images2avi.py @@ -28,7 +28,7 @@ # # changes of this file GRASS (PNG instead of JPG) by Anna Petrasova 2013 -""" Module images2avi +"""Module images2avi Uses ffmpeg to read and write AVI files. Requires PIL @@ -180,21 +180,22 @@ def readAvi(filename, asNumpy=True): # Run ffmpeg command = "ffmpeg -i input.avi im%d.jpg" - S = subprocess.Popen( - command, shell=True, cwd=tempDir, stdout=subprocess.PIPE, stderr=subprocess.PIPE - ) - - # Show what mencodec has to say - outPut = S.stdout.read() - - if S.wait(): - # An error occurred, show - print(outPut) - print(S.stderr.read()) - # Clean up - _cleanDir(tempDir) - raise RuntimeError("Could not read avi.") - + with subprocess.Popen( + command, + cwd=tempDir, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) as S: + # Show what mencodec has to say + outPut = S.stdout.read() + if S.wait(): + # An error occurred, show + print(outPut) + print(S.stderr.read()) + # Clean up + _cleanDir(tempDir) + msg = "Could not read avi." + raise RuntimeError(msg) # Read images images = images2ims.readIms(os.path.join(tempDir, "im*.jpg"), asNumpy) # Clean up diff --git a/python/grass/imaging/images2gif.py b/python/grass/imaging/images2gif.py index d37ec0c4e32..aa73723dd79 100644 --- a/python/grass/imaging/images2gif.py +++ b/python/grass/imaging/images2gif.py @@ -24,7 +24,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -""" Module images2gif +"""Module images2gif Provides functionality for reading and writing animated GIF images. Use writeGif to write a series of numpy arrays or PIL images as an @@ -101,8 +101,7 @@ def get_cKDTree(): def checkImages(images): - """checkImages(images) - Check numpy images and correct intensity range etc. + """Check numpy images and correct intensity range etc. The same for all movie formats. :param images: @@ -119,7 +118,7 @@ def checkImages(images): # Check and convert dtype if im.dtype == np.uint8: images2.append(im) # Ok - elif im.dtype in [np.float32, np.float64]: + elif im.dtype in (np.float32, np.float64): im = im.copy() im[im < 0] = 0 im[im > 1] = 1 @@ -133,9 +132,11 @@ def checkImages(images): pass # ok elif im.ndim == 3: if im.shape[2] not in [3, 4]: - raise ValueError("This array can not represent an image.") + msg = "This array can not represent an image." + raise ValueError(msg) else: - raise ValueError("This array can not represent an image.") + msg = "This array can not represent an image." + raise ValueError(msg) else: raise ValueError("Invalid image type: " + str(type(im))) @@ -184,7 +185,7 @@ def getImageDescriptor(self, im, xy=None): xy = (0, 0) # Image separator, - bb = "\x2C" + bb = "\x2c" # Image position and size bb += intToBin(xy[0]) # Left position @@ -213,12 +214,11 @@ def getAppExt(self, loops=float("inf")): # (the extension interprets zero loops # to mean an infinite number of loops) # Mmm, does not seem to work - if True: - bb = "\x21\xFF\x0B" # application extension - bb += "NETSCAPE2.0" - bb += "\x03\x01" - bb += intToBin(loops) - bb += "\x00" # end + bb = "\x21\xff\x0b" # application extension + bb += "NETSCAPE2.0" + bb += "\x03\x01" + bb += intToBin(loops) + bb += "\x00" # end return bb def getGraphicsControlExt(self, duration=0.1, dispose=2): @@ -229,9 +229,9 @@ def getGraphicsControlExt(self, duration=0.1, dispose=2): * 0 - No disposal specified. * 1 - Do not dispose. The graphic is to be left in place. - * 2 - Restore to background color. The area used by the graphic + * 2 - Restore to background color. The area used by the graphic must be restored to the background color. - * 3 - Restore to previous. The decoder is required to restore the + * 3 - Restore to previous. The decoder is required to restore the area overwritten by the graphic with what was there prior to rendering the graphic. * 4-7 -To be defined. @@ -240,7 +240,7 @@ def getGraphicsControlExt(self, duration=0.1, dispose=2): :param dispose: """ - bb = "\x21\xF9\x04" + bb = "\x21\xf9\x04" bb += chr((dispose & 3) << 2) # low bit 1 == transparency, # 2nd bit 1 == user input , next 3 bits, the low two of which are used, # are dispose. @@ -253,7 +253,6 @@ def handleSubRectangles(self, images, subRectangles): """Handle the sub-rectangle stuff. If the rectangles are given by the user, the values are checked. Otherwise the subrectangles are calculated automatically. - """ if isinstance(subRectangles, (tuple, list)): @@ -267,7 +266,8 @@ def handleSubRectangles(self, images, subRectangles): if len(xy) == len(images): xy = list(xy) else: - raise ValueError("len(xy) doesn't match amount of images.") + msg = "len(xy) doesn't match amount of images." + raise ValueError(msg) else: xy = [xy for im in images] xy[0] = (0, 0) @@ -275,32 +275,31 @@ def handleSubRectangles(self, images, subRectangles): else: # Calculate xy using some basic image processing - # Check Numpy + # Check NumPy if np is None: - raise RuntimeError("Need Numpy to use auto-subRectangles.") + msg = "Need NumPy to use auto-subRectangles." + raise RuntimeError(msg) # First make numpy arrays if required for i in range(len(images)): im = images[i] - if isinstance(im, Image.Image): - tmp = im.convert() # Make without palette - a = np.asarray(tmp) - if len(a.shape) == 0: - raise MemoryError( - "Too little memory to convert PIL image to array" - ) - images[i] = a + if not isinstance(im, Image.Image): + continue + tmp = im.convert() # Make without palette + a = np.asarray(tmp) + if len(a.shape) == 0: + msg = "Too little memory to convert PIL image to array" + raise MemoryError(msg) + images[i] = a # Determine the sub rectangles images, xy = self.getSubRectangles(images) # Done - return images, xy + return (images, xy) def getSubRectangles(self, ims): - """getSubRectangles(ims) - - Calculate the minimal rectangles that need updating each frame. + """Calculate the minimal rectangles that need updating each frame. Returns a two-element tuple containing the cropped images and a list of x-y positions. @@ -312,11 +311,12 @@ def getSubRectangles(self, ims): # Check image count if len(ims) < 2: - return ims, [(0, 0) for i in ims] + return (ims, [(0, 0) for i in ims]) - # We need numpy + # We need NumPy if np is None: - raise RuntimeError("Need Numpy to calculate sub-rectangles. ") + msg = "Need NumPy to calculate sub-rectangles." + raise RuntimeError(msg) # Prepare ims2 = [ims[0]] @@ -334,11 +334,11 @@ def getSubRectangles(self, ims): Y = np.argwhere(diff.sum(1)) # Get rect coordinates if X.size and Y.size: - x0, x1 = int(X[0]), int(X[-1] + 1) - y0, y1 = int(Y[0]), int(Y[-1] + 1) + x0, x1 = (int(X[0]), int(X[-1] + 1)) + y0, y1 = (int(Y[0]), int(Y[-1] + 1)) else: # No change ... make it minimal - x0, x1 = 0, 2 - y0, y1 = 0, 2 + x0, x1 = (0, 2) + y0, y1 = (0, 2) # Cut out and store im2 = im[y0:y1, x0:x1] @@ -349,14 +349,13 @@ def getSubRectangles(self, ims): # Done # print('%1.2f seconds to determine subrectangles of %i images' % # (time.time()-t0, len(ims2))) - return ims2, xy + return (ims2, xy) def convertImagesToPIL(self, images, dither, nq=0): - """convertImagesToPIL(images, nq=0) - - Convert images to Paletted PIL images, which can then be + """Convert images to Paletted PIL images, which can then be written to a single animaged GIF. + convertImagesToPIL(images, nq=0) """ # Convert to PIL images @@ -374,7 +373,7 @@ def convertImagesToPIL(self, images, dither, nq=0): images2.append(im) # Convert to paletted PIL images - images, images2 = images2, [] + images, images2 = (images2, []) if nq >= 1: # NeuQuant algorithm for im in images: @@ -397,17 +396,13 @@ def convertImagesToPIL(self, images, dither, nq=0): return images2 def writeGifToFile(self, fp, images, durations, loops, xys, disposes): - """writeGifToFile(fp, images, durations, loops, xys, disposes) - - Given a set of images writes the bytes to the specified stream. + """Given a set of images writes the bytes to the specified stream. Requires different handling of palette for PIL and Pillow: based on https://github.com/rec/echomesh/blob/master/ code/python/external/images2gif.py - """ - # Obtain palette for all images and count each occurrence - palettes, occur = [], [] + palettes, occur = ([], []) for im in images: if not pillow: palette = getheader(im)[1] @@ -442,33 +437,28 @@ def writeGifToFile(self, fp, images, durations, loops, xys, disposes): # Next frame is not the first firstFrame = False - if True: - # Write palette and image data - - # Gather info - data = getdata(im) - imdes, data = data[0], data[1:] - graphext = self.getGraphicsControlExt( - durations[frames], disposes[frames] - ) - # Make image descriptor suitable for using 256 local color palette - lid = self.getImageDescriptor(im, xys[frames]) - - # Write local header - if (palette != globalPalette) or (disposes[frames] != 2): - # Use local color palette - fp.write(graphext) - fp.write(lid) # write suitable image descriptor - fp.write(palette) # write local color table - fp.write("\x08") # LZW minimum size code - else: - # Use global color palette - fp.write(graphext) - fp.write(imdes) # write suitable image descriptor + # Write palette and image data + # Gather info + data = getdata(im) + imdes, data = (data[0], data[1:]) + graphext = self.getGraphicsControlExt(durations[frames], disposes[frames]) + # Make image descriptor suitable for using 256 local color palette + lid = self.getImageDescriptor(im, xys[frames]) + + # Write local header + fp.write(graphext) + if (palette != globalPalette) or (disposes[frames] != 2): + # Use local color palette + fp.write(lid) # write suitable image descriptor + fp.write(palette) # write local color table + fp.write("\x08") # LZW minimum size code + else: + # Use global color palette + fp.write(imdes) # write suitable image descriptor - # Write image data - for d in data: - fp.write(d) + # Write image data + for d in data: + fp.write(d) # Prepare for next round frames += 1 @@ -516,9 +506,7 @@ def writeGifPillow(filename, images, duration=0.1, repeat=True): """ loop = 0 if repeat else 1 - quantized = [] - for im in images: - quantized.append(im.quantize()) + quantized = [im.quantize() for im in images] quantized[0].save( filename, save_all=True, @@ -548,7 +536,7 @@ def writeGifVisvis( integer types, and between 0 and 1 for float types. :param duration: scalar or list of scalars The duration for all frames, or (if a list) for each frame. - :param repeat: bool or integer The amount of loops. If True, loops infinitetely. + :param repeat: bool or integer The amount of loops. If True, loops infinitely. :param bool dither: whether to apply dithering :param int nq: If nonzero, applies the NeuQuant quantization algorithm to create the color palette. This algorithm is superior, but @@ -575,7 +563,8 @@ def writeGifVisvis( # Check PIL if PIL is None: - raise RuntimeError("Need PIL to write animated gif files.") + msg = "Need PIL to write animated gif files." + raise RuntimeError(msg) # Check images images = checkImages(images) @@ -596,7 +585,8 @@ def writeGifVisvis( if len(duration) == len(images): duration = list(duration) else: - raise ValueError("len(duration) doesn't match amount of images.") + msg = "len(duration) doesn't match amount of images." + raise ValueError(msg) else: duration = [duration for im in images] @@ -614,7 +604,8 @@ def writeGifVisvis( dispose = defaultDispose if hasattr(dispose, "__len__"): if len(dispose) != len(images): - raise ValueError("len(xy) doesn't match amount of images.") + msg = "len(xy) doesn't match amount of images." + raise ValueError(msg) else: dispose = [dispose for im in images] @@ -622,11 +613,8 @@ def writeGifVisvis( images = gifWriter.convertImagesToPIL(images, dither, nq) # Write - fp = open(filename, "wb") - try: + with open(filename, "wb") as fp: gifWriter.writeGifToFile(fp, images, duration, loops, xy, dispose) - finally: - fp.close() def readGif(filename, asNumpy=True): @@ -637,11 +625,13 @@ def readGif(filename, asNumpy=True): # Check PIL if PIL is None: - raise RuntimeError("Need PIL to read animated gif files.") + msg = "Need PIL to read animated gif files." + raise RuntimeError(msg) - # Check Numpy + # Check NumPy if np is None: - raise RuntimeError("Need Numpy to read animated gif files.") + msg = "Need NumPy to read animated gif files." + raise RuntimeError(msg) # Check whether it exists if not os.path.isfile(filename): @@ -659,7 +649,8 @@ def readGif(filename, asNumpy=True): tmp = pilIm.convert() # Make without palette a = np.asarray(tmp) if len(a.shape) == 0: - raise MemoryError("Too little memory to convert PIL image to array") + msg = "Too little memory to convert PIL image to array" + raise MemoryError(msg) # Store, and next images.append(a) pilIm.seek(pilIm.tell() + 1) @@ -669,9 +660,7 @@ def readGif(filename, asNumpy=True): # Convert to normal PIL images if needed if not asNumpy: images2 = images - images = [] - for im in images2: - images.append(PIL.Image.fromarray(im)) + images = [PIL.Image.fromarray(im) for im in images2] # Done return images @@ -795,15 +784,18 @@ def setconstants(self, samplefac, colors): self.a_s = {} def __init__(self, image, samplefac=10, colors=256): - # Check Numpy + # Check NumPy if np is None: - raise RuntimeError("Need Numpy for the NeuQuant algorithm.") + msg = "Need NumPy for the NeuQuant algorithm." + raise RuntimeError(msg) # Check image if image.size[0] * image.size[1] < NeuQuant.MAXPRIME: - raise OSError("Image is too small") + msg = "Image is too small" + raise OSError(msg) if image.mode != "RGBA": - raise OSError("Image mode should be RGBA.") + msg = "Image mode should be RGBA." + raise OSError(msg) # Initialize self.setconstants(samplefac, colors) @@ -1078,7 +1070,7 @@ def quantize_with_scipy(self, image): return Image.fromarray(px).convert("RGB").quantize(palette=self.paletteImage()) def quantize_without_scipy(self, image): - """ " This function can be used if no scipy is available. + """This function can be used if no scipy is available. It's 7 times slower though. :param image: diff --git a/python/grass/imaging/images2ims.py b/python/grass/imaging/images2ims.py index d86a2247952..b22ceae2183 100644 --- a/python/grass/imaging/images2ims.py +++ b/python/grass/imaging/images2ims.py @@ -24,7 +24,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -""" Module images2ims +"""Module images2ims Use PIL to create a series of images. @@ -65,7 +65,7 @@ def checkImages(images): images2.append(im) # Ok elif im.dtype in [np.float32, np.float64]: theMax = im.max() - if theMax > 128 and theMax < 300: + if 128 < theMax < 300: pass # assume 0:255 else: im = im.copy() @@ -81,9 +81,11 @@ def checkImages(images): pass # ok elif im.ndim == 3: if im.shape[2] not in [3, 4]: - raise ValueError("This array can not represent an image.") + msg = "This array can not represent an image." + raise ValueError(msg) else: - raise ValueError("This array can not represent an image.") + msg = "This array can not represent an image." + raise ValueError(msg) else: raise ValueError("Invalid image type: " + str(type(im))) @@ -118,10 +120,9 @@ def _getSequenceNumber(filename, part1, part2): # Get all numeric chars seq2 = "" for c in seq: - if c in digits: - seq2 += c - else: + if c not in digits: break + seq2 += c # Make int and return return int(seq2) @@ -145,7 +146,8 @@ def writeIms(filename, images): # Check PIL if PIL is None: - raise RuntimeError("Need PIL to write series of image files.") + msg = "Need PIL to write series of image files." + raise RuntimeError(msg) # Check images images = checkImages(images) @@ -174,9 +176,7 @@ def writeIms(filename, images): def readIms(filename, asNumpy=True): - """readIms(filename, asNumpy=True) - - Read images from a series of images in a single directory. Returns a + """Read images from a series of images in a single directory. Returns a list of numpy arrays, or, if asNumpy is false, a list if PIL images. :param filename: @@ -185,11 +185,13 @@ def readIms(filename, asNumpy=True): # Check PIL if PIL is None: - raise RuntimeError("Need PIL to read a series of image files.") + msg = "Need PIL to read a series of image files." + raise RuntimeError(msg) - # Check Numpy + # Check NumPy if asNumpy and np is None: - raise RuntimeError("Need Numpy to return numpy arrays.") + msg = "Need NumPy to return numpy arrays." + raise RuntimeError(msg) # Get dirname and filename filename = os.path.abspath(filename) @@ -229,7 +231,8 @@ def readIms(filename, asNumpy=True): # Make numpy array a = np.asarray(im) if len(a.shape) == 0: - raise MemoryError("Too little memory to convert PIL image to array") + msg = "Too little memory to convert PIL image to array" + raise MemoryError(msg) # Add images.append(a) diff --git a/python/grass/imaging/images2swf.py b/python/grass/imaging/images2swf.py index 7d6f5a25f9b..85f9e447c59 100644 --- a/python/grass/imaging/images2swf.py +++ b/python/grass/imaging/images2swf.py @@ -24,7 +24,7 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -""" Module images2swf +"""Module images2swf Provides a function (writeSwf) to store a series of PIL images or numpy arrays in an SWF movie, that can be played on a wide range of OS's. @@ -65,10 +65,12 @@ of a watermark in the upper left corner. """ + from __future__ import annotations import os import zlib +from pathlib import Path try: import numpy as np @@ -92,8 +94,7 @@ def checkImages(images): - """checkImages(images) - Check numpy images and correct intensity range etc. + """Check numpy images and correct intensity range etc. The same for all movie formats. """ # Init results @@ -110,7 +111,7 @@ def checkImages(images): images2.append(im) # Ok elif im.dtype in [np.float32, np.float64]: theMax = im.max() - if theMax > 128 and theMax < 300: + if 128 < theMax < 300: pass # assume 0:255 else: im = im.copy() @@ -126,9 +127,11 @@ def checkImages(images): pass # ok elif im.ndim == 3: if im.shape[2] not in [3, 4]: - raise ValueError("This array can not represent an image.") + msg = "This array can not represent an image." + raise ValueError(msg) else: - raise ValueError("This array can not represent an image.") + msg = "This array can not represent an image." + raise ValueError(msg) else: raise ValueError("Invalid image type: " + str(type(im))) @@ -177,7 +180,8 @@ def Append(self, bits): if isinstance(bits, int): bits = str(bits) if not isinstance(bits, string_types): - raise ValueError("Append bits as strings or integers!") + msg = "Append bits as strings or integers!" + raise ValueError(msg) # add bits for bit in bits: @@ -240,7 +244,8 @@ def intToBits(i: int, n: int | None = None) -> BitArray: # justify if n is not None: if len(bb) > n: - raise ValueError("intToBits fail: len larger than padlength.") + msg = f"{intToBits.__name__} fail: len larger than padlength." + raise ValueError(msg) bb = str(bb).rjust(n, "0") # done @@ -319,7 +324,8 @@ def signedIntToBits(i: int, n: int | None = None) -> BitArray: bb = "0" + str(bb) # always need the sign bit in front if n is not None: if len(bb) > n: - raise ValueError("signedIntToBits fail: len larger than padlength.") + msg = f"{signedIntToBits.__name__} fail: len larger than padlength." + raise ValueError(msg) bb = bb.rjust(n, "0") # was it negative? (then opposite bits) @@ -361,7 +367,8 @@ def floatsToBits(arr): bits = intToBits(31, 5) # 32 does not fit in 5 bits! for i in arr: if i < 0: - raise ValueError("Dit not implement negative floats!") + msg = "Dit not implement negative floats!" + raise ValueError(msg) i1 = int(i) i2 = i - i1 bits += intToBits(i1, 15) @@ -480,6 +487,7 @@ class SetBackgroundTag(ControlTag): """Set the color in 0-255, or 0-1 (if floats given).""" def __init__(self, *rgb): + super().__init__() self.tagtype = 9 if len(rgb) == 1: rgb = rgb[0] @@ -544,21 +552,22 @@ def __init__(self, im): # when storing RGB as ARGB). if len(im.shape) == 3: - if im.shape[2] in [3, 4]: - tmp = np.ones((im.shape[0], im.shape[1], 4), dtype=np.uint8) * 255 - for i in range(3): - tmp[:, :, i + 1] = im[:, :, i] - if im.shape[2] == 4: - tmp[:, :, 0] = im[:, :, 3] # swap channel where alpha is in - else: - raise ValueError("Invalid shape to be an image.") + if im.shape[2] not in {3, 4}: + msg = "Invalid shape to be an image." + raise ValueError(msg) + tmp = np.ones((im.shape[0], im.shape[1], 4), dtype=np.uint8) * 255 + for i in range(3): + tmp[:, :, i + 1] = im[:, :, i] + if im.shape[2] == 4: + tmp[:, :, 0] = im[:, :, 3] # swap channel where alpha is in elif len(im.shape) == 2: tmp = np.ones((im.shape[0], im.shape[1], 4), dtype=np.uint8) * 255 for i in range(3): tmp[:, :, i + 1] = im[:, :] else: - raise ValueError("Invalid shape to be an image.") + msg = "Invalid shape to be an image." + raise ValueError(msg) # we changed the image to uint8 4 channels. # now compress! @@ -772,22 +781,25 @@ def writeSwf(filename, images, duration=0.1, repeat=True): """ - # Check Numpy + # Check NumPy if np is None: - raise RuntimeError("Need Numpy to write an SWF file.") + msg = "Need NumPy to write an SWF file." + raise RuntimeError(msg) - # Check images (make all Numpy) + # Check images (make all NumPy) images2 = [] images = checkImages(images) if not images: - raise ValueError("Image list is empty!") + msg = "Image list is empty!" + raise ValueError(msg) for im in images: if PIL and isinstance(im, PIL.Image.Image): if im.mode == "P": im = im.convert() im = np.asarray(im) if len(im.shape) == 0: - raise MemoryError("Too little memory to convert PIL image to array") + msg = "Too little memory to convert PIL image to array" + raise MemoryError(msg) images2.append(im) # Init @@ -795,10 +807,11 @@ def writeSwf(filename, images, duration=0.1, repeat=True): # Check duration if hasattr(duration, "__len__"): - if len(duration) == len(images2): - duration = list(duration) - else: - raise ValueError("len(duration) doesn't match amount of images.") + if len(duration) != len(images2): + msg = "len(duration) doesn't match amount of images." + raise ValueError(msg) + duration = list(duration) + else: duration = [duration for im in images2] @@ -826,19 +839,17 @@ def writeSwf(filename, images, duration=0.1, repeat=True): taglist.append(DoActionTag("stop")) # Build file - fp = open(filename, "wb") - try: + with open(filename, "wb") as fp: buildFile(fp, taglist, nframes=nframes, framesize=wh, fps=fps) - finally: - fp.close() def _readPixels(bb, i, tagType, L1): """With pf's seed after the recordheader, reads the pixeldata.""" - # Check Numpy + # Check NumPy if np is None: - raise RuntimeError("Need Numpy to read an SWF file.") + msg = "Need NumPy to read an SWF file." + raise RuntimeError(msg) # Get info # charId = bb[i : i + 2] # unused @@ -898,83 +909,76 @@ def readSwf(filename, asNumpy=True): # Check PIL if (not asNumpy) and (PIL is None): - raise RuntimeError("Need PIL to return as PIL images.") + msg = "Need PIL to return as PIL images." + raise RuntimeError(msg) - # Check Numpy + # Check NumPy if np is None: - raise RuntimeError("Need Numpy to read SWF files.") + msg = "Need NumPy to read SWF files." + raise RuntimeError(msg) # Init images images = [] # Open file and read all - fp = open(filename, "rb") - bb = fp.read() - - try: - # Check opening tag - tmp = bb[0:3].decode("ascii", "ignore") - if tmp.upper() == "FWS": - pass # ok - elif tmp.upper() == "CWS": - # Decompress movie - bb = bb[:8] + zlib.decompress(bb[8:]) + bb = Path(filename).read_bytes() + # Check opening tag + tmp = bb[0:3].decode("ascii", "ignore") + if tmp.upper() == "FWS": + pass # ok + elif tmp.upper() == "CWS": + # Decompress movie + bb = bb[:8] + zlib.decompress(bb[8:]) + else: + raise OSError("Not a valid SWF file: " + str(filename)) + + # Set filepointer at first tag (skipping framesize RECT and two uin16's + i = 8 + nbits = bitsToInt(bb[i : i + 1], 5) # skip FrameSize + nbits = 5 + nbits * 4 + Lrect = nbits / 8.0 + if Lrect % 1: + Lrect += 1 + Lrect = int(Lrect) + i += Lrect + 4 + + # Iterate over the tags + counter = 0 + while True: + counter += 1 + + # Get tag header + head = bb[i : i + 6] + if not head: + break # Done (we missed end tag) + + # Determine type and length + T, L1, L2 = getTypeAndLen(head) + if not L2: + print("Invalid tag length, could not proceed") + break + # print(T, L2) + + # Read image if we can + if T in {20, 36}: + im = _readPixels(bb, i + 6, T, L1) + if im is not None: + images.append(im) + elif T in {6, 21, 35, 90}: + print("Ignoring JPEG image: cannot read JPEG.") else: - raise OSError("Not a valid SWF file: " + str(filename)) - - # Set filepointer at first tag (skipping framesize RECT and two uin16's - i = 8 - nbits = bitsToInt(bb[i : i + 1], 5) # skip FrameSize - nbits = 5 + nbits * 4 - Lrect = nbits / 8.0 - if Lrect % 1: - Lrect += 1 - Lrect = int(Lrect) - i += Lrect + 4 - - # Iterate over the tags - counter = 0 - while True: - counter += 1 - - # Get tag header - head = bb[i : i + 6] - if not head: - break # Done (we missed end tag) - - # Determine type and length - T, L1, L2 = getTypeAndLen(head) - if not L2: - print("Invalid tag length, could not proceed") - break - # print(T, L2) - - # Read image if we can - if T in [20, 36]: - im = _readPixels(bb, i + 6, T, L1) - if im is not None: - images.append(im) - elif T in [6, 21, 35, 90]: - print("Ignoring JPEG image: cannot read JPEG.") - else: - pass # Not an image tag - - # Detect end tag - if T == 0: - break - - # Next tag! - i += L2 + pass # Not an image tag - finally: - fp.close() + # Detect end tag + if T == 0: + break + # Next tag! + i += L2 # Convert to normal PIL images if needed if not asNumpy: images2 = images - images = [] - for im in images2: - images.append(PIL.Image.fromarray(im)) + images = [PIL.Image.fromarray(im) for im in images2] # Done return images diff --git a/python/grass/jupyter/__init__.py b/python/grass/jupyter/__init__.py index 21223c2cd47..0db59ca2ea7 100644 --- a/python/grass/jupyter/__init__.py +++ b/python/grass/jupyter/__init__.py @@ -13,7 +13,7 @@ # License (>=v2). Read the file COPYING that comes with GRASS # for details. -"""The *grass.jupyter* is a convenient GRASS GIS interface for Jupyter notebooks. +"""A convenient GRASS GIS interface for Jupyter notebooks. Python is a great tool for data science and scientific computing. Jupyter_ is an environment with computational notebooks which makes it even better tool for @@ -72,7 +72,7 @@ .. image:: https://mybinder.org/badge_logo.svg :target: - https://mybinder.org/v2/gh/OSGeo/grass/main?urlpath=lab%2Ftree%2Fdoc%2Fnotebooks%2Fjupyter_example.ipynb + https://mybinder.org/v2/gh/OSGeo/grass/main?urlpath=lab%2Ftree%2Fdoc%2Fexamples%2Fnotebooks%2Fjupyter_example.ipynb There are also internal classes and functions which are not guaranteed to have as stable API, although they are available through their specific submodules. @@ -103,7 +103,7 @@ .. _Jupyter: https://jupyter.org/ .. _wiki: https://grasswiki.osgeo.org/wiki/GRASS_GIS_Jupyter_notebooks -.. _GitHub: https://github.com/OSGeo/grass/blob/main/doc/notebooks/jupyter_example.ipynb +.. _GitHub: https://github.com/OSGeo/grass/blob/main/doc/examples/notebooks/jupyter_example.ipynb """ from .interactivemap import InteractiveMap, Raster, Vector diff --git a/python/grass/jupyter/baseseriesmap.py b/python/grass/jupyter/baseseriesmap.py index 4497b16593d..00ee394d119 100644 --- a/python/grass/jupyter/baseseriesmap.py +++ b/python/grass/jupyter/baseseriesmap.py @@ -239,9 +239,7 @@ def save( if not self._layers_rendered: self.render() - input_files = [] - for index in self._indices: - input_files.append(self._base_filename_dict[index]) + input_files = [self._base_filename_dict[index] for index in self._indices] save_gif( input_files, diff --git a/python/grass/jupyter/interactivemap.py b/python/grass/jupyter/interactivemap.py index d843ac3b6b6..bf6e1196670 100644 --- a/python/grass/jupyter/interactivemap.py +++ b/python/grass/jupyter/interactivemap.py @@ -13,6 +13,7 @@ # for details. """Interactive visualizations map with folium or ipyleaflet""" + import os import base64 import json @@ -503,9 +504,7 @@ class InteractiveRegionController: changed_region (dict): The dictionary to store the changed region. """ - def __init__( - self, map_object, ipyleaflet, ipywidgets, **kwargs - ): # pylint: disable=unused-argument + def __init__(self, map_object, ipyleaflet, ipywidgets, **kwargs): # pylint: disable=unused-argument """Initializes the InteractiveRegionController. :param map_object: The map object. diff --git a/python/grass/jupyter/map.py b/python/grass/jupyter/map.py index 013ff693470..3b61372626d 100644 --- a/python/grass/jupyter/map.py +++ b/python/grass/jupyter/map.py @@ -158,12 +158,12 @@ def run(self, module, **kwargs): :param `**kwargs`: named arguments passed to run_command()""" # Check module is from display library then run - if module[0] == "d": - self._region_manager.set_region_from_command(module, **kwargs) - self._region_manager.adjust_rendering_size_from_region() - gs.run_command(module, env=self._env, **kwargs) - else: - raise ValueError("Module must begin with letter 'd'.") + if module[0] != "d": + msg = "Module must begin with letter 'd'." + raise ValueError(msg) + self._region_manager.set_region_from_command(module, **kwargs) + self._region_manager.adjust_rendering_size_from_region() + gs.run_command(module, env=self._env, **kwargs) def __getattr__(self, name): """Parse attribute to GRASS display module. Attribute should be in diff --git a/python/grass/jupyter/map3d.py b/python/grass/jupyter/map3d.py index 99253251406..b6b49dcaf0f 100644 --- a/python/grass/jupyter/map3d.py +++ b/python/grass/jupyter/map3d.py @@ -12,6 +12,7 @@ # for details. """Render 3D visualizations""" + from __future__ import annotations import os diff --git a/python/grass/jupyter/reprojection_renderer.py b/python/grass/jupyter/reprojection_renderer.py index d709b9e123c..db1dfd6b06f 100644 --- a/python/grass/jupyter/reprojection_renderer.py +++ b/python/grass/jupyter/reprojection_renderer.py @@ -12,7 +12,7 @@ # for details. """Reprojects rasters to Pseudo-Mercator and vectors to WGS84. Exports reprojected - rasters and vectors to PNGs and geoJSONs, respectively.""" +rasters and vectors to PNGs and geoJSONs, respectively.""" import os import tempfile diff --git a/python/grass/jupyter/seriesmap.py b/python/grass/jupyter/seriesmap.py index ae6bde911b6..aaed821d1eb 100644 --- a/python/grass/jupyter/seriesmap.py +++ b/python/grass/jupyter/seriesmap.py @@ -158,9 +158,10 @@ def render(self): (i.e. show or save). """ if not self._baseseries_added: - raise RuntimeError( + msg = ( "Cannot render series since none has been added." "Use SeriesMap.add_rasters() or SeriesMap.add_vectors()" ) + raise RuntimeError(msg) tasks = [(i,) for i in range(self.baseseries)] self._render(tasks) diff --git a/python/grass/jupyter/setup.py b/python/grass/jupyter/setup.py index 0ed64739039..0d7edec9767 100644 --- a/python/grass/jupyter/setup.py +++ b/python/grass/jupyter/setup.py @@ -87,8 +87,8 @@ def switch_mapset(self, path, location=None, mapset=None): gisenv = gs.gisenv() if ( not location - and not mapset - and len(Path(path).parts) == 1 + and (not mapset) + and (len(Path(path).parts) == 1) and mapset_exists( path=gisenv["GISDBASE"], location=gisenv["LOCATION_NAME"], mapset=path ) diff --git a/python/grass/jupyter/timeseriesmap.py b/python/grass/jupyter/timeseriesmap.py index 2acfa575126..5d32cc83e93 100644 --- a/python/grass/jupyter/timeseriesmap.py +++ b/python/grass/jupyter/timeseriesmap.py @@ -28,8 +28,6 @@ def fill_none_values(names): for i, name in enumerate(names): if name == "None": names[i] = names[i - 1] - else: - pass return names @@ -170,7 +168,8 @@ def add_raster_series(self, baseseries, fill_gaps=False): :param bool fill_gaps: fill empty time steps with data from previous step """ if self._baseseries_added and self.baseseries != baseseries: - raise AttributeError("Cannot add more than one space time dataset") + msg = "Cannot add more than one space time dataset" + raise AttributeError(msg) self._element_type = "strds" check_timeseries_exists(baseseries, self._element_type) self.baseseries = baseseries @@ -193,7 +192,8 @@ def add_vector_series(self, baseseries, fill_gaps=False): :param bool fill_gaps: fill empty time steps with data from previous step """ if self._baseseries_added and self.baseseries != baseseries: - raise AttributeError("Cannot add more than one space time dataset") + msg = "Cannot add more than one space time dataset" + raise AttributeError(msg) self._element_type = "stvds" check_timeseries_exists(baseseries, self._element_type) self.baseseries = baseseries @@ -293,11 +293,12 @@ def _render_worker(self, date, layer, filename): def render(self): """Renders image for each time-step in space-time dataset.""" if not self._baseseries_added: - raise RuntimeError( + msg = ( "Cannot render space time dataset since none has been added." " Use TimeSeriesMap.add_raster_series() or " "TimeSeriesMap.add_vector_series() to add dataset" ) + raise RuntimeError(msg) # Create name for empty layers random_name_none = gs.append_random("none", 8) + ".png" diff --git a/python/grass/jupyter/utils.py b/python/grass/jupyter/utils.py index b06553d2d3b..518cd9c2fa0 100644 --- a/python/grass/jupyter/utils.py +++ b/python/grass/jupyter/utils.py @@ -11,6 +11,8 @@ # for details. """Utility functions warpping existing processes in a suitable way""" + +from collections.abc import Mapping import tempfile import json import os @@ -303,17 +305,18 @@ def query_vector(coord, vector_list, distance): return _style_table(final_output) -def estimate_resolution(raster, mapset, location, dbase, env): +def estimate_resolution( + raster: str, mapset: str, location: str, dbase: str, env: Mapping +) -> float: """Estimates resolution of reprojected raster. - :param str raster: name of raster - :param str mapset: mapset of raster - :param str location: name of source location - :param str dbase: path to source database + :param raster: name of raster + :param mapset: mapset of raster + :param location: name of source location + :param dbase: path to source database :param dict env: target environment - :return float estimate: estimated resolution of raster in destination - environment + :return estimate: estimated resolution of raster in destination environment """ output = gs.read_command( "r.proj", @@ -326,9 +329,9 @@ def estimate_resolution(raster, mapset, location, dbase, env): ).strip() params = gs.parse_key_val(output, vsep=" ") output = gs.read_command("g.region", flags="ug", env=env, **params) - output = gs.parse_key_val(output, val_type=float) - cell_ns = (output["n"] - output["s"]) / output["rows"] - cell_ew = (output["e"] - output["w"]) / output["cols"] + keyval = gs.parse_key_val(output, val_type=float) + cell_ns = (keyval["n"] - keyval["s"]) / keyval["rows"] + cell_ew = (keyval["e"] - keyval["w"]) / keyval["cols"] return (cell_ew + cell_ns) / 2.0 diff --git a/python/grass/pydispatch/errors.py b/python/grass/pydispatch/errors.py index 60fd4c67f8f..80caabdee21 100644 --- a/python/grass/pydispatch/errors.py +++ b/python/grass/pydispatch/errors.py @@ -1,5 +1,4 @@ -"""Error types for dispatcher mechanism -""" +"""Error types for dispatcher mechanism""" class DispatcherError(Exception): diff --git a/python/grass/pydispatch/saferef.py b/python/grass/pydispatch/saferef.py index 90769c46b36..bbbd7dd518d 100644 --- a/python/grass/pydispatch/saferef.py +++ b/python/grass/pydispatch/saferef.py @@ -164,12 +164,6 @@ def __nonzero__(self): __bool__ = __nonzero__ - def __cmp__(self, other): - """Compare with another reference""" - if not isinstance(other, self.__class__): - return cmp(self.__class__, type(other)) - return cmp(self.key, other.key) - def __call__(self): """Return a strong reference to the bound method diff --git a/python/grass/pydispatch/signal.py b/python/grass/pydispatch/signal.py index 51808e01ee3..12e73e71882 100644 --- a/python/grass/pydispatch/signal.py +++ b/python/grass/pydispatch/signal.py @@ -266,8 +266,7 @@ def __call__(self, *args, **kwargs): Traceback (most recent call last): TypeError: mywrite() takes exactly 1 argument (0 given) """ - if "signal" in kwargs: - del kwargs["signal"] + kwargs.pop("signal", None) self.emit(*args, **kwargs) diff --git a/python/grass/pygrass/gis/__init__.py b/python/grass/pygrass/gis/__init__.py index 29fecd3d699..b905626a0f8 100644 --- a/python/grass/pygrass/gis/__init__.py +++ b/python/grass/pygrass/gis/__init__.py @@ -113,9 +113,11 @@ def make_mapset(mapset, location=None, gisdbase=None): :type gisdbase: str""" res = libgis.G_make_mapset(gisdbase, location, mapset) if res == -1: - raise GrassError("Cannot create new mapset") + msg = "Cannot create new mapset" + raise GrassError(msg) if res == -2: - raise GrassError("Illegal name") + msg = "Illegal name" + raise GrassError(msg) class Gisdbase: @@ -171,7 +173,8 @@ def __iter__(self): # TODO remove or complete this function def new_location(self): if libgis.G_make_location() != 0: - raise GrassError("Cannot create new location") + msg = "Cannot create new location" + raise GrassError(msg) def locations(self): """Return a list of locations that are available in the gisdbase: :: @@ -269,7 +272,7 @@ def mapsets(self, pattern=None, permissions=True): [...] """ - mapsets = [mapset for mapset in self] # noqa: C416 + mapsets = [mapset for mapset in self] # noqa: C416 # pylint: disable=R1721 if permissions: mapsets = [ mapset @@ -380,12 +383,11 @@ def glist(self, type, pattern=None): elist = [] for el in clist: el_name = ct.cast(el, ct.c_char_p).value - if el_name: - elist.append(decode(el_name)) - else: + if not el_name: if pattern: return fnmatch.filter(elist, pattern) return elist + elist.append(decode(el_name)) def is_current(self): """Check if the MAPSET is the working MAPSET""" @@ -402,7 +404,8 @@ def current(self): def delete(self): """Delete the mapset""" if self.is_current(): - raise GrassError("The mapset is in use.") + msg = "The mapset is in use." + raise GrassError(msg) shutil.rmtree(self.path()) def path(self): @@ -455,11 +458,11 @@ def add(self, mapset): :param mapset: a mapset's name :type mapset: str """ - if mapset not in self.read() and mapset in self.location: - with open(self.spath, "a+") as f: - f.write("%s\n" % mapset) - else: - raise TypeError("Mapset not found") + if mapset in self.read() or mapset not in self.location: + msg = "Mapset not found" + raise TypeError(msg) + with open(self.spath, "a+") as f: + f.write("%s\n" % mapset) def remove(self, mapset): """Remove mapset to the search path @@ -501,10 +504,11 @@ def reset(self): doctest.testmod() - # Remove the generated vector map, if exist mset = utils.get_mapset_vector(test_vector_name, mapset="") if mset: + # Remove the generated vector map, if exists run_command("g.remove", flags="f", type="vector", name=test_vector_name) mset = utils.get_mapset_raster(test_raster_name, mapset="") if mset: + # Remove the generated raster map, if exists run_command("g.remove", flags="f", type="raster", name=test_raster_name) diff --git a/python/grass/pygrass/gis/region.py b/python/grass/pygrass/gis/region.py index 892a3664b49..b1544c13664 100644 --- a/python/grass/pygrass/gis/region.py +++ b/python/grass/pygrass/gis/region.py @@ -448,7 +448,8 @@ def from_rast(self, raster_name): .. """ if not raster_name: - raise ValueError("Raster name or mapset are invalid") + msg = "Raster name or mapset are invalid" + raise ValueError(msg) mapset = get_mapset_raster(raster_name) @@ -602,7 +603,8 @@ def write(self): """ self.adjust() if libgis.G_put_window(self.byref()) < 0: - raise GrassError("Cannot change region (WIND file).") + msg = "Cannot change region (WIND file)." + raise GrassError(msg) def read_default(self): """ @@ -672,7 +674,7 @@ def set_bbox(self, bbox): doctest.testmod() - """Remove the generated vector map, if exist""" + # Remove the generated vector map, if exists mset = utils.get_mapset_vector(test_vector_name, mapset="") if mset: run_command("g.remove", flags="f", type="vector", name=test_vector_name) diff --git a/python/grass/pygrass/modules/grid/grid.py b/python/grass/pygrass/modules/grid/grid.py index cc3f406d72d..be2c2c971c2 100644 --- a/python/grass/pygrass/modules/grid/grid.py +++ b/python/grass/pygrass/modules/grid/grid.py @@ -163,8 +163,7 @@ def copy_groups(groups, gisrc_src, gisrc_dst, region=None): :param gisrc_dst: path of the GISRC file where the groups will be created :type gisrc_dst: str :param region: a region like object or a dictionary with the region - parameters that will be used to crop the rasters of the - groups + parameters that will be used to crop the rasters of the groups :type region: Region object or dictionary :returns: None @@ -204,8 +203,7 @@ def set_region(region, gisrc_src, gisrc_dst, env): """Set a region into two different mapsets. :param region: a region like object or a dictionary with the region - parameters that will be used to crop the rasters of the - groups + parameters that will be used to crop the rasters of the groups :type region: Region object or dictionary :param gisrc_src: path of the GISRC file from where we want to copy the groups :type gisrc_src: str @@ -238,8 +236,7 @@ def copy_rasters(rasters, gisrc_src, gisrc_dst, region=None): :param gisrc_dst: path of the GISRC file where the groups will be created :type gisrc_dst: str :param region: a region like object or a dictionary with the region - parameters that will be used to crop the rasters of the - groups + parameters that will be used to crop the rasters of the groups :type region: Region object or dictionary :returns: None """ @@ -319,12 +316,10 @@ def get_cmd(cmdd): >>> get_cmd(slp.get_dict()) # doctest: +ELLIPSIS ['r.slope.aspect', 'elevation=ele', 'format=degrees', ..., '--o'] """ - cmd = [ + return [ cmdd["name"], - ] - cmd.extend(("%s=%s" % (k, v) for k, v in cmdd["inputs"] if not isinstance(v, list))) - cmd.extend( - ( + *("%s=%s" % (k, v) for k, v in cmdd["inputs"] if not isinstance(v, list)), + *( "%s=%s" % ( k, @@ -332,21 +327,16 @@ def get_cmd(cmdd): ) for k, vals in cmdd["inputs"] if isinstance(vals, list) - ) - ) - cmd.extend( - ("%s=%s" % (k, v) for k, v in cmdd["outputs"] if not isinstance(v, list)) - ) - cmd.extend( - ( + ), + *("%s=%s" % (k, v) for k, v in cmdd["outputs"] if not isinstance(v, list)), + *( "%s=%s" % (k, ",".join([repr(v) for v in vals])) for k, vals in cmdd["outputs"] if isinstance(vals, list) - ) - ) - cmd.extend(f"-{flg}" for flg in cmdd["flags"] if len(flg) == 1) - cmd.extend(f"--{flg[0]}" for flg in cmdd["flags"] if len(flg) > 1) - return cmd + ), + *(f"-{flg}" for flg in cmdd["flags"] if len(flg) == 1), + *(f"--{flg[0]}" for flg in cmdd["flags"] if len(flg) > 1), + ] def cmd_exe(args): @@ -385,10 +375,7 @@ def cmd_exe(args): sub.Popen(["g.region", "raster=%s" % key], shell=shell, env=env).wait() else: # set the computational region - lcmd = [ - "g.region", - ] - lcmd.extend(["%s=%s" % (k, v) for k, v in bbox.items()]) + lcmd = ["g.region", *["%s=%s" % (k, v) for k, v in bbox.items()]] sub.Popen(lcmd, shell=shell, env=env).wait() if groups: copy_groups(groups, gisrc_src, gisrc_dst) @@ -411,7 +398,7 @@ class GridModule: :param overlap: overlap between tiles, in pixel. :type overlap: int :param processes: number of threads, default value is equal to the number - of processor available. + of processors available. :param split: if True use r.tile to split all the inputs. :type split: bool :param mapset_prefix: if specified created mapsets start with this prefix @@ -597,13 +584,17 @@ def get_works(self): if self.inlist: inms = {} cols = len(box_row) + + indx = row * cols + col for key in self.inlist: - indx = row * cols + col inms[key] = "%s@%s" % (self.inlist[key][indx], self.mset.name) # set the computational region, prepare the region parameters - bbox = {k[0]: str(v) for k, v in box.items()[:-2]} - bbox["nsres"] = "%f" % reg.nsres - bbox["ewres"] = "%f" % reg.ewres + bbox = { + **{k[0]: str(v) for k, v in box.items()[:-2]}, + "nsres": "%f" % reg.nsres, + "ewres": "%f" % reg.ewres, + } + new_mset = ( self.msetstr % (self.start_row + row, self.start_col + col), ) diff --git a/python/grass/pygrass/modules/grid/split.py b/python/grass/pygrass/modules/grid/split.py index cac604327d8..17aa4905467 100644 --- a/python/grass/pygrass/modules/grid/split.py +++ b/python/grass/pygrass/modules/grid/split.py @@ -99,10 +99,9 @@ def split_region_in_overlapping_tiles(region=None, width=100, height=100, overla box_list = [] # print reg for row in range(nrows): - row_list = [] - for col in range(ncols): - # print 'c', c, 'r', r - row_list.append(get_bbox(reg, row, col, width, height, overlap)) + row_list = [ + get_bbox(reg, row, col, width, height, overlap) for col in range(ncols) + ] box_list.append(row_list) return box_list @@ -123,9 +122,10 @@ def split_region_tiles(region=None, width=100, height=100): nrows = (reg.rows + height - 1) // height box_list = [] for row in range(nrows): - row_list = [] - for col in range(ncols): - row_list.append(get_tile_start_end_row_col(reg, row, col, width, height)) + row_list = [ + get_tile_start_end_row_col(reg, row, col, width, height) + for col in range(ncols) + ] box_list.append(row_list) return box_list @@ -146,11 +146,9 @@ def get_overlap_region_tiles(region=None, width=100, height=100, overlap=0): ncols = (reg.cols + width - 1) // width nrows = (reg.rows + height - 1) // height box_list = [] - # print reg for row in range(nrows): - row_list = [] - for col in range(ncols): - # print 'c', c, 'r', r - row_list.append(get_bbox(reg, row, col, width, height, -overlap)) + row_list = [ + get_bbox(reg, row, col, width, height, -overlap) for col in range(ncols) + ] box_list.append(row_list) return box_list diff --git a/python/grass/pygrass/modules/interface/docstring.py b/python/grass/pygrass/modules/interface/docstring.py index 397338fcbb8..86c556559bf 100644 --- a/python/grass/pygrass/modules/interface/docstring.py +++ b/python/grass/pygrass/modules/interface/docstring.py @@ -47,7 +47,9 @@ def __get__(self, obj, type=None): return self.fget(obj) def __set__(self, obj, value): - raise AttributeError("can't set attribute") + msg = "Can't set attribute" + raise AttributeError(msg) def __delete__(self, obj): - raise AttributeError("can't delete attribute") + msg = "Can't delete attribute" + raise AttributeError(msg) diff --git a/python/grass/pygrass/modules/interface/env.py b/python/grass/pygrass/modules/interface/env.py index d3d6e371238..e355a5af30e 100644 --- a/python/grass/pygrass/modules/interface/env.py +++ b/python/grass/pygrass/modules/interface/env.py @@ -12,7 +12,8 @@ def get_env(): """Parse the GISRC file and return the GRASS variales""" gisrc = os.environ.get("GISRC") if gisrc is None: - raise RuntimeError("You are not in a GRASS session, GISRC not found.") + msg = "You are not in a GRASS session, GISRC not found." + raise RuntimeError(msg) with open(gisrc) as grc: return { k.strip(): v.strip() for k, v in [row.split(":", 1) for row in grc if row] diff --git a/python/grass/pygrass/modules/interface/flag.py b/python/grass/pygrass/modules/interface/flag.py index b6f00fdb744..c4014eb0b93 100644 --- a/python/grass/pygrass/modules/interface/flag.py +++ b/python/grass/pygrass/modules/interface/flag.py @@ -49,11 +49,11 @@ def get_bash(self): >>> flag.get_bash() '--o' """ - if self.value: - if self.special: - return "--%s" % self.name[0] - return "-%s" % self.name - return "" + if not self.value: + return "" + if self.special: + return "--%s" % self.name[0] + return "-%s" % self.name def get_python(self): """Return the python representation of a flag. diff --git a/python/grass/pygrass/modules/interface/module.py b/python/grass/pygrass/modules/interface/module.py index 7f70d82bac1..723ea5b5224 100644 --- a/python/grass/pygrass/modules/interface/module.py +++ b/python/grass/pygrass/modules/interface/module.py @@ -545,10 +545,10 @@ def f(*args, **kargs): """ # noqa: E501 def __init__(self, cmd, *args, **kargs): - if isinstance(cmd, str): - self.name = cmd - else: - raise GrassError("Problem initializing the module {s}".format(s=cmd)) + if not isinstance(cmd, str): + msg = "Problem initializing the module {s}".format(s=cmd) + raise GrassError(msg) + self.name = cmd try: # call the command with --interface-description get_cmd_xml = Popen([cmd, "--interface-description"], stdout=PIPE) @@ -637,17 +637,13 @@ def __call__(self, *args, **kargs): self.update(*args, **kargs) - # # check if execute - # - if self.run_: - # - # check reqire parameters - # - if self.check_: - self.check() - return self.run() - return self + if not self.run_: + return self + # check required parameters + if self.check_: + self.check() + return self.run() def update(self, *args, **kargs): """Update module parameters and selected object attributes. @@ -773,12 +769,12 @@ def get_dict(self): """Return a dictionary that includes the name, all valid inputs, outputs and flags """ - dic = {} - dic["name"] = self.name - dic["inputs"] = [(k, v.value) for k, v in self.inputs.items() if v.value] - dic["outputs"] = [(k, v.value) for k, v in self.outputs.items() if v.value] - dic["flags"] = [flg for flg in self.flags if self.flags[flg].value] - return dic + return { + "name": self.name, + "inputs": [(k, v.value) for k, v in self.inputs.items() if v.value], + "outputs": [(k, v.value) for k, v in self.outputs.items() if v.value], + "flags": [flg for flg in self.flags if self.flags[flg].value], + } def make_cmd(self): """Create the command string that can be executed in a shell diff --git a/python/grass/pygrass/modules/interface/parameter.py b/python/grass/pygrass/modules/interface/parameter.py index d950cd40008..78508a60c74 100644 --- a/python/grass/pygrass/modules/interface/parameter.py +++ b/python/grass/pygrass/modules/interface/parameter.py @@ -55,15 +55,14 @@ def check_string(value): if isinstance(value, tuple) else (value, value) ) - if param.multiple: - # everything looks fine, so check each value - try: - return [param.type(check_string(val)) for val in value], value - except Exception as exc: - raiseexcpet(exc, param, param.type, value) - else: + if not param.multiple: msg = "The Parameter <%s> does not accept multiple inputs" raise TypeError(msg % param.name) + # everything looks fine, so check each value + try: + return ([param.type(check_string(val)) for val in value], value) + except Exception as exc: + raiseexcpet(exc, param, param.type, value) if param.keydescvalues: msg = "The Parameter <%s> require multiple inputs in the form: %s" @@ -112,10 +111,11 @@ def check_string(value): good = True break if not good: - raise ValueError( - f"The Parameter <{param.name}>, must be one of the following " + msg = ( + f"The parameter <{param.name}>, must be one of the following " f"values: {param.values!r} not '{newvalue}'" ) + raise ValueError(msg) return ( ( [ @@ -148,7 +148,7 @@ class Parameter: >>> param.value = 3 Traceback (most recent call last): ... - ValueError: The Parameter , must be one of the following values: [2, 4, 6, 8] not '3' + ValueError: The parameter , must be one of the following values: [2, 4, 6, 8] not '3' ... """ # noqa: E501 @@ -160,16 +160,16 @@ def __init__(self, xparameter=None, diz=None): self.max = None diz = element2dict(xparameter) if xparameter is not None else diz if diz is None: - raise TypeError("Xparameter or diz are required") + msg = "xparameter or diz are required" + raise TypeError(msg) self.name = diz["name"] self.required = diz["required"] == "yes" self.multiple = diz["multiple"] == "yes" # check the type - if diz["type"] in GETTYPE: - self.type = GETTYPE[diz["type"]] - self.typedesc = diz["type"] - else: + if diz["type"] not in GETTYPE: raise TypeError("New type: %s, ignored" % diz["type"]) + self.type = GETTYPE[diz["type"]] + self.typedesc = diz["type"] self.description = diz.get("description", None) self.keydesc, self.keydescvalues = diz.get("keydesc", (None, None)) diff --git a/python/grass/pygrass/modules/interface/typedict.py b/python/grass/pygrass/modules/interface/typedict.py index fb3ddb368eb..dd6eb731993 100644 --- a/python/grass/pygrass/modules/interface/typedict.py +++ b/python/grass/pygrass/modules/interface/typedict.py @@ -30,11 +30,10 @@ def __dir__(self): return self.keys() def __setitem__(self, key, value): - if isinstance(value, self._type): - super().__setitem__(key, value) - else: + if not isinstance(value, self._type): str_err = "The value: %r is not a %s instance." raise TypeError(str_err % (value, self._type.__name__)) + super().__setitem__(key, value) @docstring_property(__doc__) def __doc__(self): @@ -62,8 +61,4 @@ def __reduce__(self): ) def used(self): - key_dict = {} - for key in self: - if getattr(self, key): - key_dict[key] = getattr(self, key) - return key_dict + return {key: getattr(self, key) for key in self if getattr(self, key)} diff --git a/python/grass/pygrass/raster/__init__.py b/python/grass/pygrass/raster/__init__.py index 9967d62d98e..8a21a5a2466 100644 --- a/python/grass/pygrass/raster/__init__.py +++ b/python/grass/pygrass/raster/__init__.py @@ -133,9 +133,6 @@ class RasterRow(RasterAbstractBase): """ - def __init__(self, name, mapset="", *args, **kargs): - super().__init__(name, mapset, *args, **kargs) - # mode = "r", method = "row", @must_be_open def get_row(self, row, row_buffer=None): @@ -195,17 +192,16 @@ def open(self, mode=None, mtype=None, overwrite=None): self.overwrite = overwrite if overwrite is not None else self.overwrite if self.mode == "r": - if self.exist(): - self.info.read() - self.cats.mtype = self.mtype - self.cats.read() - self.hist.read() - self._fd = libraster.Rast_open_old(self.name, self.mapset) - self._gtype = libraster.Rast_get_map_type(self._fd) - self.mtype = RTYPE_STR[self._gtype] - else: + if not self.exist(): str_err = _("The map does not exist, I can't open in 'r' mode") raise OpenError(str_err) + self.info.read() + self.cats.mtype = self.mtype + self.cats.read() + self.hist.read() + self._fd = libraster.Rast_open_old(self.name, self.mapset) + self._gtype = libraster.Rast_get_map_type(self._fd) + self.mtype = RTYPE_STR[self._gtype] elif self.mode == "w": if self.exist(): if not self.overwrite: @@ -217,7 +213,8 @@ def open(self, mode=None, mtype=None, overwrite=None): raise OpenError(_("Raster type not defined")) self._fd = libraster.Rast_open_new(self.name, self._gtype) else: - raise OpenError("Open mode: %r not supported, valid mode are: r, w") + msg = "Open mode: %r not supported, valid mode are: r, w" + raise OpenError(msg) # read rows and cols from the active region self._rows = libraster.Rast_window_rows() self._cols = libraster.Rast_window_cols() @@ -339,7 +336,8 @@ def __setitem__(self, key, row): if key >= self._rows: raise IndexError(_("Index out of range: %r.") % key) return self.put_row(key, row) - raise TypeError("Invalid argument type.") + msg = "Invalid argument type." + raise TypeError(msg) @must_be_open def map2segment(self): @@ -746,10 +744,11 @@ def numpy2raster(array, mtype, rastname, overwrite=False): doctest.testmod() - """Remove the generated vector map, if exist""" mset = utils.get_mapset_raster(test_raster_name, mapset="") if mset: + # Remove the generated vector map, if exists Module("g.remove", flags="f", type="raster", name=test_raster_name) mset = utils.get_mapset_raster(test_raster_name + "_segment", mapset="") if mset: + # Remove the generated raster map, if exists Module("g.remove", flags="f", type="raster", name=test_raster_name + "_segment") diff --git a/python/grass/pygrass/raster/abstract.py b/python/grass/pygrass/raster/abstract.py index 68b5e501ed4..d5db9bb3b6e 100644 --- a/python/grass/pygrass/raster/abstract.py +++ b/python/grass/pygrass/raster/abstract.py @@ -398,15 +398,15 @@ def __getitem__(self, key): return self.get(x, y) if isinstance(key, int): if not self.is_open(): - raise IndexError("Can not operate on a closed map. Call open() first.") + msg = "Can not operate on a closed map. Call open() first." + raise IndexError(msg) if key < 0: # Handle negative indices key += self._rows if key >= self._rows: - raise IndexError( - "The row index {0} is out of range [0, {1}).".format( - key, self._rows - ) + msg = "The row index {0} is out of range [0, {1}).".format( + key, self._rows ) + raise IndexError(msg) return self.get_row(key) fatal("Invalid argument type.") @@ -427,13 +427,13 @@ def exist(self): >>> ele.exist() True """ - if self.name: - if self.mapset == "": - mapset = utils.get_mapset_raster(self.name, self.mapset) - self.mapset = mapset or "" - return bool(mapset) - return bool(utils.get_mapset_raster(self.name, self.mapset)) - return False + if not self.name: + return False + if self.mapset == "": + mapset = utils.get_mapset_raster(self.name, self.mapset) + self.mapset = mapset or "" + return bool(mapset) + return bool(utils.get_mapset_raster(self.name, self.mapset)) def is_open(self): """Return True if the map is open False otherwise. @@ -631,7 +631,7 @@ def set_cat(self, label, min_cat, max_cat=None, index=None): doctest.testmod() - """Remove the generated vector map, if exist""" mset = utils.get_mapset_raster(test_raster_name, mapset="") if mset: + # Remove the generated vector map, if exists Module("g.remove", flags="f", type="raster", name=test_raster_name) diff --git a/python/grass/pygrass/raster/buffer.py b/python/grass/pygrass/raster/buffer.py index 0e34ac84159..67ba86563bc 100644 --- a/python/grass/pygrass/raster/buffer.py +++ b/python/grass/pygrass/raster/buffer.py @@ -45,8 +45,8 @@ def __array_finalize__(self, obj): def __array_wrap__(self, out_arr, context=None): """See: - http://docs.scipy.org/doc/numpy/user/ - basics.subclassing.html#array-wrap-for-ufuncs""" + https://numpy.org/doc/stable/user/basics.subclassing.html#array-wrap-for-ufuncs-and-other-functions + """ if out_arr.dtype == bool: # there is not support for boolean maps, so convert into integer out_arr = out_arr.astype(np.int32) diff --git a/python/grass/pygrass/raster/category.py b/python/grass/pygrass/raster/category.py index 9ace632d136..b151fef0976 100644 --- a/python/grass/pygrass/raster/category.py +++ b/python/grass/pygrass/raster/category.py @@ -115,15 +115,16 @@ def _chk_index(self, index): return index def _chk_value(self, value): - if isinstance(value, tuple): - length = len(value) - if length == 2: - label, min_cat = value - value = (label, min_cat, None) - elif length < 2 or length > 3: - raise TypeError("Tuple with a length that is not supported.") - else: - raise TypeError("Only Tuple are supported.") + if not isinstance(value, tuple): + msg = "Only tuples are supported." + raise TypeError(msg) + length = len(value) + if length == 2: + label, min_cat = value + value = (label, min_cat, None) + elif length < 2 or length > 3: + msg = "Tuple with a length that is not supported." + raise TypeError(msg) return value def __getitem__(self, index): @@ -204,7 +205,8 @@ def set_cat(self, index, value): elif index < (len(self)): self[index] = value else: - raise TypeError("Index outside range.") + msg = "Index outside range." + raise TypeError(msg) def reset(self): for i in range(len(self) - 1, -1, -1): @@ -244,7 +246,8 @@ def read(self): self.name, self.mapset, ctypes.byref(self.c_cats) ) if err == -1: - raise GrassError("Can not read the categories.") + msg = "Can not read the categories." + raise GrassError(msg) # copy from C struct to list self._read_cats() @@ -268,8 +271,9 @@ def copy(self, category): :type category: Category object """ libraster.Rast_copy_cats( - ctypes.byref(self.c_cats), ctypes.byref(category.c_cats) # to - ) # from + ctypes.byref(self.c_cats), # to + ctypes.byref(category.c_cats), # from + ) self._read_cats() def ncats(self): @@ -280,7 +284,8 @@ def set_cats_fmt(self, fmt, m1, a1, m2, a2): void Rast_set_cats_fmt() """ # TODO: add - raise ImplementationError("set_cats_fmt() is not implemented yet.") + msg = f"{self.set_cats_fmt.__name__}() is not implemented yet." + raise ImplementationError(msg) def read_rules(self, filename, sep=":"): """Copy categories from a rules file, default separator is ':', the @@ -308,7 +313,8 @@ def read_rules(self, filename, sep=":"): elif len(cat) == 3: label, min_cat, max_cat = cat else: - raise TypeError("Row length is greater than 3") + msg = "Row length is greater than 3" + raise TypeError(msg) self.append((label, min_cat, max_cat)) def write_rules(self, filename, sep=":"): diff --git a/python/grass/pygrass/raster/rowio.py b/python/grass/pygrass/raster/rowio.py index ed43c9a8452..a49da7efe26 100644 --- a/python/grass/pygrass/raster/rowio.py +++ b/python/grass/pygrass/raster/rowio.py @@ -64,7 +64,8 @@ def open(self, fd, rows, cols, mtype): ) == -1 ): - raise GrassError("Fatal error, Rowio not setup correctly.") + msg = f"Fatal error, {RowIO.__name__} not setup correctly." + raise GrassError(msg) def release(self): librowio.Rowio_release(ctypes.byref(self.c_rowio)) diff --git a/python/grass/pygrass/raster/testsuite/test_category.py b/python/grass/pygrass/raster/testsuite/test_category.py index 113264c82f6..a00525a4108 100644 --- a/python/grass/pygrass/raster/testsuite/test_category.py +++ b/python/grass/pygrass/raster/testsuite/test_category.py @@ -54,7 +54,7 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): - """Remove the generated vector map, if exist""" + """Remove the generated vector map, if exists""" cls.runModule("g.remove", flags="f", type="raster", name=cls.name) cls.del_temp_region() diff --git a/python/grass/pygrass/raster/testsuite/test_history.py b/python/grass/pygrass/raster/testsuite/test_history.py index c369ac8da02..4ec01fb3103 100644 --- a/python/grass/pygrass/raster/testsuite/test_history.py +++ b/python/grass/pygrass/raster/testsuite/test_history.py @@ -35,7 +35,7 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): - """Remove the generated vector map, if exist""" + """Remove the generated vector map, if exists""" cls.runModule("g.remove", flags="f", type="raster", name=cls.name) cls.del_temp_region() diff --git a/python/grass/pygrass/raster/testsuite/test_numpy.py b/python/grass/pygrass/raster/testsuite/test_numpy.py index b23926d8ba7..fb83ab61493 100644 --- a/python/grass/pygrass/raster/testsuite/test_numpy.py +++ b/python/grass/pygrass/raster/testsuite/test_numpy.py @@ -38,7 +38,7 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): - """Remove the generated vector map, if exist""" + """Remove the generated vector map, if exists""" cls.runModule("g.remove", flags="f", type="raster", name=cls.name) cls.del_temp_region() diff --git a/python/grass/pygrass/raster/testsuite/test_pygrass_raster.py b/python/grass/pygrass/raster/testsuite/test_pygrass_raster.py index 2f20de582f4..393ff42fc6f 100644 --- a/python/grass/pygrass/raster/testsuite/test_pygrass_raster.py +++ b/python/grass/pygrass/raster/testsuite/test_pygrass_raster.py @@ -21,7 +21,7 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): - """Remove the generated vector map, if exist""" + """Remove the generated vector map, if exists""" cls.runModule("g.remove", flags="f", type="raster", name=cls.name) cls.del_temp_region() diff --git a/python/grass/pygrass/raster/testsuite/test_raster_img.py b/python/grass/pygrass/raster/testsuite/test_raster_img.py index b873b16a86b..8781dd20d46 100644 --- a/python/grass/pygrass/raster/testsuite/test_raster_img.py +++ b/python/grass/pygrass/raster/testsuite/test_raster_img.py @@ -37,7 +37,7 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): - """Remove the generated vector map, if exist""" + """Remove the generated vector map, if exists""" cls.runModule("g.remove", flags="f", type="raster", name=cls.name) cls.del_temp_region() diff --git a/python/grass/pygrass/raster/testsuite/test_raster_region.py b/python/grass/pygrass/raster/testsuite/test_raster_region.py index cb8cc8c30c8..259c16c11f7 100644 --- a/python/grass/pygrass/raster/testsuite/test_raster_region.py +++ b/python/grass/pygrass/raster/testsuite/test_raster_region.py @@ -22,7 +22,7 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): - """Remove the generated vector map, if exist""" + """Remove the generated vector map, if exists""" cls.runModule("g.remove", flags="f", type="raster", name=cls.name) cls.del_temp_region() @@ -65,16 +65,14 @@ def test_resampling_2(self): rast.set_region(region) rast.open(mode="r") - """ - [nan, nan, nan, nan, nan, nan, nan, nan] - [nan, nan, nan, nan, nan, nan, nan, nan] - [nan, nan, 11.0, 21.0, 31.0, 41.0, nan, nan] - [nan, nan, 12.0, 22.0, 32.0, 42.0, nan, nan] - [nan, nan, 13.0, 23.0, 33.0, 43.0, nan, nan] - [nan, nan, 14.0, 24.0, 34.0, 44.0, nan, nan] - [nan, nan, nan, nan, nan, nan, nan, nan] - [nan, nan, nan, nan, nan, nan, nan, nan] - """ + # [nan, nan, nan, nan, nan, nan, nan, nan] + # [nan, nan, nan, nan, nan, nan, nan, nan] + # [nan, nan, 11.0, 21.0, 31.0, 41.0, nan, nan] + # [nan, nan, 12.0, 22.0, 32.0, 42.0, nan, nan] + # [nan, nan, 13.0, 23.0, 33.0, 43.0, nan, nan] + # [nan, nan, 14.0, 24.0, 34.0, 44.0, nan, nan] + # [nan, nan, nan, nan, nan, nan, nan, nan] + # [nan, nan, nan, nan, nan, nan, nan, nan] self.assertCountEqual(rast[2].tolist()[2:6], [11.0, 21.0, 31.0, 41.0]) self.assertCountEqual(rast[5].tolist()[2:6], [14.0, 24.0, 34.0, 44.0]) diff --git a/python/grass/pygrass/rpc/__init__.py b/python/grass/pygrass/rpc/__init__.py index e95c3453250..a0737288d88 100644 --- a/python/grass/pygrass/rpc/__init__.py +++ b/python/grass/pygrass/rpc/__init__.py @@ -116,9 +116,8 @@ def _get_vector_table_as_dict(lock, conn, data): table = layer.table_to_dict(where=where) layer.close() - ret = {} - ret["table"] = table - ret["columns"] = columns + ret = {"table": table, "columns": columns} + finally: # Send even if an exception was raised. conn.send(ret) @@ -478,7 +477,7 @@ def get_vector_features_as_wkb_list( doctest.testmod() - """Remove the generated maps, if exist""" + # Remove the generated maps, if exist mset = utils.get_mapset_raster(test_raster_name, mapset="") if mset: Module("g.remove", flags="f", type="raster", name=test_raster_name) diff --git a/python/grass/pygrass/rpc/base.py b/python/grass/pygrass/rpc/base.py index e12bea9f985..5b811d2a7f7 100644 --- a/python/grass/pygrass/rpc/base.py +++ b/python/grass/pygrass/rpc/base.py @@ -49,7 +49,8 @@ def dummy_server(lock: _LockLike, conn: Connection) -> NoReturn: conn.close() sys.exit() if data[0] == 1: - raise Exception("Server process intentionally killed by exception") + msg = "Server process intentionally killed by exception" + raise Exception(msg) class RPCServerBase: diff --git a/python/grass/pygrass/tests/benchmark.py b/python/grass/pygrass/tests/benchmark.py index b6c5e98c26d..f2915ff60d7 100644 --- a/python/grass/pygrass/tests/benchmark.py +++ b/python/grass/pygrass/tests/benchmark.py @@ -385,7 +385,7 @@ class OptionWithDefault(optparse.Option): ATTRS = optparse.Option.ATTRS + [strREQUIRED] def __init__(self, *opts, **attrs): - if attrs.get(strREQUIRED, False): + if attrs.get(strREQUIRED): attrs["help"] = "(Required) " + attrs.get("help", "") optparse.Option.__init__(self, *opts, **attrs) diff --git a/python/grass/pygrass/utils.py b/python/grass/pygrass/utils.py index 2d6a2498848..b28f01e3fa0 100644 --- a/python/grass/pygrass/utils.py +++ b/python/grass/pygrass/utils.py @@ -74,10 +74,11 @@ def find_in_location(type, pattern, location): return res def find_in_gisdbase(type, pattern, gisdbase): - res = [] - for loc in gisdbase.locations(): - res.extend(find_in_location(type, pattern, Location(loc, gisdbase.name))) - return res + return [ + a + for loc in gisdbase.locations() + for a in find_in_location(type, pattern, Location(loc, gisdbase.name)) + ] if gisdbase and location and mapset: mset = Mapset(mapset, location, gisdbase) @@ -187,7 +188,7 @@ def is_clean_name(name) -> bool: False """ - return not libgis.G_legal_filename(name) < 0 + return libgis.G_legal_filename(name) >= 0 def coor2pixel(coord, region): @@ -354,7 +355,8 @@ def r_export(rast, output="", fmt="png", **kargs): **kargs, ) return output - raise ValueError("Raster map does not exist.") + msg = "Raster map does not exist." + raise ValueError(msg) def get_lib_path(modname, libname=None): @@ -600,10 +602,11 @@ def create_test_stream_network_map(map_name="streams"): doctest.testmod() - # Remove the generated vector map, if exist mset = get_mapset_vector(test_vector_name, mapset="") if mset: + # Remove the generated vector map, if exists run_command("g.remove", flags="f", type="vector", name=test_vector_name) mset = get_mapset_raster(test_raster_name, mapset="") if mset: + # Remove the generated raster map, if exists run_command("g.remove", flags="f", type="raster", name=test_raster_name) diff --git a/python/grass/pygrass/vector/__init__.py b/python/grass/pygrass/vector/__init__.py index 6723b2e44b3..5d486d675cb 100644 --- a/python/grass/pygrass/vector/__init__.py +++ b/python/grass/pygrass/vector/__init__.py @@ -1,22 +1,26 @@ from os.path import join, exists import grass.lib.gis as libgis +import ctypes + +# flake8: noqa: E402 libgis.G_gisinit("") import grass.lib.vector as libvect -import ctypes - -# import pygrass modules from grass.pygrass.vector.vector_type import VTYPE from grass.pygrass.errors import GrassError, must_be_open from grass.pygrass.gis import Location - -from grass.pygrass.vector.geometry import GEOOBJ as _GEOOBJ -from grass.pygrass.vector.geometry import read_line, read_next_line -from grass.pygrass.vector.geometry import Area as _Area +from grass.pygrass.vector.geometry import ( + GEOOBJ as _GEOOBJ, + read_line, + read_next_line, + Area as _Area, +) from grass.pygrass.vector.abstract import Info from grass.pygrass.vector.basic import Bbox, Cats, Ilist +# flake8: qa + _NUMOF = { "areas": libvect.Vect_get_num_areas, @@ -113,7 +117,8 @@ def next(self): def rewind(self): """Rewind vector map to cause reads to start at beginning.""" if libvect.Vect_rewind(self.c_mapinfo) == -1: - raise GrassError("Vect_rewind raise an error.") + msg = "Vect_rewind raise an error." + raise GrassError(msg) @must_be_open def write(self, geo_obj, cat=None, attrs=None): @@ -122,7 +127,7 @@ def write(self, geo_obj, cat=None, attrs=None): :param geo_obj: a geometry grass object define in grass.pygrass.vector.geometry :type geo_obj: geometry GRASS object - :param attrs: a list with the values that will be insert in the + :param attrs: a list with the values that will be inserted in the attribute table. :type attrs: list :param cat: The category of the geometry feature, otherwise the @@ -205,10 +210,7 @@ def write(self, geo_obj, cat=None, attrs=None): if cat is not None and cat not in self._cats: self._cats.append(cat) if self.table is not None and attrs is not None: - attr = [ - cat, - ] - attr.extend(attrs) + attr = [cat, *attrs] cur = self.table.conn.cursor() cur.execute(self.table.columns.insert_str, attr) cur.close() @@ -224,7 +226,8 @@ def write(self, geo_obj, cat=None, attrs=None): self.c_mapinfo, geo_obj.gtype, geo_obj.c_points, geo_obj.c_cats ) if result == -1: - raise GrassError("Not able to write the vector feature.") + msg = "Not able to write the vector feature." + raise GrassError(msg) if self._topo_level == 2: # return new feature id (on level 2) geo_obj.id = result @@ -386,21 +389,18 @@ def number_of(self, vtype): .. """ - if vtype in _NUMOF.keys(): - if isinstance(_NUMOF[vtype], tuple): - fn, ptype = _NUMOF[vtype] - return fn(self.c_mapinfo, ptype) - return _NUMOF[vtype](self.c_mapinfo) - keys = "', '".join(sorted(_NUMOF.keys())) - raise ValueError("vtype not supported, use one of: '%s'" % keys) + if vtype not in _NUMOF.keys(): + keys = "', '".join(sorted(_NUMOF.keys())) + raise ValueError("vtype not supported, use one of: '%s'" % keys) + if isinstance(_NUMOF[vtype], tuple): + fn, ptype = _NUMOF[vtype] + return fn(self.c_mapinfo, ptype) + return _NUMOF[vtype](self.c_mapinfo) @must_be_open def num_primitives(self): """Return dictionary with the number of all primitives""" - output = {} - for prim in VTYPE.keys(): - output[prim] = self.num_primitive_of(prim) - return output + return {prim: self.num_primitive_of(prim) for prim in VTYPE.keys()} @must_be_open def viter(self, vtype, idonly=False): @@ -444,24 +444,23 @@ def viter(self, vtype, idonly=False): >>> test_vect.close() """ is2D = not self.is_3D() - if vtype in _GEOOBJ.keys(): - if _GEOOBJ[vtype] is not None: - ids = (indx for indx in range(1, self.number_of(vtype) + 1)) - if idonly: - return ids - return ( - _GEOOBJ[vtype]( - v_id=indx, - c_mapinfo=self.c_mapinfo, - table=self.table, - writeable=self.writeable, - is2D=is2D, - ) - for indx in ids - ) - else: + if vtype not in _GEOOBJ.keys(): keys = "', '".join(sorted(_GEOOBJ.keys())) raise ValueError("vtype not supported, use one of: '%s'" % keys) + if _GEOOBJ[vtype] is not None: + ids = (indx for indx in range(1, self.number_of(vtype) + 1)) + if idonly: + return ids + return ( + _GEOOBJ[vtype]( + v_id=indx, + c_mapinfo=self.c_mapinfo, + table=self.table, + writeable=self.writeable, + is2D=is2D, + ) + for indx in ids + ) @must_be_open def rewind(self): @@ -633,15 +632,17 @@ def rewrite(self, geo_obj, cat, attrs=None, **kargs): self.table.update(key=cat, values=attrs) elif self.table is None and attrs: print( - "Table for vector {name} does not exist, attributes not" - " loaded".format(name=self.name) + "Table for vector {name} does not exist, attributes not loaded".format( + name=self.name + ) ) libvect.Vect_cat_set(geo_obj.c_cats, self.layer, cat) result = libvect.Vect_rewrite_line( self.c_mapinfo, cat, geo_obj.gtype, geo_obj.c_points, geo_obj.c_cats ) if result == -1: - raise GrassError("Not able to write the vector feature.") + msg = "Not able to write the vector feature." + raise GrassError(msg) # return offset into file where the feature starts geo_obj.offset = result @@ -654,25 +655,25 @@ def delete(self, feature_id): :type feature_id: int """ if libvect.Vect_rewrite_line(self.c_mapinfo, feature_id) == -1: - raise GrassError("C function: Vect_rewrite_line.") + msg = "C function: Vect_rewrite_line." + raise GrassError(msg) @must_be_open def restore(self, geo_obj): - if hasattr(geo_obj, "offset"): - if ( - libvect.Vect_restore_line(self.c_mapinfo, geo_obj.offset, geo_obj.id) - == -1 - ): - raise GrassError("C function: Vect_restore_line.") - else: - raise ValueError("The value have not an offset attribute.") + if not hasattr(geo_obj, "offset"): + msg = "The value have not an offset attribute." + raise ValueError(msg) + if libvect.Vect_restore_line(self.c_mapinfo, geo_obj.offset, geo_obj.id) == -1: + msg = "C function: Vect_restore_line." + raise GrassError(msg) @must_be_open def bbox(self): - """Return the BBox of the vecor map""" + """Return the BBox of the vector map""" bbox = Bbox() if libvect.Vect_get_map_box(self.c_mapinfo, bbox.c_bbox) == 0: - raise GrassError("I can not find the Bbox.") + msg = "I can not find the Bbox." + raise GrassError(msg) return bbox def close(self, build=True, release=True): @@ -709,27 +710,26 @@ def table_to_dict(self, where=None): """ - if self.table is not None: - table_dict = {} - # Get the category index - cat_index = self.table.columns.names().index("cat") - # Prepare a filter - if where is not None: - self.table.filters.where(where) - - self.table.filters.order_by("cat") + if self.table is None: + return None + table_dict = {} + # Get the category index + cat_index = self.table.columns.names().index("cat") + # Prepare a filter + if where is not None: + self.table.filters.where(where) - self.table.filters.select(",".join(self.table.columns.names())) - # Execute the query and fetch the result - cur = self.table.execute() - entries = cur.fetchall() - # Generate the dictionary - for entry in entries: - table_dict[entry[cat_index]] = list(entry) + self.table.filters.order_by("cat") - return table_dict + self.table.filters.select(",".join(self.table.columns.names())) + # Execute the query and fetch the result + cur = self.table.execute() + entries = cur.fetchall() + # Generate the dictionary + for entry in entries: + table_dict[entry[cat_index]] = list(entry) - return None + return table_dict @must_be_open def features_to_wkb_list(self, bbox=None, feature_type="point", field=1): @@ -830,42 +830,38 @@ def features_to_wkb_list(self, bbox=None, feature_type="point", field=1): bbox, type=feature_type.lower(), bboxlist_only=True ) - if bboxlist is not None and len(bboxlist) > 0: - wkb_list = [] - line_p = libvect.line_pnts() - line_c = libvect.line_cats() - size = ctypes.c_size_t() - cat = ctypes.c_int() - error = ctypes.c_int() - - for f_id in bboxlist.ids: - barray = libvect.Vect_read_line_to_wkb( - self.c_mapinfo, - ctypes.byref(line_p), - ctypes.byref(line_c), - f_id, - ctypes.byref(size), - ctypes.byref(error), - ) - if not barray: - if error == -1: - raise GrassError( - _("Unable to read line of feature %i") % (f_id) - ) - if error == -2: - print("Empty feature %i" % (f_id)) - continue - - ok = libvect.Vect_cat_get( - ctypes.byref(line_c), field, ctypes.byref(cat) - ) - pcat = None if ok < 1 else cat.value + if bboxlist is None or len(bboxlist) <= 0: + return None + + wkb_list = [] + line_p = libvect.line_pnts() + line_c = libvect.line_cats() + size = ctypes.c_size_t() + cat = ctypes.c_int() + error = ctypes.c_int() + + for f_id in bboxlist.ids: + barray = libvect.Vect_read_line_to_wkb( + self.c_mapinfo, + ctypes.byref(line_p), + ctypes.byref(line_c), + f_id, + ctypes.byref(size), + ctypes.byref(error), + ) + if not barray: + if error == -1: + raise GrassError(_("Unable to read line of feature %i") % f_id) + if error == -2: + print("Empty feature %i" % f_id) + continue + ok = libvect.Vect_cat_get(ctypes.byref(line_c), field, ctypes.byref(cat)) + pcat = None if ok < 1 else cat.value - wkb_list.append((f_id, pcat, ctypes.string_at(barray, size.value))) - libgis.G_free(barray) + wkb_list.append((f_id, pcat, ctypes.string_at(barray, size.value))) + libgis.G_free(barray) - return wkb_list - return None + return wkb_list @must_be_open def areas_to_wkb_list(self, bbox=None, field=1): @@ -928,35 +924,36 @@ def areas_to_wkb_list(self, bbox=None, field=1): bboxlist = self.find_by_bbox.areas(bbox, bboxlist_only=True) - if bboxlist is not None and len(bboxlist) > 0: - wkb_list = [] - line_c = libvect.line_cats() - size = ctypes.c_size_t() - cat = ctypes.c_int() + if bboxlist is None or len(bboxlist) <= 0: + return None - for a_id in bboxlist.ids: - barray = libvect.Vect_read_area_to_wkb( - self.c_mapinfo, a_id, ctypes.byref(size) - ) - if not barray: - raise GrassError(_("Unable to read area with id %i") % (a_id)) + wkb_list = [] + line_c = libvect.line_cats() + size = ctypes.c_size_t() + cat = ctypes.c_int() + + for a_id in bboxlist.ids: + barray = libvect.Vect_read_area_to_wkb( + self.c_mapinfo, a_id, ctypes.byref(size) + ) + if not barray: + raise GrassError(_("Unable to read area with id %i") % a_id) - pcat = None - c_ok = libvect.Vect_get_area_cats( - self.c_mapinfo, a_id, ctypes.byref(line_c) + pcat = None + c_ok = libvect.Vect_get_area_cats( + self.c_mapinfo, a_id, ctypes.byref(line_c) + ) + if c_ok == 0: # Centroid found + ok = libvect.Vect_cat_get( + ctypes.byref(line_c), field, ctypes.byref(cat) ) - if c_ok == 0: # Centroid found - ok = libvect.Vect_cat_get( - ctypes.byref(line_c), field, ctypes.byref(cat) - ) - if ok > 0: - pcat = cat.value + if ok > 0: + pcat = cat.value - wkb_list.append((a_id, pcat, ctypes.string_at(barray, size.value))) - libgis.G_free(barray) + wkb_list.append((a_id, pcat, ctypes.string_at(barray, size.value))) + libgis.G_free(barray) - return wkb_list - return None + return wkb_list if __name__ == "__main__": @@ -966,10 +963,10 @@ def areas_to_wkb_list(self, bbox=None, field=1): utils.create_test_vector_map(test_vector_name) doctest.testmod() - """Remove the generated vector map, if exist""" from grass.pygrass.utils import get_mapset_vector from grass.script.core import run_command mset = get_mapset_vector(test_vector_name, mapset="") if mset: + # Remove the generated vector map, if exists run_command("g.remove", flags="f", type="vector", name=test_vector_name) diff --git a/python/grass/pygrass/vector/abstract.py b/python/grass/pygrass/vector/abstract.py index 79307f0d354..36fb2dd4d51 100644 --- a/python/grass/pygrass/vector/abstract.py +++ b/python/grass/pygrass/vector/abstract.py @@ -281,10 +281,11 @@ def rename(self, newname): :type newname: str """ if self.exist(): - if not self.is_open(): - utils.rename(self.name, newname, "vect") - else: - raise GrassError("The map is open, not able to renamed it.") + if self.is_open(): + msg = "The map is open, not able to rename it." + raise GrassError(msg) + utils.rename(self.name, newname, "vect") + self._name = newname def is_3D(self): @@ -293,13 +294,13 @@ def is_3D(self): def exist(self): """Return if the Vector exists or not""" - if self.name: - if self.mapset == "": - mapset = utils.get_mapset_vector(self.name, self.mapset) - self.mapset = mapset or "" - return bool(mapset) - return bool(utils.get_mapset_vector(self.name, self.mapset)) - return False + if not self.name: + return False + if self.mapset == "": + mapset = utils.get_mapset_vector(self.name, self.mapset) + self.mapset = mapset or "" + return bool(mapset) + return bool(utils.get_mapset_vector(self.name, self.mapset)) def is_open(self): """Return if the Vector is open""" @@ -336,12 +337,11 @@ def open( :param tab_name: define the name of the table that will be generate :type tab_name: str :param tab_cols: define the name and type of the columns of the - attribute table of the vecto map + attribute table of the vector map :type tab_cols: list of pairs - :param link_name: define the name of the link connection with the - database + :param link_name: define the name of the link connection with the database :type link_name: str - :param link_key: define the nema of the column that will be use as + :param link_key: define the name of the column that will be use as vector category :type link_key: str :param link_db: define the database connection parameters @@ -360,12 +360,14 @@ def open( if not self.exist() and self.mode != "w": raise OpenError("Map <%s> not found." % self._name) if libvect.Vect_set_open_level(self._topo_level) != 0: - raise OpenError("Invalid access level.") + msg = "Invalid access level." + raise OpenError(msg) # update the overwrite attribute self.overwrite = overwrite if overwrite is not None else self.overwrite # check if the mode is valid if self.mode not in {"r", "rw", "w"}: - raise ValueError("Mode not supported. Use one of: 'r', 'rw', 'w'.") + msg = "Mode not supported. Use one of: 'r', 'rw', 'w'." + raise ValueError(msg) # check if the map exist if self.exist() and self.mode in {"r", "rw"}: diff --git a/python/grass/pygrass/vector/basic.py b/python/grass/pygrass/vector/basic.py index f031e23ff98..86eed054340 100644 --- a/python/grass/pygrass/vector/basic.py +++ b/python/grass/pygrass/vector/basic.py @@ -311,13 +311,15 @@ def __getitem__(self, key): if key < 0: # Handle negative indices key += self.c_ilist.contents.n_values if key >= self.c_ilist.contents.n_values: - raise IndexError("Index out of range") + msg = "Index out of range" + raise IndexError(msg) return self.c_ilist.contents.value[key] raise ValueError("Invalid argument type: %r." % key) def __setitem__(self, key, value): if self.contains(value): - raise ValueError("Integer already in the list") + msg = "Integer already in the list" + raise ValueError(msg) self.c_ilist.contents.value[key] = int(value) def __len__(self): diff --git a/python/grass/pygrass/vector/find.py b/python/grass/pygrass/vector/find.py index 25ce9aca8f4..0769e85e72e 100644 --- a/python/grass/pygrass/vector/find.py +++ b/python/grass/pygrass/vector/find.py @@ -4,28 +4,38 @@ @author: pietro """ -import grass.lib.vector as libvect +from __future__ import annotations + +from typing import TYPE_CHECKING +import grass.lib.vector as libvect from grass.pygrass.errors import must_be_open +from grass.pygrass.vector.basic import BoxList, Ilist +from grass.pygrass.vector.geometry import Area, Isle, Node, read_line -from grass.pygrass.vector.basic import Ilist, BoxList -from grass.pygrass.vector.geometry import read_line, Isle, Area, Node +if TYPE_CHECKING: + from grass.pygrass.vector.table import Table # For test purposes test_vector_name = "find_doctest_map" class AbstractFinder: - def __init__(self, c_mapinfo, table=None, writeable=False): - """Abstract finder - ----------------- + def __init__( + self, c_mapinfo, table: Table | None = None, writeable: bool = False + ) -> None: + """Find geometry feature(s) around a point or that are inside or intersect + with a bounding box. - Find geometry feature around a point. + :param c_mapinfo: Pointer to the vector layer mapinfo structure + :type c_mapinfo: ctypes pointer to mapinfo structure + :param table: Attribute table of the vector layer + :param writable: True or False """ self.c_mapinfo = c_mapinfo - self.table = table - self.writeable = writeable - self.vtype = { + self.table: Table | None = table + self.writeable: bool = writeable + self.vtype: dict[str, int] = { "point": libvect.GV_POINT, # 1 "line": libvect.GV_LINE, # 2 "boundary": libvect.GV_BOUNDARY, # 3 @@ -33,7 +43,7 @@ def __init__(self, c_mapinfo, table=None, writeable=False): "all": -1, } - def is_open(self): + def is_open(self) -> bool: """Check if the vector map is open or not""" from . import abstract @@ -48,20 +58,6 @@ class PointFinder(AbstractFinder): is part of a topological vector map object. """ - def __init__(self, c_mapinfo, table=None, writeable=False): - """Find geometry feature(s) around a point. - - :param c_mapinfo: Pointer to the vector layer mapinfo structure - :type c_mapinfo: ctypes pointer to mapinfo structure - - :param table: Attribute table of the vector layer - :type table: Class Table from grass.pygrass.table - - :param writable: True or False - :type writeable: boolean - """ - super().__init__(c_mapinfo, table, writeable) - @must_be_open def node(self, point, maxdist): """Find the nearest node around a specific point. @@ -397,21 +393,6 @@ class BboxFinder(AbstractFinder): """ - def __init__(self, c_mapinfo, table=None, writeable=False): - """Find geometry feature(s)that are insider or intersect - with a boundingbox. - - :param c_mapinfo: Pointer to the vector layer mapinfo structure - :type c_mapinfo: ctypes pointer to mapinfo structure - - :param table: Attribute table of the vector layer - :type table: Class Table from grass.pygrass.table - - :param writable: True or False - :type writeable: boolean - """ - super().__init__(c_mapinfo, table, writeable) - @must_be_open def geos(self, bbox, type="all", bboxlist_only=False): """Find vector features inside a boundingbox. @@ -662,9 +643,6 @@ def islands(self, bbox, bboxlist_only=False): class PolygonFinder(AbstractFinder): - def __init__(self, c_mapinfo, table=None, writeable=False): - super().__init__(c_mapinfo, table, writeable) - def lines(self, polygon, isles=None): pass @@ -674,15 +652,16 @@ def areas(self, polygon, isles=None): if __name__ == "__main__": import doctest + from grass.pygrass import utils utils.create_test_vector_map(test_vector_name) doctest.testmod() - """Remove the generated vector map, if exist""" from grass.pygrass.utils import get_mapset_vector from grass.script.core import run_command mset = get_mapset_vector(test_vector_name, mapset="") if mset: + # Remove the generated vector map, if exists run_command("g.remove", flags="f", type="vector", name=test_vector_name) diff --git a/python/grass/pygrass/vector/geometry.py b/python/grass/pygrass/vector/geometry.py index b734161d1ea..d653c7b8350 100644 --- a/python/grass/pygrass/vector/geometry.py +++ b/python/grass/pygrass/vector/geometry.py @@ -200,31 +200,27 @@ def __setitem__(self, keys, values): >>> test_vect.close() """ - if self.writeable: - if np.isscalar(keys): - keys, values = (keys,), (values,) - # check if key is a column of the table or not - for key in keys: - if key not in self.table.columns: - raise KeyError("Column: %s not in table" % key) - # prepare the string using as paramstyle: qmark - vals = ",".join(["%s=?" % k for k in keys]) - # "UPDATE {tname} SET {values} WHERE {condition};" - sqlcode = sql.UPDATE_WHERE.format( - tname=self.table.name, values=vals, condition=self.cond - ) - self.table.execute(sqlcode, values=values) - # self.table.conn.commit() - else: + if not self.writeable: str_err = "You can only read the attributes if the map is in another mapset" raise GrassError(str_err) + if np.isscalar(keys): + keys, values = ((keys,), (values,)) + # check if key is a column of the table or not + for key in keys: + if key not in self.table.columns: + raise KeyError("Column: %s not in table" % key) + # prepare the string using as paramstyle: qmark + vals = ",".join(["%s=?" % k for k in keys]) + # "UPDATE {tname} SET {values} WHERE {condition};" + sqlcode = sql.UPDATE_WHERE.format( + tname=self.table.name, values=vals, condition=self.cond + ) + self.table.execute(sqlcode, values=values) + # self.table.conn.commit() def __dict__(self): """Return a dict of the attribute table row.""" - dic = {} - for key, val in zip(self.keys(), self.values()): - dic[key] = val - return dic + return dict(zip(self.keys(), self.values())) def values(self): """Return the values of the attribute table row. @@ -594,8 +590,7 @@ def buffer( :type angle: num :param round_: to make corners round :type round_: bool - :param tol: fix the maximum distance between theoretical arc and - output segments + :param tol: fix the maximum distance between theoretical arc and output segments :type tol: float :returns: the buffer as Area object @@ -611,7 +606,8 @@ def buffer( dist_x = dist dist_y = dist elif not dist_x or not dist_y: - raise TypeError("TypeError: buffer expected 1 arguments, got 0") + msg = "buffer expected 1 arguments, got 0" + raise TypeError(msg) bound = Line() p_points = ctypes.pointer(bound.c_points) libvect.Vect_point_buffer2( @@ -675,7 +671,8 @@ def __getitem__(self, key): if key < 0: # Handle negative indices key += self.c_points.contents.n_points if key >= self.c_points.contents.n_points: - raise IndexError("Index out of range") + msg = "Index out of range" + raise IndexError(msg) return Point( self.c_points.contents.x[key], self.c_points.contents.y[key], @@ -743,7 +740,8 @@ def point_on_line(self, distance, angle=0, slope=0): ctypes.pointer(ctypes.c_double(angle)), ctypes.pointer(ctypes.c_double(slope)), ): - raise ValueError("Vect_point_on_line give an error.") + msg = "Vect_point_on_line gave an error." + raise ValueError(msg) pnt.is2D = self.is2D return pnt @@ -845,7 +843,8 @@ def insert(self, indx, pnt): if indx < 0: # Handle negative indices indx += self.c_points.contents.n_points if indx >= self.c_points.contents.n_points: - raise IndexError("Index out of range") + msg = "Index out of range" + raise IndexError(msg) x, y, z = get_xyz(pnt) libvect.Vect_line_insert_point(self.c_points, indx, x, y, z) @@ -949,7 +948,8 @@ def pop(self, indx): if indx < 0: # Handle negative indices indx += self.c_points.contents.n_points if indx >= self.c_points.contents.n_points: - raise IndexError("Index out of range") + msg = "Index out of range" + raise IndexError(msg) pnt = self[indx] libvect.Vect_line_delete_point(self.c_points, indx) return pnt @@ -968,7 +968,8 @@ def delete(self, indx): if indx < 0: # Handle negative indices indx += self.c_points.contents.n_points if indx >= self.c_points.contents.n_points: - raise IndexError("Index out of range") + msg = "Index out of range" + raise IndexError(msg) libvect.Vect_line_delete_point(self.c_points, indx) def prune(self): @@ -1024,7 +1025,8 @@ def remove(self, pnt): if pnt == point: libvect.Vect_line_delete_point(self.c_points, indx) return - raise ValueError("list.remove(x): x not in list") + msg = "list.remove(x): x not in list" + raise ValueError(msg) def reverse(self): """Reverse the order of vertices, using `Vect_line_reverse` @@ -1044,11 +1046,9 @@ def reverse(self): def segment(self, start, end): """Create line segment. using the ``Vect_line_segment`` C function. - :param start: distance from the beginning of the line where - the segment start + :param start: distance from the beginning of the line where the segment starts :type start: float - :param end: distance from the beginning of the line where - the segment end + :param end: distance from the beginning of the line where the segment ends :type end: float :: @@ -1127,8 +1127,6 @@ def from_wkt(self, wkt): self.reset() for coord in match.groups()[0].strip().split(","): self.append(tuple(float(e) for e in coord.split(" "))) - else: - return None def buffer( self, @@ -1153,8 +1151,7 @@ def buffer( :type angle: num :param round_: to make corners round :type round_: bool - :param tol: fix the maximum distance between theoretical arc and - output segments + :param tol: fix the maximum distance between theoretical arc and output segments :type tol: float :returns: the buffer as Area object @@ -1173,7 +1170,8 @@ def buffer( dist_x = dist dist_y = dist elif not dist_x or not dist_y: - raise TypeError("TypeError: buffer expected 1 arguments, got 0") + msg = "buffer expected 1 arguments, got 0" + raise TypeError(msg) p_bound = ctypes.pointer(ctypes.pointer(libvect.line_pnts())) pp_isle = ctypes.pointer(ctypes.pointer(ctypes.pointer(libvect.line_pnts()))) n_isles = ctypes.pointer(ctypes.c_int()) @@ -1296,7 +1294,8 @@ def to_wkb(self): TODO: Must be implemented """ - raise Exception("Not implemented") + msg = "Not implemented" + raise Exception(msg) def ilines(self, only_in=False, only_out=False): """Return a generator with all lines id connected to a node. @@ -1507,7 +1506,8 @@ def to_wkt(self): def to_wkb(self): """Return a "well know text" (WKB) geometry array. ::""" - raise Exception("Not implemented") + msg = "Not implemented" + raise Exception(msg) @mapinfo_must_be_set def points_geos(self): @@ -1712,8 +1712,7 @@ def buffer( :type angle: num :param round_: to make corners round :type round_: bool - :param tol: fix the maximum distance between theoretical arc and - output segments + :param tol: fix the maximum distance between theoretical arc and output segments :type tol: float :returns: the buffer as line, centroid, isles object tuple @@ -1723,7 +1722,8 @@ def buffer( dist_x = dist dist_y = dist elif not dist_x or not dist_y: - raise TypeError("TypeError: buffer expected 1 arguments, got 0") + msg = "buffer expected 1 arguments, got 0" + raise TypeError(msg) p_bound = ctypes.pointer(ctypes.pointer(libvect.line_pnts())) pp_isle = ctypes.pointer(ctypes.pointer(ctypes.pointer(libvect.line_pnts()))) n_isles = ctypes.pointer(ctypes.c_int()) @@ -1901,7 +1901,8 @@ def c_read_line(feature_id, c_mapinfo, c_points, c_cats): if feature_id < 0: # Handle negative indices feature_id += nmax + 1 if feature_id > nmax: - raise IndexError("Index out of range") + msg = "Index out of range" + raise IndexError(msg) if feature_id > 0: ftype = libvect.Vect_read_line(c_mapinfo, c_points, c_cats, feature_id) return feature_id, ftype, c_points, c_cats @@ -1953,10 +1954,10 @@ def read_line( utils.create_test_vector_map(test_vector_name) doctest.testmod() - """Remove the generated vector map, if exist""" from grass.pygrass.utils import get_mapset_vector from grass.script.core import run_command mset = get_mapset_vector(test_vector_name, mapset="") if mset: + # Remove the generated vector map, if exists run_command("g.remove", flags="f", type="vector", name=test_vector_name) diff --git a/python/grass/pygrass/vector/table.py b/python/grass/pygrass/vector/table.py index b83f751a0dd..a6df2119f28 100644 --- a/python/grass/pygrass/vector/table.py +++ b/python/grass/pygrass/vector/table.py @@ -133,7 +133,8 @@ def limit(self, number): :type number: int """ if not isinstance(number, int): - raise ValueError("Must be an integer.") + msg = "Must be an integer." + raise ValueError(msg) self._limit = "LIMIT {number}".format(number=number) return self @@ -264,9 +265,9 @@ def update_odict(self): """Read columns name and types from table and update the odict attribute. """ + cur = self.conn.cursor() if self.is_pg(): # is a postgres connection - cur = self.conn.cursor() cur.execute("SELECT oid,typname FROM pg_type") diz = dict(cur.fetchall()) odict = OrderedDict() @@ -280,17 +281,15 @@ def update_odict(self): odict[name] = diz[ctype] except pg.ProgrammingError: pass - self.odict = odict else: # is a sqlite connection - cur = self.conn.cursor() cur.execute(sql.PRAGMA.format(tname=self.tname)) descr = cur.fetchall() odict = OrderedDict() for column in descr: name, ctype = column[1:3] odict[name] = ctype - self.odict = odict + self.odict = odict values = ",".join( [ "?", @@ -362,11 +361,9 @@ def names(self, remove=None, unicod=True): ['cat', 'name', 'value'] """ + nams = list(self.odict.keys()) if remove: - nams = list(self.odict.keys()) nams.remove(remove) - else: - nams = list(self.odict.keys()) if unicod: return nams return [str(name) for name in nams] @@ -551,17 +548,15 @@ def cast(self, col_name, new_type): It is not possible to cast a column with sqlite """ - if self.is_pg(): - cur = self.conn.cursor() - cur.execute( - sql.CAST_COL.format(tname=self.tname, col=col_name, ctype=new_type) - ) - self.conn.commit() - cur.close() - self.update_odict() - else: + if not self.is_pg(): # sqlite does not support rename columns: - raise DBError("SQLite does not support to cast columns.") + msg = "SQLite does not support to cast columns." + raise DBError(msg) + cur = self.conn.cursor() + cur.execute(sql.CAST_COL.format(tname=self.tname, col=col_name, ctype=new_type)) + self.conn.commit() + cur.close() + self.update_odict() def drop(self, col_name): """Drop a column from the table. @@ -663,7 +658,8 @@ def _get_layer(self): def _set_layer(self, number): if number <= 0: - raise TypeError("Number must be positive and greater than 0.") + msg = "Number must be positive and greater than 0." + raise TypeError(msg) self.c_fieldinfo.contents.number = number layer = property( @@ -710,18 +706,17 @@ def _get_driver(self): return decode(self.c_fieldinfo.contents.driver) def _set_driver(self, driver): - if driver in DRIVERS: - self.c_fieldinfo.contents.driver = ReturnString(driver) - elif driver in UNSUPPORTED_DRIVERS: - raise NotImplementedError( - "The database driver %s is not supported by PyGRASS, " - "use: %s." % (driver, ", ".join(DRIVERS)) - ) - else: + if driver not in DRIVERS: + if driver in UNSUPPORTED_DRIVERS: + raise NotImplementedError( + "The database driver %s is not supported by PyGRASS, use: %s." + % (driver, ", ".join(DRIVERS)) + ) raise ValueError( - "The database driver %s is not known to PyGRASS, " - "use: %s." % (driver, ", ".join(DRIVERS)) + "The database driver %s is not known to PyGRASS, use: %s." + % (driver, ", ".join(DRIVERS)) ) + self.c_fieldinfo.contents.driver = ReturnString(driver) driver = property( fget=_get_driver, @@ -824,7 +819,7 @@ def connection(self): if driver == "sqlite": import sqlite3 - # Numpy is using some custom integer data types to efficiently + # NumPy is using some custom integer data types to efficiently # pack data into memory. Since these types aren't familiar to # sqlite, you'll have to tell it about how to handle them. for t in ( @@ -853,9 +848,9 @@ def connection(self): except ImportError: er = "You need to install psycopg2 to connect with this table." raise ImportError(er) - else: - str_err = "Driver is not supported yet, pleas use: sqlite or pg" - raise TypeError(str_err) + + str_err = "Driver is not supported yet, pleas use: sqlite or pg" + raise TypeError(str_err) def table(self): """Return a Table object. @@ -1122,8 +1117,7 @@ def drop(self, cursor=None, force=False): :param cursor: the cursor to connect, if None it use the cursor of connection table object :type cursor: Cursor object - :param force: True to remove the table, by default False to print - advice + :param force: True to remove the table, by default False to print advice :type force: bool """ @@ -1162,8 +1156,7 @@ def execute(self, sql_code=None, cursor=None, many=False, values=None): """Execute SQL code from a given string or build with filters and return a cursor object. - :param sql_code: the SQL code to execute, if not pass it use filters - variable + :param sql_code: the SQL code to execute, if not pass it use filters variable :type sql_code: str :param cursor: the cursor to connect, if None it use the cursor of connection table object @@ -1282,10 +1275,10 @@ def create(self, cols, name=None, overwrite=False, cursor=None): utils.create_test_vector_map(test_vector_name) doctest.testmod() - """Remove the generated vector map, if exist""" from grass.pygrass.utils import get_mapset_vector from grass.script.core import run_command mset = get_mapset_vector(test_vector_name, mapset="") if mset: + # Remove the generated vector map, if exists run_command("g.remove", flags="f", type="vector", name=test_vector_name) diff --git a/python/grass/pygrass/vector/testsuite/test_geometry.py b/python/grass/pygrass/vector/testsuite/test_geometry.py index 6957a473165..3dfd0e5c655 100644 --- a/python/grass/pygrass/vector/testsuite/test_geometry.py +++ b/python/grass/pygrass/vector/testsuite/test_geometry.py @@ -83,7 +83,7 @@ def test_eq(self): point1 = Point(1, 0) self.assertFalse(point0 == point1) self.assertFalse(point0 == (1, 0)) - self.assertTrue(point0 == point0) # noqa: PLR0124 + self.assertTrue(point0 == point0) # noqa: PLR0124 # pylint: disable=R0124 self.assertTrue(point0 == (0, 0)) def test_repr(self): @@ -117,7 +117,7 @@ def tearDownClass(cls): cls.vect.close() cls.c_mapinfo = None - """Remove the generated vector map, if exist""" + # Remove the generated vector map, if exists cls.runModule("g.remove", flags="f", type="vector", name=cls.tmpname) def test_len(self): @@ -207,7 +207,7 @@ def tearDownClass(cls): cls.vect.close() cls.c_mapinfo = None - """Remove the generated vector map, if exist""" + # Remove the generated vector map, if exists cls.runModule("g.remove", flags="f", type="vector", name=cls.tmpname) def test_init(self): @@ -264,7 +264,7 @@ def tearDownClass(cls): cls.vect.close() cls.c_mapinfo = None - """Remove the generated vector map, if exist""" + # Remove the generated vector map, if exists cls.runModule("g.remove", flags="f", type="vector", name=cls.tmpname) def test_init(self): diff --git a/python/grass/pygrass/vector/testsuite/test_geometry_attrs.py b/python/grass/pygrass/vector/testsuite/test_geometry_attrs.py index cba638b1901..8fa198bea0d 100644 --- a/python/grass/pygrass/vector/testsuite/test_geometry_attrs.py +++ b/python/grass/pygrass/vector/testsuite/test_geometry_attrs.py @@ -32,7 +32,7 @@ def tearDownClass(cls): cls.vect.close() cls.c_mapinfo = None - """Remove the generated vector map, if exist""" + # Remove the generated vector map, if exists cls.runModule("g.remove", flags="f", type="vector", name=cls.tmpname) def test_getitem(self): diff --git a/python/grass/pygrass/vector/testsuite/test_table.py b/python/grass/pygrass/vector/testsuite/test_table.py index c77233400b1..ecd4500c488 100644 --- a/python/grass/pygrass/vector/testsuite/test_table.py +++ b/python/grass/pygrass/vector/testsuite/test_table.py @@ -154,7 +154,6 @@ def setUp(self): self.cols = self.table.columns def tearDown(self): - """Remove the generated vector map, if exist""" self.table.drop(force=True) self.table = None self.cols = None @@ -176,7 +175,6 @@ def setUp(self): self.cols = self.table.columns def tearDown(self): - """Remove the generated vector map, if exist""" self.table.drop(force=True) self.table = None self.cols = None diff --git a/python/grass/pygrass/vector/testsuite/test_vector.py b/python/grass/pygrass/vector/testsuite/test_vector.py index b4eebb2ab1e..09baf02d9c3 100644 --- a/python/grass/pygrass/vector/testsuite/test_vector.py +++ b/python/grass/pygrass/vector/testsuite/test_vector.py @@ -28,7 +28,7 @@ def tearDownClass(cls): if cls.vect.is_open(): cls.vect.close() - """Remove the generated vector map, if exist""" + # Remove the generated vector map, if exists cls.runModule("g.remove", flags="f", type="vector", name=cls.tmpname) def test_getitem_slice(self): diff --git a/python/grass/pygrass/vector/testsuite/test_vector3d.py b/python/grass/pygrass/vector/testsuite/test_vector3d.py index 84e688d22e8..a928434c2a8 100644 --- a/python/grass/pygrass/vector/testsuite/test_vector3d.py +++ b/python/grass/pygrass/vector/testsuite/test_vector3d.py @@ -55,7 +55,7 @@ def test_writing_reading_points(self): @classmethod def tearDownClass(cls): - """Remove the generated vector map, if exist""" + """Remove the generated vector map, if exists""" cls.runModule("g.remove", flags="f", type="vector", name=cls.tmpname) diff --git a/python/grass/script/__init__.py b/python/grass/script/__init__.py index 032a0eb67f3..a5dfbedd239 100644 --- a/python/grass/script/__init__.py +++ b/python/grass/script/__init__.py @@ -1,5 +1,4 @@ -"""Python interface to launch GRASS GIS modules in scripts -""" +"""Python interface to launch GRASS GIS modules in scripts""" from . import setup from .core import ( diff --git a/python/grass/script/array.py b/python/grass/script/array.py index 29c99d7617c..5f23bb6f022 100644 --- a/python/grass/script/array.py +++ b/python/grass/script/array.py @@ -131,6 +131,7 @@ def __del__(self): class array(np.memmap): + # pylint: disable-next=signature-differs; W0222 def __new__(cls, mapname=None, null=None, dtype=np.double, env=None): """Define new numpy array @@ -242,6 +243,7 @@ def write(self, mapname, title=None, null=None, overwrite=None, quiet=None): class array3d(np.memmap): + # pylint: disable-next=signature-differs; W0222 def __new__(cls, mapname=None, null=None, dtype=np.double, env=None): """Define new 3d numpy array diff --git a/python/grass/script/core.py b/python/grass/script/core.py index e6de4331b43..ab116dc42db 100644 --- a/python/grass/script/core.py +++ b/python/grass/script/core.py @@ -8,7 +8,7 @@ from grass.script import core as grass grass.parser() -(C) 2008-2024 by the GRASS Development Team +(C) 2008-2025 by 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. @@ -32,14 +32,24 @@ import json import csv import io +from collections.abc import Mapping from tempfile import NamedTemporaryFile from pathlib import Path +from typing import TYPE_CHECKING, TypeVar from .utils import KeyValue, parse_key_val, basename, encode, decode, try_remove from grass.exceptions import ScriptError, CalledModuleError from grass.grassdb.manage import resolve_mapset_path +if TYPE_CHECKING: + from _typeshed import StrPath + + +T = TypeVar("T") +_Env = Mapping[str, str] + + # subprocess wrapper that uses shell on Windows class Popen(subprocess.Popen): _builtin_exts = {".com", ".exe", ".bat", ".cmd"} @@ -53,7 +63,7 @@ def __init__(self, args, **kwargs): if ( sys.platform == "win32" and isinstance(args, list) - and not kwargs.get("shell", False) + and not kwargs.get("shell") and kwargs.get("executable") is None ): cmd = shutil.which(args[0]) @@ -270,7 +280,8 @@ def make_command( if flags: flags = _make_val(flags) if "-" in flags: - raise ScriptError("'-' is not a valid flag") + msg = "'-' is not a valid flag" + raise ScriptError(msg) args.append("-" + flags) for opt, val in options.items(): if opt in _popen_args: @@ -351,8 +362,7 @@ def get_module_and_code(args, kwargs): module, code = get_module_and_code(args, kwargs) fatal( _( - "Module {module} ({code}) failed with" - " non-zero return code {returncode}" + "Module {module} ({code}) failed with non-zero return code {returncode}" ).format(module=module, code=code, returncode=returncode) ) elif handler.lower() == "exit": @@ -866,16 +876,16 @@ def _parse_opts(lines: list) -> tuple[dict[str, str], dict[str, bool]]: try: var, val = line.split(b"=", 1) except ValueError: - raise SyntaxError("invalid output from g.parser: {}".format(line)) + msg = "invalid output from g.parser: {}".format(line) + raise SyntaxError(msg) try: var = decode(var) val = decode(val) except UnicodeError as error: - raise SyntaxError( - "invalid output from g.parser ({error}): {line}".format( - error=error, line=line - ) + msg = "invalid output from g.parser ({error}): {line}".format( + error=error, line=line ) + raise SyntaxError(msg) if var.startswith("flag_"): flags[var[5:]] = bool(int(val)) elif var.startswith("opt_"): @@ -883,9 +893,8 @@ def _parse_opts(lines: list) -> tuple[dict[str, str], dict[str, bool]]: elif var in {"GRASS_OVERWRITE", "GRASS_VERBOSE"}: os.environ[var] = val else: - raise SyntaxError( - "unexpected output variable from g.parser: {}".format(line) - ) + msg = "unexpected output variable from g.parser: {}".format(line) + raise SyntaxError(msg) return (options, flags) @@ -923,15 +932,14 @@ def parser() -> tuple[dict[str, str], dict[str, bool]]: argv[0] = os.path.join(sys.path[0], name) prog = "g.parser.exe" if sys.platform == "win32" else "g.parser" - p = subprocess.Popen([prog, "-n"] + argv, stdout=subprocess.PIPE) - s = p.communicate()[0] - lines = s.split(b"\0") - - if not lines or lines[0] != b"@ARGS_PARSED@": - stdout = os.fdopen(sys.stdout.fileno(), "wb") - stdout.write(s) - sys.exit(p.returncode) - return _parse_opts(lines[1:]) + with subprocess.Popen([prog, "-n"] + argv, stdout=subprocess.PIPE) as p: + s = p.communicate()[0] + lines = s.split(b"\0") + if not lines or lines[0] != b"@ARGS_PARSED@": + stdout = os.fdopen(sys.stdout.fileno(), "wb") + stdout.write(s) + sys.exit(p.returncode) + return _parse_opts(lines[1:]) # interface to g.tempfile @@ -1032,20 +1040,22 @@ def _compare_units(dic): def _text_to_key_value_dict( - filename, sep=":", val_sep=",", checkproj=False, checkunits=False -): + filename: StrPath, + sep: str = ":", + val_sep: str = ",", + checkproj: bool = False, + checkunits: bool = False, +) -> KeyValue[list[int | float | str]]: """Convert a key-value text file, where entries are separated by newlines and the key and value are separated by `sep', into a key-value dictionary and discover/use the correct data types (float, int or string) for values. - :param str filename: The name or name and path of the text file to convert - :param str sep: The character that separates the keys and values, default - is ":" - :param str val_sep: The character that separates the values of a single + :param filename: The name or name and path of the text file to convert + :param sep: The character that separates the keys and values, default is ":" + :param val_sep: The character that separates the values of a single key, default is "," - :param bool checkproj: True if it has to check some information about - projection system - :param bool checkproj: True if it has to check some information about units + :param checkproj: True if it has to check some information about projection system + :param checkunits: True if it has to check some information about units :return: The dictionary @@ -1064,19 +1074,19 @@ def _text_to_key_value_dict( {'a': ['Hello'], 'c': [1, 2, 3, 4, 5], 'b': [1.0], 'd': ['hello', 8, 0.1]} """ - text = open(filename).readlines() - kvdict = KeyValue() + with Path(filename).open() as f: + text = f.readlines() + kvdict: KeyValue[list[int | float | str]] = KeyValue() for line in text: - if line.find(sep) >= 0: - key, value = line.split(sep) - key = key.strip() - value = value.strip() - else: + if line.find(sep) < 0: # Jump over empty values continue + key, value = line.split(sep) + key = key.strip() + value = value.strip() values = value.split(val_sep) - value_list = [] + value_list: list[int | float | str] = [] for value in values: not_float = False @@ -1172,7 +1182,7 @@ def compare_key_value_text_files( # interface to g.gisenv -def gisenv(env=None): +def gisenv(env: _Env | None = None) -> KeyValue[str | None]: """Returns the output from running g.gisenv (with no arguments), as a dictionary. Example: @@ -1190,14 +1200,14 @@ def gisenv(env=None): # interface to g.region -def locn_is_latlong(env=None) -> bool: +def locn_is_latlong(env: _Env | None = None) -> bool: """Tests if location is lat/long. Value is obtained by checking the "g.region -pu" projection code. :return: True for a lat/long region, False otherwise """ s = read_command("g.region", flags="pu", env=env) - kv = parse_key_val(s, ":") + kv: KeyValue[str | None] = parse_key_val(s, ":") return kv["projection"].split(" ")[0] == "3" @@ -1245,7 +1255,9 @@ def region(region3d=False, complete=False, env=None): return reg -def region_env(region3d=False, flags=None, env=None, **kwargs): +def region_env( + region3d: bool = False, flags: str | None = None, env: _Env | None = None, **kwargs +) -> str: """Returns region settings as a string which can used as GRASS_REGION environmental variable. @@ -1255,8 +1267,8 @@ def region_env(region3d=False, flags=None, env=None, **kwargs): See also :func:`use_temp_region()` for alternative method how to define temporary region used for raster-based computation. - :param bool region3d: True to get 3D region - :param string flags: for example 'a' + :param region3d: True to get 3D region + :param flags: for example 'a' :param env: dictionary with system environment variables (`os.environ` by default) :param kwargs: g.region's parameters like 'raster', 'vector' or 'region' @@ -1270,7 +1282,7 @@ def region_env(region3d=False, flags=None, env=None, **kwargs): :return: empty string on error """ # read proj/zone from WIND file - gis_env = gisenv(env) + gis_env: KeyValue[str | None] = gisenv(env) windfile = os.path.join( gis_env["GISDBASE"], gis_env["LOCATION_NAME"], gis_env["MAPSET"], "WIND" ) @@ -1449,20 +1461,19 @@ def list_strings(type, pattern=None, mapset=None, exclude=None, flag="", env=Non if type == "cell": verbose(_('Element type should be "raster" and not "%s"') % type, env=env) - result = [] - for line in read_command( - "g.list", - quiet=True, - flags="m" + flag, - type=type, - pattern=pattern, - exclude=exclude, - mapset=mapset, - env=env, - ).splitlines(): - result.append(line.strip()) - - return result + return [ + line.strip() + for line in read_command( + "g.list", + quiet=True, + flags="m" + flag, + type=type, + pattern=pattern, + exclude=exclude, + mapset=mapset, + env=env, + ).splitlines() + ] def list_pairs(type, pattern=None, mapset=None, exclude=None, flag="", env=None): @@ -1575,7 +1586,7 @@ def list_grouped( # color parsing -named_colors = { +named_colors: dict[str, tuple[float, float, float]] = { "white": (1.00, 1.00, 1.00), "black": (0.00, 0.00, 0.00), "red": (1.00, 0.00, 0.00), @@ -1595,7 +1606,9 @@ def list_grouped( } -def parse_color(val, dflt=None): +def parse_color( + val: str, dflt: tuple[float, float, float] | None = None +) -> tuple[float, float, float] | None: """Parses the string "val" as a GRASS colour, which can be either one of the named colours or an R:G:B tuple e.g. 255:255:255. Returns an (r,g,b) triple whose components are floating point values between 0 @@ -1614,9 +1627,9 @@ def parse_color(val, dflt=None): if val in named_colors: return named_colors[val] - vals = val.split(":") + vals: list[str] = val.split(":") if len(vals) == 3: - return tuple(float(v) / 255 for v in vals) + return (float(vals[0]) / 255, float(vals[1]) / 255, float(vals[2]) / 255) return dflt @@ -1678,14 +1691,13 @@ def find_program(pgm, *args): or non-zero return code :return: True otherwise """ - nuldev = open(os.devnull, "w+") - try: - # TODO: the doc or impl is not correct, any return code is accepted - call([pgm] + list(args), stdin=nuldev, stdout=nuldev, stderr=nuldev) - found = True - except Exception: - found = False - nuldev.close() + with open(os.devnull, "w+") as nuldev: + try: + # TODO: the doc or impl is not correct, any return code is accepted + call([pgm] + list(args), stdin=nuldev, stdout=nuldev, stderr=nuldev) + found = True + except Exception: + found = False return found @@ -1800,6 +1812,7 @@ def create_project( if datum_trans: kwargs["datum_trans"] = datum_trans + ps = None if epsg: ps = pipe_command( "g.proj", @@ -1855,7 +1868,7 @@ def create_project( else: _create_location_xy(mapset_path.directory, mapset_path.location) - if epsg or proj4 or filename or wkt: + if ps is not None and (epsg or proj4 or filename or wkt): error = ps.communicate(stdin)[1] try_remove(tmp_gisrc) @@ -1868,16 +1881,15 @@ def create_project( def _set_location_description(path, location, text): """Set description (aka title aka MYNAME) for a location""" try: - fd = codecs.open( + with codecs.open( os.path.join(path, location, "PERMANENT", "MYNAME"), encoding="utf-8", mode="w", - ) - if text: - fd.write(text + os.linesep) - else: - fd.write(os.linesep) - fd.close() + ) as fd: + if text: + fd.write(text + os.linesep) + else: + fd.write(os.linesep) except OSError as e: raise ScriptError(repr(e)) @@ -1893,8 +1905,11 @@ def _create_location_xy(database, location): cur_dir = Path.cwd() try: os.chdir(database) + permanent_dir = Path(location, "PERMANENT") + default_wind_path = permanent_dir / "DEFAULT_WIND" + wind_path = permanent_dir / "WIND" os.mkdir(location) - os.mkdir(os.path.join(location, "PERMANENT")) + permanent_dir.mkdir() # create DEFAULT_WIND and WIND files regioninfo = [ @@ -1918,16 +1933,8 @@ def _create_location_xy(database, location): "t-b resol: 1", ] - defwind = open(os.path.join(location, "PERMANENT", "DEFAULT_WIND"), "w") - for param in regioninfo: - defwind.write(param + "%s" % os.linesep) - defwind.close() - - shutil.copy( - os.path.join(location, "PERMANENT", "DEFAULT_WIND"), - os.path.join(location, "PERMANENT", "WIND"), - ) - + default_wind_path.write_text("\n".join(regioninfo)) + shutil.copy(default_wind_path, wind_path) os.chdir(cur_dir) except OSError as e: raise ScriptError(repr(e)) diff --git a/python/grass/script/db.py b/python/grass/script/db.py index 5591b92d4ca..46b4753bf42 100644 --- a/python/grass/script/db.py +++ b/python/grass/script/db.py @@ -94,23 +94,20 @@ def db_table_exist(table, env=None, **args): :return: True for success, False otherwise """ - nuldev = open(os.devnull, "w+") ok = True - try: - run_command( - "db.describe", - flags="c", - table=table, - stdout=nuldev, - stderr=nuldev, - env=env, - **args, - ) - except CalledModuleError: - ok = False - finally: - nuldev.close() - + with open(os.devnull, "w+") as nuldev: + try: + run_command( + "db.describe", + flags="c", + table=table, + stdout=nuldev, + stderr=nuldev, + env=env, + **args, + ) + except CalledModuleError: + ok = False return ok @@ -127,9 +124,8 @@ def db_connection(force=False, env=None): :return: parsed output of db.connect """ # noqa: E501 try: - nuldev = open(os.devnull, "w") - conn = parse_command("db.connect", flags="g", stderr=nuldev, env=env) - nuldev.close() + with open(os.devnull, "w") as nuldev: + conn = parse_command("db.connect", flags="g", stderr=nuldev, env=env) except CalledModuleError: conn = None @@ -191,9 +187,8 @@ def db_select(sql=None, filename=None, table=None, env=None, **args): except CalledModuleError: fatal(_("Fetching data failed"), env=env) - ofile = open(fname) - result = [tuple(x.rstrip(os.linesep).split(args["sep"])) for x in ofile] - ofile.close() + with open(fname) as ofile: + result = [tuple(x.rstrip(os.linesep).split(args["sep"])) for x in ofile] try_remove(fname) return tuple(result) @@ -218,16 +213,16 @@ def db_table_in_vector(table, mapset=".", env=None): """ from .vector import vector_db - nuldev = open(os.devnull, "w") used = [] vects = list_strings("vector", mapset=mapset, env=env) - for vect in vects: - for f in vector_db(vect, stderr=nuldev, env=env).values(): - if not f: - continue - if f["table"] == table: - used.append(vect) - break + with open(os.devnull, "w") as nuldev: + for vect in vects: + for f in vector_db(vect, stderr=nuldev, env=env).values(): + if not f: + continue + if f["table"] == table: + used.append(vect) + break if len(used) > 0: return used return None diff --git a/python/grass/script/imagery.py b/python/grass/script/imagery.py index 2c2c3868423..c0531541409 100644 --- a/python/grass/script/imagery.py +++ b/python/grass/script/imagery.py @@ -93,10 +93,12 @@ def group_to_dict( ) if dict_keys not in {"indices", "map_names", "semantic_labels"}: - raise ValueError(f"Invalid dictionary keys <{dict_keys}> requested") + msg = f"Invalid dictionary keys <{dict_keys}> requested" + raise ValueError(msg) if dict_values not in {"indices", "map_names", "semantic_labels", "metadata"}: - raise ValueError(f"Invalid dictionary values <{dict_values}> requested") + msg = f"Invalid dictionary values <{dict_values}> requested" + raise ValueError(msg) if subgroup and not maps_in_group: warning( diff --git a/python/grass/script/raster.py b/python/grass/script/raster.py index aaf9e0179b0..73c555a67f0 100644 --- a/python/grass/script/raster.py +++ b/python/grass/script/raster.py @@ -21,6 +21,8 @@ import os import string import time +from pathlib import Path + from .core import ( gisenv, @@ -49,27 +51,25 @@ def raster_history(map, overwrite=False, env=None): """ current_mapset = gisenv(env)["MAPSET"] - if find_file(name=map, env=env)["mapset"] == current_mapset: - if overwrite is True: - historyfile = tempfile(env=env) - f = open(historyfile, "w") - f.write(os.environ["CMDLINE"]) - f.close() - run_command("r.support", map=map, loadhistory=historyfile, env=env) - try_remove(historyfile) - else: - run_command("r.support", map=map, history=os.environ["CMDLINE"], env=env) - return True - - warning( - _( - "Unable to write history for <%(map)s>. " - "Raster map <%(map)s> not found in current mapset." + if find_file(name=map, env=env)["mapset"] != current_mapset: + warning( + _( + "Unable to write history for <%(map)s>. " + "Raster map <%(map)s> not found in current mapset." + ) + % {"map": map}, + env=env, ) - % {"map": map}, - env=env, - ) - return False + return False + + if overwrite is True: + historyfile = tempfile(env=env) + Path(historyfile).write_text(os.environ["CMDLINE"]) + run_command("r.support", map=map, loadhistory=historyfile, env=env) + try_remove(historyfile) + else: + run_command("r.support", map=map, history=os.environ["CMDLINE"], env=env) + return True def raster_info(map, env=None): @@ -218,8 +218,7 @@ def raster_what(map, coord, env=None, localized=False): [{'elevation': {'color': '255:214:000', 'label': '', 'value': '102.479'}}] :param str map: the map name - :param list coord: a list of list containing all the point that you want - query + :param list coord: a list of list containing all the point that you want to query :param env: """ map_list = [map] if isinstance(map, (bytes, str)) else map @@ -255,8 +254,7 @@ def raster_what(map, coord, env=None, localized=False): for item in ret.splitlines(): line = item.split(sep)[3:] for i, map_name in enumerate(map_list): - tmp_dict = {} - tmp_dict[map_name] = {} + tmp_dict = {map_name: {}} for j in range(len(labels)): tmp_dict[map_name][labels[j]] = line[i * len(labels) + j] diff --git a/python/grass/script/raster3d.py b/python/grass/script/raster3d.py index 1a4a2782984..8b3148be4e3 100644 --- a/python/grass/script/raster3d.py +++ b/python/grass/script/raster3d.py @@ -30,6 +30,7 @@ def raster3d_info(map, env=None): """Return information about a raster3d map (interface to `r3.info`). + Example: >>> mapcalc3d('volume = row() + col() + depth()') diff --git a/python/grass/script/setup.py b/python/grass/script/setup.py index cadf05bbe38..e57c6e81668 100644 --- a/python/grass/script/setup.py +++ b/python/grass/script/setup.py @@ -65,7 +65,7 @@ session.finish() -(C) 2010-2024 by the GRASS Development Team +(C) 2010-2025 by 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. @@ -404,9 +404,8 @@ def __enter__(self): :returns: reference to the object (self) """ if not self.active: - raise ValueError( - "Attempt to use inactive (finished) session as a context manager" - ) + msg = "Attempt to use inactive (finished) session as a context manager" + raise ValueError(msg) return self def __exit__(self, type, value, traceback): @@ -423,7 +422,8 @@ def finish(self): and finish the session. No GRASS modules can be called afterwards. """ if not self.active: - raise ValueError("Attempt to finish an already finished session") + msg = "Attempt to finish an already finished session" + raise ValueError(msg) self._active = False finish(env=self._env, start_time=self._start_time) diff --git a/python/grass/script/task.py b/python/grass/script/task.py index 758372938e3..f2dd2f96a55 100644 --- a/python/grass/script/task.py +++ b/python/grass/script/task.py @@ -86,12 +86,12 @@ def get_error_msg(self): def get_name(self): """Get task name""" - if sys.platform == "win32": - name, ext = os.path.splitext(self.name) - if ext in {".py", ".sh"}: - return name + if sys.platform != "win32": return self.name + name, ext = os.path.splitext(self.name) + if ext in {".py", ".sh"}: + return name return self.name def get_description(self, full=True): @@ -99,11 +99,11 @@ def get_description(self, full=True): :param bool full: True for label + desc """ - if self.label: - if full: - return self.label + " " + self.description - return self.label - return self.description + if not self.label: + return self.description + if full: + return self.label + " " + self.description + return self.label def get_keywords(self): """Get module's keywords""" @@ -114,22 +114,14 @@ def get_list_params(self, element="name"): :param str element: element name """ - params = [] - for p in self.params: - params.append(p[element]) - - return params + return [p[element] for p in self.params] def get_list_flags(self, element="name"): """Get list of flags :param str element: element name """ - flags = [] - for p in self.flags: - flags.append(p[element]) - - return flags + return [p[element] for p in self.flags] def get_param(self, value, element="name", raiseError=True): """Find and return a param by name @@ -313,7 +305,7 @@ def _process_module(self): self.task.label = self._get_node_text(self.root, "label") self.task.description = self._get_node_text(self.root, "description") - def _process_params(self): + def _process_params(self) -> None: """Process parameters""" for p in self.root.findall("parameter"): # gisprompt @@ -347,15 +339,12 @@ def _process_params(self): multiple = p.get("multiple", "no") == "yes" required = p.get("required", "no") == "yes" - if ( + hidden: bool = bool( self.task.blackList["enabled"] and self.task.name in self.task.blackList["items"] and p.get("name") in self.task.blackList["items"][self.task.name].get("params", []) - ): - hidden = True - else: - hidden = False + ) self.task.params.append( { @@ -380,23 +369,17 @@ def _process_params(self): } ) - def _process_flags(self): + def _process_flags(self) -> None: """Process flags""" for p in self.root.findall("flag"): - if ( + hidden: bool = bool( self.task.blackList["enabled"] and self.task.name in self.task.blackList["items"] and p.get("name") in self.task.blackList["items"][self.task.name].get("flags", []) - ): - hidden = True - else: - hidden = False + ) - if p.find("suppress_required") is not None: - suppress_required = True - else: - suppress_required = False + suppress_required: bool = bool(p.find("suppress_required") is not None) self.task.flags.append( { @@ -557,12 +540,14 @@ def command_info(cmd): :param str cmd: the command to query """ task = parse_interface(cmd) - cmdinfo = {} - - cmdinfo["description"] = task.get_description() - cmdinfo["keywords"] = task.get_keywords() - cmdinfo["flags"] = flags = task.get_options()["flags"] - cmdinfo["params"] = params = task.get_options()["params"] + flags = task.get_options()["flags"] + params = task.get_options()["params"] + cmdinfo = { + "description": task.get_description(), + "keywords": task.get_keywords(), + "flags": flags, + "params": params, + } usage = task.get_name() flags_short = [] diff --git a/python/grass/script/tests/grass_script_core_location_test.py b/python/grass/script/tests/grass_script_core_location_test.py index e04f65f211c..4a06489f6d3 100644 --- a/python/grass/script/tests/grass_script_core_location_test.py +++ b/python/grass/script/tests/grass_script_core_location_test.py @@ -35,9 +35,7 @@ def create_and_get_srid(tmp_path): """Create location on the same path as the current one""" bootstrap_location = "bootstrap" desired_location = "desired" - gs.core._create_location_xy( - tmp_path, bootstrap_location - ) # pylint: disable=protected-access + gs.core._create_location_xy(tmp_path, bootstrap_location) # pylint: disable=protected-access with gs.setup.init(tmp_path / bootstrap_location, env=os.environ.copy()) as session: gs.create_location(tmp_path, desired_location, epsg="3358") assert (tmp_path / desired_location).exists() @@ -101,9 +99,7 @@ def test_with_different_path(tmp_path): tmp_path_a = tmp_path / "a" tmp_path_b = tmp_path / "b" tmp_path_a.mkdir() - gs.core._create_location_xy( - tmp_path_a, bootstrap_location - ) # pylint: disable=protected-access + gs.core._create_location_xy(tmp_path_a, bootstrap_location) # pylint: disable=protected-access with gs.setup.init( tmp_path_a / bootstrap_location, env=os.environ.copy() ) as session: @@ -149,9 +145,7 @@ def test_files(tmp_path): """Check expected files are created""" bootstrap_location = "bootstrap" desired_location = "desired" - gs.core._create_location_xy( - tmp_path, bootstrap_location - ) # pylint: disable=protected-access + gs.core._create_location_xy(tmp_path, bootstrap_location) # pylint: disable=protected-access with gs.setup.init(tmp_path / bootstrap_location, env=os.environ.copy()): description = "This is a test (not Gauss-Krüger or Křovák)" gs.create_location(tmp_path, desired_location, epsg="3358", desc=description) diff --git a/python/grass/script/tests/grass_script_setup_test.py b/python/grass/script/tests/grass_script_setup_test.py index 36dfbb77dbb..f2593d1e0ca 100644 --- a/python/grass/script/tests/grass_script_setup_test.py +++ b/python/grass/script/tests/grass_script_setup_test.py @@ -8,6 +8,9 @@ import grass.script as gs +RUNTIME_GISBASE_SHOULD_BE_PRESENT = "Runtime (GISBASE) should be present" +SESSION_FILE_NOT_DELETED = "Session file not deleted" + xfail_mp_spawn = pytest.mark.xfail( multiprocessing.get_start_method() == "spawn", reason="Multiprocessing using 'spawn' start method requires pickable functions", @@ -91,8 +94,8 @@ def test_init_finish_global_functions_capture_strerr0_partial(tmp_path): ) session_file, runtime_present = run_in_subprocess(init_finish) assert session_file, "Expected file name from the subprocess" - assert runtime_present, "Runtime (GISBASE) should be present" - assert not os.path.exists(session_file), "Session file not deleted" + assert runtime_present, RUNTIME_GISBASE_SHOULD_BE_PRESENT + assert not os.path.exists(session_file), SESSION_FILE_NOT_DELETED @xfail_mp_spawn @@ -113,8 +116,8 @@ def init_finish(queue): session_file, runtime_present = run_in_subprocess(init_finish) assert session_file, "Expected file name from the subprocess" - assert runtime_present, "Runtime (GISBASE) should be present" - assert not os.path.exists(session_file), "Session file not deleted" + assert runtime_present, RUNTIME_GISBASE_SHOULD_BE_PRESENT + assert not os.path.exists(session_file), SESSION_FILE_NOT_DELETED @xfail_mp_spawn @@ -139,8 +142,8 @@ def init_finish(queue): init_finish ) assert session_file, "Expected file name from the subprocess" - assert runtime_present, "Runtime (GISBASE) should be present" - assert not os.path.exists(session_file), "Session file not deleted" + assert runtime_present, RUNTIME_GISBASE_SHOULD_BE_PRESENT + assert not os.path.exists(session_file), SESSION_FILE_NOT_DELETED # This is testing the current implementation behavior, but it is not required # to be this way in terms of design. assert runtime_present_after, "Runtime should continue to be present" @@ -189,7 +192,7 @@ def init_finish(queue): ) = run_in_subprocess(init_finish) # Runtime - assert runtime_present_during, "Runtime (GISBASE) should be present" + assert runtime_present_during, RUNTIME_GISBASE_SHOULD_BE_PRESENT # This is testing the current implementation behavior, but it is not required # to be this way in terms of design. assert runtime_present_after, "Expected GISBASE to be present when finished" @@ -198,7 +201,7 @@ def init_finish(queue): assert session_file_present_during, "Expected session file to be present" assert session_file_variable_present_during, "Variable GISRC should be present" assert not session_file_variable_present_after, "Not expecting GISRC when finished" - assert not os.path.exists(session_file), "Session file not deleted" + assert not os.path.exists(session_file), SESSION_FILE_NOT_DELETED @xfail_mp_spawn @@ -220,8 +223,8 @@ def workload(queue): session_file, file_existed, runtime_present = run_in_subprocess(workload) assert session_file, "Expected file name from the subprocess" assert file_existed, "File should have been present" - assert runtime_present, "Runtime (GISBASE) should be present" - assert not os.path.exists(session_file), "Session file not deleted" + assert runtime_present, RUNTIME_GISBASE_SHOULD_BE_PRESENT + assert not os.path.exists(session_file), SESSION_FILE_NOT_DELETED assert not os.environ.get("GISRC") assert not os.environ.get("GISBASE") diff --git a/python/grass/script/tests/test_script_task.py b/python/grass/script/tests/test_script_task.py index cae8735827e..aff10500d7c 100644 --- a/python/grass/script/tests/test_script_task.py +++ b/python/grass/script/tests/test_script_task.py @@ -1,11 +1,54 @@ +import os + +import pytest + +import grass.script as gs from grass.script.task import grassTask -def test_mapcalc_simple_e_name(): +@pytest.fixture +def xy_session_patched_env(tmp_path, monkeypatch): + """Active session in an XY location (scope: function), patching env vars directly. + + This allows functions not accepting an env dictionary argument to work in tests""" + location = "xy_test" + gs.core._create_location_xy(tmp_path, location) # pylint: disable=protected-access + with gs.setup.init(tmp_path / location, env=os.environ.copy()) as session: + for key, value in session.env.items(): + monkeypatch.setenv(key, value) + yield session + + +def test_mapcalc_simple_e_name(xy_session_patched_env): gt = grassTask("r.mapcalc.simple") assert gt.get_param("e")["name"] == "e" -def test_mapcalc_simple_expession_name(): +def test_mapcalc_simple_expression_name(xy_session_patched_env): gt = grassTask("r.mapcalc.simple") assert gt.get_param("expression")["name"] == "expression" + + +def test_d_vect_from_bin(xy_session_patched_env): + """Tests that a module installed in "$GISBASE/bin can be used with grassTask""" + task = grassTask("d.vect") + task.get_param("map")["value"] = "map_name" + task.get_flag("i")["value"] = True + task.get_param("layer")["value"] = 1 + task.get_param("label_bcolor")["value"] = "red" + # the default parameter display is added automatically + actual = " ".join(task.get_cmd()) + expected = "d.vect -i map=map_name layer=1 display=shape label_bcolor=red" + assert actual == expected + + +def test_v_clip_from_scripts(xy_session_patched_env): + """Tests that a module installed in "$GISBASE/scripts can be used with grassTask""" + task = grassTask("v.clip") + task.get_param("input")["value"] = "map_name" + task.get_flag("r")["value"] = True + task.get_param("clip")["value"] = "clip_map_name" + task.get_param("output")["value"] = "output_map_name" + actual = " ".join(task.get_cmd()) + expected = "v.clip -r input=map_name clip=clip_map_name output=output_map_name" + assert actual == expected diff --git a/python/grass/script/utils.py b/python/grass/script/utils.py index 7cbcc28ae4a..3684091ba73 100644 --- a/python/grass/script/utils.py +++ b/python/grass/script/utils.py @@ -17,6 +17,8 @@ .. sectionauthor:: Anna Petrasova """ +from __future__ import annotations + import os import shutil import locale @@ -29,9 +31,19 @@ import string from pathlib import Path +from typing import TYPE_CHECKING, AnyStr, Callable, TypeVar, cast, overload + + +if TYPE_CHECKING: + from _typeshed import FileDescriptorOrPath, StrOrBytesPath, StrPath + +# Type variables +T = TypeVar("T") +VT = TypeVar("VT") # Value type -def float_or_dms(s): + +def float_or_dms(s) -> float: """Convert DMS to float. >>> round(float_or_dms('26:45:30'), 5) @@ -48,7 +60,7 @@ def float_or_dms(s): return sum(float(x) / 60**n for (n, x) in enumerate(s.split(":"))) -def separator(sep): +def separator(sep: str) -> str: """Returns separator from G_OPT_F_SEP appropriately converted to character. @@ -80,7 +92,9 @@ def separator(sep): return sep -def diff_files(filename_a, filename_b): +def diff_files( + filename_a: FileDescriptorOrPath, filename_b: FileDescriptorOrPath +) -> list[str]: """Diffs two text files and returns difference. :param str filename_a: first file path @@ -91,12 +105,11 @@ def diff_files(filename_a, filename_b): import difflib differ = difflib.Differ() - fh_a = open(filename_a) - fh_b = open(filename_b) - return list(differ.compare(fh_a.readlines(), fh_b.readlines())) + with open(filename_a) as fh_a, open(filename_b) as fh_b: + return list(differ.compare(fh_a.readlines(), fh_b.readlines())) -def try_remove(path): +def try_remove(path: StrOrBytesPath) -> None: """Attempt to remove a file; no exception is generated if the attempt fails. @@ -108,7 +121,7 @@ def try_remove(path): pass -def try_rmdir(path): +def try_rmdir(path: StrOrBytesPath) -> None: """Attempt to remove a directory; no exception is generated if the attempt fails. @@ -120,23 +133,23 @@ def try_rmdir(path): shutil.rmtree(path, ignore_errors=True) -def basename(path, ext=None): +def basename(path: StrPath, ext: str | None = None) -> str: """Remove leading directory components and an optional extension from the specified path :param str path: path :param str ext: extension """ - name = os.path.basename(path) + name: str = os.path.basename(path) if not ext: return name - fs = name.rsplit(".", 1) + fs: list[str] = name.rsplit(".", 1) if len(fs) > 1 and fs[1].lower() == ext: name = fs[0] return name -class KeyValue(dict): +class KeyValue(dict[str, VT]): """A general-purpose key-value store. KeyValue is a subclass of dict, but also allows entries to be read and @@ -149,16 +162,19 @@ class KeyValue(dict): >>> reg.south = 205 >>> reg['south'] 205 + + The keys of KeyValue are strings. To use other key types, use other mapping types. + To use the attribute syntax, the keys must be valid Python attribute names. """ - def __getattr__(self, key): + def __getattr__(self, key: str) -> VT: return self[key] - def __setattr__(self, key, value): + def __setattr__(self, key: str, value: VT) -> None: self[key] = value -def _get_encoding(): +def _get_encoding() -> str: try: # Python >= 3.11 encoding = locale.getencoding() @@ -169,7 +185,7 @@ def _get_encoding(): return encoding -def decode(bytes_, encoding=None): +def decode(bytes_: AnyStr, encoding: str | None = None) -> str: """Decode bytes with default locale and return (unicode) string No-op if parameter is not bytes (assumed unicode string). @@ -193,16 +209,17 @@ def decode(bytes_, encoding=None): 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") + msg = "can only accept types str and bytes" + raise TypeError(msg) -def encode(string, encoding=None): +def encode(string: AnyStr, encoding: str | None = None) -> bytes: """Encode string with default locale and return bytes with that encoding No-op if parameter is bytes (assumed already encoded). This ensures garbage in, garbage out. - :param str string: the string to encode + :param string: the string to encode :param encoding: encoding to be used, default value is None Example @@ -221,35 +238,77 @@ def encode(string, encoding=None): 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") + msg = "Can only accept types str and bytes" + raise TypeError(msg) -def text_to_string(text, encoding=None): +def text_to_string(text: AnyStr, encoding: str | None = None) -> str: """Convert text to str. Useful when passing text into environments, in Python 2 it needs to be bytes on Windows, in Python 3 in needs unicode. """ return decode(text, encoding=encoding) -def parse_key_val(s, sep="=", dflt=None, val_type=None, vsep=None): +@overload +def parse_key_val( + s: AnyStr, + sep: str = "=", + dflt: T | None = None, + val_type: None = ..., + vsep: str | None = None, +) -> KeyValue[str | T | None]: + pass + + +@overload +def parse_key_val( + s: AnyStr, + sep: str = "=", + dflt: T | None = None, + val_type: Callable[[str], T] = ..., + vsep: str | None = None, +) -> KeyValue[T | None]: + pass + + +@overload +def parse_key_val( + s: AnyStr, + sep: str = "=", + dflt: T | None = None, + val_type: Callable[[str], T] | None = None, + vsep: str | None = None, +) -> KeyValue[str | T] | KeyValue[T | None] | KeyValue[T] | KeyValue[str | T | None]: + pass + + +def parse_key_val( + s: AnyStr, + sep: str = "=", + dflt: T | None = None, + val_type: Callable[[str], T] | None = None, + vsep: str | None = None, +) -> KeyValue[str | T] | KeyValue[T | None] | KeyValue[T] | KeyValue[str | T | None]: """Parse a string into a dictionary, where entries are separated by newlines and the key and value are separated by `sep` (default: `=`) >>> parse_key_val('min=20\\nmax=50') == {'min': '20', 'max': '50'} True - >>> parse_key_val('min=20\\nmax=50', - ... val_type=float) == {'min': 20, 'max': 50} + >>> parse_key_val('min=20\\nmax=50', val_type=float) == {'min': 20, 'max': 50} True - :param str s: string to be parsed - :param str sep: key/value separator + :param s: string to be parsed + :param sep: key/value separator :param dflt: default value to be used :param val_type: value type (None for no cast) :param vsep: vertical separator (default is Python 'universal newlines' approach) :return: parsed input (dictionary of keys/values) """ - result = KeyValue() + + result: ( + KeyValue[str | T] | KeyValue[T | None] | KeyValue[T] | KeyValue[str | T | None] + ) = KeyValue() if not s: return result @@ -259,7 +318,7 @@ def parse_key_val(s, sep="=", dflt=None, val_type=None, vsep=None): vsep = encode(vsep) if vsep else vsep if vsep: - lines = s.split(vsep) + lines: list[bytes] | list[str] = s.split(vsep) try: lines.remove("\n") except ValueError: @@ -267,15 +326,25 @@ def parse_key_val(s, sep="=", dflt=None, val_type=None, vsep=None): else: lines = s.splitlines() + if callable(val_type): + result = cast("KeyValue[T | None]", result) + for line in lines: + kv: list[bytes] | list[str] = line.split(sep, 1) + k: str = decode(kv[0].strip()) + result[k] = val_type(decode(kv[1].strip())) if len(kv) > 1 else dflt + + if dflt is not None: + result = cast("KeyValue[T]", result) + return result + + result = cast("KeyValue[str | T | None]", result) for line in lines: kv = line.split(sep, 1) k = decode(kv[0].strip()) - v = decode(kv[1].strip()) if len(kv) > 1 else dflt + result[k] = decode(kv[1].strip()) if len(kv) > 1 else dflt - if val_type: - result[k] = val_type(v) - else: - result[k] = v + if dflt is not None: + result = cast("KeyValue[str | T]", result) return result @@ -352,8 +421,8 @@ def alphanum_key(actual_key): def get_lib_path(modname, libname=None): """Return the path of the libname contained in the module.""" - from os.path import isdir, join, sep from os import getenv + from os.path import isdir, join, sep if isdir(join(getenv("GISBASE"), "etc", modname)): path = join(os.getenv("GISBASE"), "etc", modname) @@ -500,9 +569,11 @@ def legalize_vector_name(name, fallback_prefix="x"): """ # The implementation is based on Vect_legal_filename(). if not name: - raise ValueError("name cannot be empty") + msg = "name cannot be empty" + raise ValueError(msg) if fallback_prefix and re.match(r"[^A-Za-z]", fallback_prefix[0]): - raise ValueError("fallback_prefix must start with an ASCII letter") + msg = "fallback_prefix must start with an ASCII letter" + raise ValueError(msg) if fallback_prefix and re.match(r"[^A-Za-z]", name[0], flags=re.ASCII): # We prefix here rather than just replace, because in cases of unique # identifiers, e.g., columns or node names, replacing the first @@ -587,21 +658,22 @@ def append_random(name, suffix_length=None, total_length=None): :func:`append_node_pid()` description. """ if suffix_length and total_length: - raise ValueError( - "Either suffix_length or total_length can be provided, not both" - ) + msg = "Either suffix_length or total_length can be provided, not both" + raise ValueError(msg) if not suffix_length and not total_length: - raise ValueError("suffix_length or total_length has to be provided") + msg = "suffix_length or total_length has to be provided" + raise ValueError(msg) if total_length: # remove len of name and one underscore name_length = len(name) suffix_length = total_length - name_length - 1 if suffix_length <= 0: - raise ValueError( + msg = ( "No characters left for the suffix:" " total_length <{total_length}> is too small" " or name <{name}> ({name_length}) is too long".format(**locals()) ) + raise ValueError(msg) # We don't do lower and upper case because that could cause conflicts in # contexts which are case-insensitive. # We use lowercase because that's what is in UUID4 hex string. diff --git a/python/grass/script/vector.py b/python/grass/script/vector.py index 4adf3e38da1..8a05069839a 100644 --- a/python/grass/script/vector.py +++ b/python/grass/script/vector.py @@ -163,6 +163,7 @@ def vector_history(map, replace=False, env=None): def vector_info_topo(map, layer=1, env=None): """Return information about a vector map (interface to `v.info -t`). + Example: >>> vector_info_topo("geology") # doctest: +NORMALIZE_WHITESPACE diff --git a/python/grass/semantic_label/reader.py b/python/grass/semantic_label/reader.py index 591006fe352..358e7fafdf9 100644 --- a/python/grass/semantic_label/reader.py +++ b/python/grass/semantic_label/reader.py @@ -26,7 +26,8 @@ def __init__(self): os.path.join(os.environ["GISBASE"], "etc", "i.band.library", "*.json") ) if not self._json_files: - raise SemanticLabelReaderError("No semantic label definitions found") + msg = "No semantic label definitions found" + raise SemanticLabelReaderError(msg) self._read_config() @@ -38,9 +39,8 @@ def _read_config(self): with open(json_file) as fd: config = json.load(fd, object_pairs_hook=OrderedDict) except json.decoder.JSONDecodeError as e: - raise SemanticLabelReaderError( - "Unable to parse '{}': {}".format(json_file, e) - ) + msg = "Unable to parse '{}': {}".format(json_file, e) + raise SemanticLabelReaderError(msg) # check if configuration is valid self._check_config(config) @@ -58,13 +58,11 @@ def _check_config(config): for items in config.values(): for item in ("shortcut", "bands"): if item not in items.keys(): - raise SemanticLabelReaderError( - "Invalid band definition: <{}> is missing".format(item) - ) + msg = "Invalid band definition: <{}> is missing".format(item) + raise SemanticLabelReaderError(msg) if len(items["bands"]) < 1: - raise SemanticLabelReaderError( - "Invalid band definition: no bands defined" - ) + msg = "Invalid band definition: no bands defined" + raise SemanticLabelReaderError(msg) @staticmethod def _print_label_extended(label, item): @@ -116,13 +114,13 @@ def print_info(self, shortcut=None, band=None, semantic_label=None, extended=Fal if shortcut and re.match(shortcut, item["shortcut"]) is None: continue except re.error as e: - raise SemanticLabelReaderError("Invalid pattern: {}".format(e)) + msg = "Invalid pattern: {}".format(e) + raise SemanticLabelReaderError(msg) found = True if band and band not in item["bands"]: - raise SemanticLabelReaderError( - "Band <{}> not found in <{}>".format(band, shortcut) - ) + msg = "Band <{}> not found in <{}>".format(band, shortcut) + raise SemanticLabelReaderError(msg) # print generic information if extended: @@ -192,9 +190,9 @@ def get_bands(self): :return list: list of valid band identifiers """ - bands = [] - for root in self.config.values(): - for item in root.values(): - for band in item["bands"]: - bands.append("{}_{}".format(item["shortcut"], band)) - return bands + return [ + "{}_{}".format(item["shortcut"], band) + for root in self.config.values() + for item in root.values() + for band in item["bands"] + ] diff --git a/python/grass/temporal/__init__.py b/python/grass/temporal/__init__.py index 2048b66ccf0..b70fe0ab96f 100644 --- a/python/grass/temporal/__init__.py +++ b/python/grass/temporal/__init__.py @@ -1,33 +1,360 @@ -from .abstract_dataset import * -from .abstract_map_dataset import * -from .abstract_space_time_dataset import * -from .aggregation import * -from .base import * -from .c_libraries_interface import * -from .core import * -from .datetime_math import * -from .extract import * -from .factory import * -from .gui_support import * -from .list_stds import * -from .mapcalc import * -from .metadata import * -from .open_stds import * -from .register import * -from .sampling import * -from .space_time_datasets import * -from .spatial_extent import * -from .spatial_topology_dataset_connector import * -from .spatio_temporal_relationships import * -from .stds_export import * -from .stds_import import * -from .temporal_algebra import * -from .temporal_extent import * -from .temporal_granularity import * -from .temporal_operator import * -from .temporal_raster3d_algebra import * -from .temporal_raster_algebra import * -from .temporal_raster_base_algebra import * -from .temporal_topology_dataset_connector import * -from .temporal_vector_algebra import * -from .univar_statistics import * +from .abstract_dataset import ( + AbstractDataset, + AbstractDatasetComparisonKeyEndTime, + AbstractDatasetComparisonKeyStartTime, +) +from .abstract_map_dataset import AbstractMapDataset +from .abstract_space_time_dataset import AbstractSpaceTimeDataset +from .aggregation import aggregate_by_topology, aggregate_raster_maps, collect_map_names +from .base import ( + AbstractSTDSRegister, + DatasetBase, + DictSQLSerializer, + Raster3DBase, + Raster3DSTDSRegister, + RasterBase, + RasterSTDSRegister, + SQLDatabaseInterface, + STDSBase, + STR3DSBase, + STRDSBase, + STVDSBase, + VectorBase, + VectorSTDSRegister, +) +from .c_libraries_interface import CLibrariesInterface, RPCDefs, c_library_server +from .core import ( + DBConnection, + SQLDatabaseInterfaceConnection, + create_temporal_database, + get_available_temporal_mapsets, + get_current_gisdbase, + get_current_location, + get_current_mapset, + get_database_info_string, + get_enable_mapset_check, + get_enable_timestamp_write, + get_raise_on_error, + get_sql_template_path, + get_tgis_backend, + get_tgis_c_library_interface, + get_tgis_database, + get_tgis_database_string, + get_tgis_db_version, + get_tgis_db_version_from_metadata, + get_tgis_dbmi_paramstyle, + get_tgis_message_interface, + get_tgis_metadata, + get_tgis_version, + init, + init_dbif, + profile_function, + set_raise_on_error, + stop_subprocesses, + upgrade_temporal_database, +) +from .datetime_math import ( + adjust_datetime_to_granularity, + check_datetime_string, + compute_datetime_delta, + create_numeric_suffix, + create_suffix_from_datetime, + create_time_suffix, + datetime_to_grass_datetime_string, + decrement_datetime_by_string, + increment_datetime_by_string, + modify_datetime, + modify_datetime_by_string, + relative_time_to_time_delta, + relative_time_to_time_delta_seconds, + string_to_datetime, + time_delta_to_relative_time, + time_delta_to_relative_time_seconds, +) +from .extract import ( + extract_dataset, + run_mapcalc2d, + run_mapcalc3d, + run_vector_extraction, +) +from .factory import dataset_factory +from .gui_support import tlist, tlist_grouped +from .list_stds import get_dataset_list, list_maps_of_stds +from .mapcalc import dataset_mapcalculator +from .metadata import ( + Raster3DMetadata, + RasterMetadata, + RasterMetadataBase, + STDSMetadataBase, + STDSRasterMetadataBase, + STR3DSMetadata, + STRDSMetadata, + STVDSMetadata, + VectorMetadata, +) +from .open_stds import ( + check_new_map_dataset, + check_new_stds, + open_new_map_dataset, + open_new_stds, + open_old_stds, +) +from .register import ( + assign_valid_time_to_map, + register_map_object_list, + register_maps_in_space_time_dataset, +) +from .sampling import sample_stds_by_stds_topology +from .space_time_datasets import ( + Raster3DDataset, + RasterDataset, + SpaceTimeRaster3DDataset, + SpaceTimeRasterDataset, + SpaceTimeVectorDataset, + VectorDataset, +) +from .spatial_extent import ( + Raster3DSpatialExtent, + RasterSpatialExtent, + SpatialExtent, + STR3DSSpatialExtent, + STRDSSpatialExtent, + STVDSSpatialExtent, + VectorSpatialExtent, +) +from .spatial_topology_dataset_connector import SpatialTopologyDatasetConnector +from .spatio_temporal_relationships import ( + SpatioTemporalTopologyBuilder, + count_temporal_topology_relationships, + create_temporal_relation_sql_where_statement, + print_spatio_temporal_topology_relationships, + print_temporal_topology_relationships, + set_spatial_relationship, + set_temporal_relationship, +) +from .stds_export import export_stds +from .stds_import import import_stds +from .temporal_algebra import ( + FatalError, + GlobalTemporalVar, + TemporalAlgebraLexer, + TemporalAlgebraParser, +) +from .temporal_extent import ( + AbsoluteTemporalExtent, + Raster3DAbsoluteTime, + Raster3DRelativeTime, + RasterAbsoluteTime, + RasterRelativeTime, + RelativeTemporalExtent, + STDSAbsoluteTime, + STDSRelativeTime, + STR3DSAbsoluteTime, + STR3DSRelativeTime, + STRDSAbsoluteTime, + STRDSRelativeTime, + STVDSAbsoluteTime, + STVDSRelativeTime, + TemporalExtent, + VectorAbsoluteTime, + VectorRelativeTime, +) +from .temporal_granularity import ( + check_granularity_string, + compute_absolute_time_granularity, + compute_common_absolute_time_granularity, + compute_common_absolute_time_granularity_simple, + compute_common_relative_time_granularity, + compute_relative_time_granularity, + gcd, + gcd_list, + get_time_tuple_function, + gran_plural_unit, + gran_singular_unit, + gran_to_gran, +) +from .temporal_operator import TemporalOperatorLexer, TemporalOperatorParser +from .temporal_raster3d_algebra import TemporalRaster3DAlgebraParser +from .temporal_raster_algebra import TemporalRasterAlgebraParser +from .temporal_raster_base_algebra import ( + TemporalRasterAlgebraLexer, + TemporalRasterBaseAlgebraParser, +) +from .temporal_topology_dataset_connector import TemporalTopologyDatasetConnector +from .temporal_vector_algebra import ( + TemporalVectorAlgebraLexer, + TemporalVectorAlgebraParser, +) +from .univar_statistics import ( + compute_univar_stats, + print_gridded_dataset_univar_statistics, + print_vector_dataset_univar_statistics, +) + +__all__ = [ + "AbsoluteTemporalExtent", + "AbstractDataset", + "AbstractDatasetComparisonKeyEndTime", + "AbstractDatasetComparisonKeyStartTime", + "AbstractMapDataset", + "AbstractSTDSRegister", + "AbstractSpaceTimeDataset", + "CLibrariesInterface", + "DBConnection", + "DatasetBase", + "DictSQLSerializer", + "FatalError", + "GlobalTemporalVar", + "RPCDefs", + "Raster3DAbsoluteTime", + "Raster3DBase", + "Raster3DDataset", + "Raster3DMetadata", + "Raster3DRelativeTime", + "Raster3DSTDSRegister", + "Raster3DSpatialExtent", + "RasterAbsoluteTime", + "RasterBase", + "RasterDataset", + "RasterMetadata", + "RasterMetadataBase", + "RasterRelativeTime", + "RasterSTDSRegister", + "RasterSpatialExtent", + "RelativeTemporalExtent", + "SQLDatabaseInterface", + "SQLDatabaseInterfaceConnection", + "STDSAbsoluteTime", + "STDSBase", + "STDSMetadataBase", + "STDSRasterMetadataBase", + "STDSRelativeTime", + "STR3DSAbsoluteTime", + "STR3DSBase", + "STR3DSMetadata", + "STR3DSRelativeTime", + "STR3DSSpatialExtent", + "STRDSAbsoluteTime", + "STRDSBase", + "STRDSMetadata", + "STRDSRelativeTime", + "STRDSSpatialExtent", + "STVDSAbsoluteTime", + "STVDSBase", + "STVDSMetadata", + "STVDSRelativeTime", + "STVDSSpatialExtent", + "SpaceTimeRaster3DDataset", + "SpaceTimeRasterDataset", + "SpaceTimeVectorDataset", + "SpatialExtent", + "SpatialTopologyDatasetConnector", + "SpatioTemporalTopologyBuilder", + "TemporalAlgebraLexer", + "TemporalAlgebraParser", + "TemporalExtent", + "TemporalOperatorLexer", + "TemporalOperatorParser", + "TemporalRaster3DAlgebraParser", + "TemporalRasterAlgebraLexer", + "TemporalRasterAlgebraParser", + "TemporalRasterBaseAlgebraParser", + "TemporalTopologyDatasetConnector", + "TemporalVectorAlgebraLexer", + "TemporalVectorAlgebraParser", + "VectorAbsoluteTime", + "VectorBase", + "VectorDataset", + "VectorMetadata", + "VectorRelativeTime", + "VectorSTDSRegister", + "VectorSpatialExtent", + "adjust_datetime_to_granularity", + "aggregate_by_topology", + "aggregate_raster_maps", + "assign_valid_time_to_map", + "c_library_server", + "check_datetime_string", + "check_granularity_string", + "check_new_map_dataset", + "check_new_stds", + "collect_map_names", + "compute_absolute_time_granularity", + "compute_common_absolute_time_granularity", + "compute_common_absolute_time_granularity_simple", + "compute_common_relative_time_granularity", + "compute_datetime_delta", + "compute_relative_time_granularity", + "compute_univar_stats", + "count_temporal_topology_relationships", + "create_numeric_suffix", + "create_suffix_from_datetime", + "create_temporal_database", + "create_temporal_relation_sql_where_statement", + "create_time_suffix", + "dataset_factory", + "dataset_mapcalculator", + "datetime_to_grass_datetime_string", + "decrement_datetime_by_string", + "export_stds", + "extract_dataset", + "gcd", + "gcd_list", + "get_available_temporal_mapsets", + "get_current_gisdbase", + "get_current_location", + "get_current_mapset", + "get_database_info_string", + "get_dataset_list", + "get_enable_mapset_check", + "get_enable_timestamp_write", + "get_raise_on_error", + "get_sql_template_path", + "get_tgis_backend", + "get_tgis_c_library_interface", + "get_tgis_database", + "get_tgis_database_string", + "get_tgis_db_version", + "get_tgis_db_version_from_metadata", + "get_tgis_dbmi_paramstyle", + "get_tgis_message_interface", + "get_tgis_metadata", + "get_tgis_version", + "get_time_tuple_function", + "gran_plural_unit", + "gran_singular_unit", + "gran_to_gran", + "import_stds", + "increment_datetime_by_string", + "init", + "init_dbif", + "list_maps_of_stds", + "modify_datetime", + "modify_datetime_by_string", + "open_new_map_dataset", + "open_new_stds", + "open_old_stds", + "print_gridded_dataset_univar_statistics", + "print_spatio_temporal_topology_relationships", + "print_temporal_topology_relationships", + "print_vector_dataset_univar_statistics", + "profile_function", + "register_map_object_list", + "register_maps_in_space_time_dataset", + "relative_time_to_time_delta", + "relative_time_to_time_delta_seconds", + "run_mapcalc2d", + "run_mapcalc3d", + "run_vector_extraction", + "sample_stds_by_stds_topology", + "set_raise_on_error", + "set_spatial_relationship", + "set_temporal_relationship", + "stop_subprocesses", + "string_to_datetime", + "time_delta_to_relative_time", + "time_delta_to_relative_time_seconds", + "tlist", + "tlist_grouped", + "upgrade_temporal_database", +] diff --git a/python/grass/temporal/abstract_dataset.py b/python/grass/temporal/abstract_dataset.py index fefef525216..4e8cf294105 100644 --- a/python/grass/temporal/abstract_dataset.py +++ b/python/grass/temporal/abstract_dataset.py @@ -74,7 +74,7 @@ def get_number_of_relations(self): topologies must be build first using the SpatioTemporalTopologyBuilder. :return: The dictionary with relations as keys and number as values or - None in case the topology wasn't build + None in case the topology wasn't built """ if self.is_temporal_topology_build() and not self.is_spatial_topology_build(): return self.get_number_of_temporal_relations() @@ -104,11 +104,10 @@ def is_topology_build(self): :return: A dictionary with "spatial" and "temporal" as keys that have boolean values """ - d = {} - d["spatial"] = self.is_spatial_topology_build() - d["temporal"] = self.is_temporal_topology_build() - - return d + return { + "spatial": self.is_spatial_topology_build(), + "temporal": self.is_temporal_topology_build(), + } def print_topology_info(self) -> None: if self.is_temporal_topology_build(): @@ -342,8 +341,7 @@ def get_spatial_extent_as_tuple(self): Top and bottom are set to 0 in case of a two dimensional spatial extent. - :return: A the spatial extent as tuple (north, south, east, west, - top, bottom) + :return: A the spatial extent as tuple (north, south, east, west, top, bottom) """ return self.spatial_extent.get_spatial_extent_as_tuple() diff --git a/python/grass/temporal/abstract_map_dataset.py b/python/grass/temporal/abstract_map_dataset.py index c91020d4941..cb4cfc83850 100644 --- a/python/grass/temporal/abstract_map_dataset.py +++ b/python/grass/temporal/abstract_map_dataset.py @@ -84,7 +84,8 @@ def check_resolution_with_current_region(self): :return: "finer" or "coarser" """ - raise ImplementationError("This method must be implemented in the subclasses") + msg = "This method must be implemented in the subclasses" + raise ImplementationError(msg) @abstractmethod def has_grass_timestamp(self): @@ -175,8 +176,7 @@ def split_name(name: str, layer=None, mapset=None): the keyword arguments. :param name: The name of the map - :param layer: The layer of the vector map, use None in case no - layer exists + :param layer: The layer of the vector map, use None in case no layer exists :param mapset: The mapset in which the map is located :return: tuple of three elements name, layer, mapset e(:layer)@mapset" while @@ -243,11 +243,9 @@ def build_id(name: str, mapset, layer=None) -> str: :param name: The name of the map :param mapset: The mapset in which the map is located - :param layer: The layer of the vector map, use None in case no - layer exists + :param layer: The layer of the vector map, use None in case no layer exists - :return: the id of the map as "name(:layer)@mapset" where layer is - optional + :return: the id of the map as "name(:layer)@mapset" where layer is optional """ # Split given name into relevant parts @@ -420,8 +418,7 @@ def set_absolute_time(self, start_time, end_time=None) -> bool: This method only modifies this object and does not commit the modifications to the temporal database. - :param start_time: A datetime object specifying the start time of - the map + :param start_time: A datetime object specifying the start time of the map :param end_time: A datetime object specifying the end time of the map, None in case or time instance @@ -511,8 +508,7 @@ def update_absolute_time(self, start_time, end_time=None, dbif=None) -> None: grass file system based database in addition to the temporal database entry. - :param start_time: A datetime object specifying the start time of - the map + :param start_time: A datetime object specifying the start time of the map :param end_time: A datetime object specifying the end time of the map, None in case or time instance :param dbif: The database interface to be used @@ -668,8 +664,7 @@ def set_temporal_extent(self, extent) -> None: """Convenient method to set the temporal extent from a temporal extent object - :param extent: The temporal extent that should be set for - this object + :param extent: The temporal extent that should be set for this object .. code-block: : python @@ -852,8 +847,7 @@ def set_spatial_extent(self, spatial_extent) -> None: This method only modifies this object and does not commit the modifications to the temporal database. - :param spatial_extent: An object of type SpatialExtent or its - subclasses + :param spatial_extent: An object of type SpatialExtent or its subclasses .. code-block: : python @@ -942,33 +936,30 @@ def check_for_correct_time(self) -> bool: else: start, end, unit = self.get_relative_time() - if start is not None: - if end is not None: - if start >= end: - if self.get_layer() is not None: - self.msgr.error( - _( - "Map <%(id)s> with layer %(layer)s " - "has incorrect time interval, start " - "time is greater than end time" - ) - % {"id": self.get_map_id(), "layer": self.get_layer()} + if start is None: + self.msgr.error(_("Map <%s> has incorrect start time") % self.get_map_id()) + return False + if end is not None: + if start >= end: + if self.get_layer() is not None: + self.msgr.error( + _( + "Map <%(id)s> with layer %(layer)s " + "has incorrect time interval, start " + "time is greater than end time" ) - else: - self.msgr.error( - _( - "Map <%s> has incorrect time " - "interval, start time is greater " - "than end time" - ) - % (self.get_map_id()) + % {"id": self.get_map_id(), "layer": self.get_layer()} + ) + else: + self.msgr.error( + _( + "Map <%s> has incorrect time " + "interval, start time is greater " + "than end time" ) - return False - else: - self.msgr.error( - _("Map <%s> has incorrect start time") % (self.get_map_id()) - ) - return False + % (self.get_map_id()) + ) + return False return True diff --git a/python/grass/temporal/abstract_space_time_dataset.py b/python/grass/temporal/abstract_space_time_dataset.py index 74e38c12e6e..3b14e28b494 100644 --- a/python/grass/temporal/abstract_space_time_dataset.py +++ b/python/grass/temporal/abstract_space_time_dataset.py @@ -19,6 +19,7 @@ class that is the base class for all space time datasets. from abc import ABCMeta, abstractmethod from datetime import datetime from pathlib import Path +from grass.exceptions import FatalError from .abstract_dataset import AbstractDataset, AbstractDatasetComparisonKeyStartTime from .core import ( @@ -1743,17 +1744,11 @@ def _update_where_statement_by_spatial_extent( ) elif spatial_relation == "is_contained": spatial_where_template = ( - "(north <= {n}" - " AND south >= {s}" - " AND east <= {e}" - " AND west >= {w}" + "(north <= {n} AND south >= {s} AND east <= {e} AND west >= {w}" ) elif spatial_relation == "contains": spatial_where_template = ( - "(north >= {n}" - " AND south <= {s}" - " AND east >= {e}" - " AND west <= {w}" + "(north >= {n} AND south <= {s} AND east >= {e} AND west <= {w}" ) if self.get_type() == "str3ds": @@ -2463,7 +2458,9 @@ def is_map_registered(self, map_id, dbif=None): try: dbif.execute(sql, (map_id,), mapset=self.base.mapset) row = dbif.fetchone(mapset=self.base.mapset) - except: + except FatalError: + raise + except Exception: self.msgr.warning(_("Error in register table request")) raise diff --git a/python/grass/temporal/aggregation.py b/python/grass/temporal/aggregation.py index 69537ab0e49..bd4c7eb7b8f 100644 --- a/python/grass/temporal/aggregation.py +++ b/python/grass/temporal/aggregation.py @@ -166,13 +166,11 @@ def aggregate_raster_maps( # Create the r.series input file filename = gs.tempfile(True) - file = open(filename, "w") + with open(filename, "w") as out_file: + for name in inputs: + string = "%s\n" % (name) + out_file.write(string) - for name in inputs: - string = "%s\n" % (name) - file.write(string) - - file.close() # Run r.series try: if len(inputs) > 1000: @@ -364,11 +362,10 @@ def aggregate_by_topology( if len(aggregation_list) > 1: # Create the r.series input file filename = gs.tempfile(True) - file = open(filename, "w") - for name in aggregation_list: - string = "%s\n" % (name) - file.write(string) - file.close() + with open(filename, "w") as out_file: + for name in aggregation_list: + string = "%s\n" % (name) + out_file.write(string) mod = copy.deepcopy(r_series) mod(file=filename, output=output_name) diff --git a/python/grass/temporal/c_libraries_interface.py b/python/grass/temporal/c_libraries_interface.py index 5ac75f6b139..596bad50129 100644 --- a/python/grass/temporal/c_libraries_interface.py +++ b/python/grass/temporal/c_libraries_interface.py @@ -17,7 +17,7 @@ from ctypes import CFUNCTYPE, POINTER, byref, c_int, c_void_p, cast from datetime import datetime from multiprocessing import Lock, Pipe, Process -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any, Literal import grass.lib.date as libdate import grass.lib.gis as libgis @@ -33,6 +33,7 @@ from grass.script.utils import encode if TYPE_CHECKING: + from collections.abc import Callable from multiprocessing.connection import Connection from multiprocessing.synchronize import _LockLike @@ -1239,8 +1240,10 @@ def error_handler(data) -> None: libgis.G_add_error_handler(cerror_handler, None) - # Crerate the function array - functions = [0] * 50 + # Create the function array + functions: list[ + Callable[[_LockLike, Connection, Any], Literal[-1, -2] | None] | int + ] = [0] * 50 functions[RPCDefs.STOP] = _stop functions[RPCDefs.HAS_TIMESTAMP] = _has_timestamp functions[RPCDefs.WRITE_TIMESTAMP] = _write_timestamp diff --git a/python/grass/temporal/core.py b/python/grass/temporal/core.py index 73cba84b6d3..5daa99e2b20 100644 --- a/python/grass/temporal/core.py +++ b/python/grass/temporal/core.py @@ -52,7 +52,7 @@ try: import psycopg2 import psycopg2.extras -except: +except ImportError: pass import atexit @@ -414,7 +414,7 @@ def get_tgis_metadata(dbif=None): statement = "SELECT * FROM tgis_metadata;\n" dbif.execute(statement) rows = dbif.fetchall() - except: + except Exception: rows = None if connection_state_changed: @@ -630,8 +630,7 @@ def init(raise_fatal_error: bool = False, skip_db_version_check: bool = False): import sqlite3 except ImportError: msgr.error( - "Unable to locate the sqlite SQL Python interface" - " module sqlite3." + "Unable to locate the sqlite SQL Python interface module sqlite3." ) raise dbmi = sqlite3 @@ -1501,7 +1500,7 @@ def execute(self, statement, args=None): self.cursor.execute(statement, args) else: self.cursor.execute(statement) - except: + except (sqlite3.Error, psycopg2.Error): if connected: self.close() self.msgr.error(_("Unable to execute :\n %(sql)s") % {"sql": statement}) @@ -1544,7 +1543,7 @@ def execute_transaction(self, statement, mapset=None): else: self.cursor.execute(statement) self.connection.commit() - except: + except (sqlite3.Error, psycopg2.Error): if connected: self.close() self.msgr.error( diff --git a/python/grass/temporal/datetime_math.py b/python/grass/temporal/datetime_math.py index 2ce435ccf9f..a40be0aea16 100644 --- a/python/grass/temporal/datetime_math.py +++ b/python/grass/temporal/datetime_math.py @@ -1,7 +1,7 @@ """ Functions for mathematical datetime operations -(C) 2011-2013 by the GRASS Development Team +(C) 2011-2024 by 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. @@ -13,6 +13,7 @@ import copy from datetime import datetime, timedelta +from typing import TypedDict from .core import get_tgis_message_interface @@ -20,7 +21,7 @@ from dateutil import parser has_dateutil = True -except: +except (ImportError, ModuleNotFoundError): has_dateutil = False @@ -490,7 +491,20 @@ def adjust_datetime_to_granularity(mydate: datetime, granularity): ############################################################################### -def compute_datetime_delta(start, end): +class datetime_delta(TypedDict): + """Typed dictionary to return the accumulated delta in year, month, day, + hour, minute and second as well as max_days. At runtime, it is a plain dict.""" + + year: int + month: int + day: int + hour: int + minute: int + second: int + max_days: int + + +def compute_datetime_delta(start: datetime, end: datetime) -> datetime_delta: """Return a dictionary with the accumulated delta in year, month, day, hour, minute and second @@ -501,136 +515,137 @@ def compute_datetime_delta(start, end): >>> start = datetime(2001, 1, 1, 0, 0, 0) >>> end = datetime(2001, 1, 1, 0, 0, 0) >>> compute_datetime_delta(start, end) - {'hour': 0, 'month': 0, 'second': 0, 'max_days': 0, 'year': 0, 'day': 0, 'minute': 0} + {'year': 0, 'month': 0, 'day': 0, 'hour': 0, 'minute': 0, 'second': 0, 'max_days': 0} >>> start = datetime(2001, 1, 1, 0, 0, 14) >>> end = datetime(2001, 1, 1, 0, 0, 44) >>> compute_datetime_delta(start, end) - {'hour': 0, 'month': 0, 'second': 30, 'max_days': 0, 'year': 0, 'day': 0, 'minute': 0} + {'year': 0, 'month': 0, 'day': 0, 'hour': 0, 'minute': 0, 'second': 30, 'max_days': 0} >>> start = datetime(2001, 1, 1, 0, 0, 44) >>> end = datetime(2001, 1, 1, 0, 1, 14) >>> compute_datetime_delta(start, end) - {'hour': 0, 'month': 0, 'second': 30, 'max_days': 0, 'year': 0, 'day': 0, 'minute': 1} + {'year': 0, 'month': 0, 'day': 0, 'hour': 0, 'minute': 1, 'second': 30, 'max_days': 0} >>> start = datetime(2001, 1, 1, 0, 0, 30) >>> end = datetime(2001, 1, 1, 0, 5, 30) >>> compute_datetime_delta(start, end) - {'hour': 0, 'month': 0, 'second': 300, 'max_days': 0, 'year': 0, 'day': 0, 'minute': 5} + {'year': 0, 'month': 0, 'day': 0, 'hour': 0, 'minute': 5, 'second': 300, 'max_days': 0} >>> start = datetime(2001, 1, 1, 0, 0, 0) >>> end = datetime(2001, 1, 1, 0, 1, 0) >>> compute_datetime_delta(start, end) - {'hour': 0, 'month': 0, 'second': 0, 'max_days': 0, 'year': 0, 'day': 0, 'minute': 1} + {'year': 0, 'month': 0, 'day': 0, 'hour': 0, 'minute': 1, 'second': 0, 'max_days': 0} >>> start = datetime(2011, 10, 31, 0, 45, 0) >>> end = datetime(2011, 10, 31, 1, 45, 0) >>> compute_datetime_delta(start, end) - {'hour': 1, 'second': 0, 'max_days': 0, 'year': 0, 'day': 0, 'minute': 60} + {'year': 0, 'month': 0, 'day': 0, 'hour': 1, 'minute': 60, 'second': 0, 'max_days': 0} >>> start = datetime(2011, 10, 31, 0, 45, 0) >>> end = datetime(2011, 10, 31, 1, 15, 0) >>> compute_datetime_delta(start, end) - {'hour': 1, 'second': 0, 'max_days': 0, 'year': 0, 'day': 0, 'minute': 30} + {'year': 0, 'month': 0, 'day': 0, 'hour': 1, 'minute': 30, 'second': 0, 'max_days': 0} >>> start = datetime(2011, 10, 31, 0, 45, 0) >>> end = datetime(2011, 10, 31, 12, 15, 0) >>> compute_datetime_delta(start, end) - {'hour': 12, 'second': 0, 'max_days': 0, 'year': 0, 'day': 0, 'minute': 690} + {'year': 0, 'month': 0, 'day': 0, 'hour': 12, 'minute': 690, 'second': 0, 'max_days': 0} >>> start = datetime(2011, 10, 31, 0, 0, 0) >>> end = datetime(2011, 10, 31, 1, 0, 0) >>> compute_datetime_delta(start, end) - {'hour': 1, 'second': 0, 'max_days': 0, 'year': 0, 'day': 0, 'minute': 0} + {'year': 0, 'month': 0, 'day': 0, 'hour': 1, 'minute': 0, 'second': 0, 'max_days': 0} >>> start = datetime(2011, 10, 31, 0, 0, 0) >>> end = datetime(2011, 11, 1, 1, 0, 0) >>> compute_datetime_delta(start, end) - {'hour': 25, 'second': 0, 'max_days': 1, 'year': 0, 'day': 1, 'minute': 0} + {'year': 0, 'month': 0, 'day': 1, 'hour': 25, 'minute': 0, 'second': 0, 'max_days': 1} >>> start = datetime(2011, 10, 31, 12, 0, 0) >>> end = datetime(2011, 11, 1, 6, 0, 0) >>> compute_datetime_delta(start, end) - {'hour': 18, 'second': 0, 'max_days': 0, 'year': 0, 'day': 0, 'minute': 0} + {'year': 0, 'month': 0, 'day': 0, 'hour': 18, 'minute': 0, 'second': 0, 'max_days': 0} >>> start = datetime(2011, 11, 1, 0, 0, 0) >>> end = datetime(2011, 12, 1, 1, 0, 0) >>> compute_datetime_delta(start, end) - {'hour': 721, 'month': 1, 'second': 0, 'max_days': 30, 'year': 0, 'day': 0, 'minute': 0} + {'year': 0, 'month': 1, 'day': 0, 'hour': 721, 'minute': 0, 'second': 0, 'max_days': 30} >>> start = datetime(2011, 11, 1, 0, 0, 0) >>> end = datetime(2011, 11, 5, 0, 0, 0) >>> compute_datetime_delta(start, end) - {'hour': 0, 'second': 0, 'max_days': 4, 'year': 0, 'day': 4, 'minute': 0} + {'year': 0, 'month': 0, 'day': 4, 'hour': 0, 'minute': 0, 'second': 0, 'max_days': 4} >>> start = datetime(2011, 10, 6, 0, 0, 0) >>> end = datetime(2011, 11, 5, 0, 0, 0) >>> compute_datetime_delta(start, end) - {'hour': 0, 'second': 0, 'max_days': 30, 'year': 0, 'day': 30, 'minute': 0} + {'year': 0, 'month': 0, 'day': 30, 'hour': 0, 'minute': 0, 'second': 0, 'max_days': 30} >>> start = datetime(2011, 12, 2, 0, 0, 0) >>> end = datetime(2012, 1, 1, 0, 0, 0) >>> compute_datetime_delta(start, end) - {'hour': 0, 'second': 0, 'max_days': 30, 'year': 1, 'day': 30, 'minute': 0} + {'year': 1, 'month': 0, 'day': 30, 'hour': 0, 'minute': 0, 'second': 0, 'max_days': 30} >>> start = datetime(2011, 1, 1, 0, 0, 0) >>> end = datetime(2011, 2, 1, 0, 0, 0) >>> compute_datetime_delta(start, end) - {'hour': 0, 'month': 1, 'second': 0, 'max_days': 31, 'year': 0, 'day': 0, 'minute': 0} + {'year': 0, 'month': 1, 'day': 0, 'hour': 0, 'minute': 0, 'second': 0, 'max_days': 31} >>> start = datetime(2011, 12, 1, 0, 0, 0) >>> end = datetime(2012, 1, 1, 0, 0, 0) >>> compute_datetime_delta(start, end) - {'hour': 0, 'month': 1, 'second': 0, 'max_days': 31, 'year': 1, 'day': 0, 'minute': 0} + {'year': 1, 'month': 1, 'day': 0, 'hour': 0, 'minute': 0, 'second': 0, 'max_days': 31} >>> start = datetime(2011, 12, 1, 0, 0, 0) >>> end = datetime(2012, 6, 1, 0, 0, 0) >>> compute_datetime_delta(start, end) - {'hour': 0, 'month': 6, 'second': 0, 'max_days': 183, 'year': 1, 'day': 0, 'minute': 0} + {'year': 1, 'month': 6, 'day': 0, 'hour': 0, 'minute': 0, 'second': 0, 'max_days': 183} >>> start = datetime(2011, 6, 1, 0, 0, 0) >>> end = datetime(2021, 6, 1, 0, 0, 0) >>> compute_datetime_delta(start, end) - {'hour': 0, 'month': 120, 'second': 0, 'max_days': 3653, 'year': 10, 'day': 0, 'minute': 0} + {'year': 10, 'month': 120, 'day': 0, 'hour': 0, 'minute': 0, 'second': 0, 'max_days': 3653} >>> start = datetime(2011, 6, 1, 0, 0, 0) >>> end = datetime(2012, 6, 1, 12, 0, 0) >>> compute_datetime_delta(start, end) - {'hour': 8796, 'month': 12, 'second': 0, 'max_days': 366, 'year': 1, 'day': 0, 'minute': 0} + {'year': 1, 'month': 12, 'day': 0, 'hour': 8796, 'minute': 0, 'second': 0, 'max_days': 366} >>> start = datetime(2011, 6, 1, 0, 0, 0) >>> end = datetime(2012, 6, 1, 12, 30, 0) >>> compute_datetime_delta(start, end) - {'hour': 8796, 'month': 12, 'second': 0, 'max_days': 366, 'year': 1, 'day': 0, 'minute': 527790} + {'year': 1, 'month': 12, 'day': 0, 'hour': 8796, 'minute': 527790, 'second': 0, 'max_days': 366} >>> start = datetime(2011, 6, 1, 0, 0, 0) >>> end = datetime(2012, 6, 1, 12, 0, 5) >>> compute_datetime_delta(start, end) - {'hour': 8796, 'month': 12, 'second': 31665605, 'max_days': 366, 'year': 1, 'day': 0, 'minute': 0} + {'year': 1, 'month': 12, 'day': 0, 'hour': 8796, 'minute': 0, 'second': 31665605, 'max_days': 366} >>> start = datetime(2011, 6, 1, 0, 0, 0) >>> end = datetime(2012, 6, 1, 0, 30, 0) >>> compute_datetime_delta(start, end) - {'hour': 0, 'month': 12, 'second': 0, 'max_days': 366, 'year': 1, 'day': 0, 'minute': 527070} - - >>> start = datetime(2011, 12, 1, 0, 0, 0) - >>> end = datetime(2012, 6, 1, 0, 0, 0) - >>> compute_datetime_delta(start, end) - {'hour': 0, 'month': 12, 'second': 31622405, 'max_days': 366, 'year': 1, 'day': 0, 'minute': 0} + {'year': 1, 'month': 12, 'day': 0, 'hour': 0, 'minute': 527070, 'second': 0, 'max_days': 366} - :return: A dictionary with year, month, day, hour, minute and second as - keys() + :return: A dictionary with year, month, day, hour, minute, second and max_days as keys() """ # noqa: E501 - comp = {} - - day_diff = (end - start).days + # TODO: set default values here, and ensure processing below covers all situations, + # not leaking these default values + comp = datetime_delta( + year=0, + month=0, + day=0, + hour=0, + minute=0, + second=0, + max_days=(end - start).days, + ) - comp["max_days"] = day_diff + day_diff = comp["max_days"] # Date # Count full years - d = end.year - start.year - comp["year"] = d + comp["year"] = end.year - start.year # Count full months if start.month == 1 and end.month == 1: @@ -644,10 +659,7 @@ def compute_datetime_delta(start, end): comp["month"] = d # Count full days - if start.day == 1 and end.day == 1: - comp["day"] = 0 - else: - comp["day"] = day_diff + comp["day"] = 0 if start.day == 1 and end.day == 1 else day_diff # Time # Hours @@ -799,7 +811,7 @@ def check_datetime_string(time_string: str, use_dateutil: bool = True): try: return datetime.strptime(time_string, time_format) - except: + except (ValueError, TypeError): return _("Unable to parse time string: %s") % time_string @@ -836,7 +848,7 @@ def string_to_datetime(time_string: str) -> datetime | None: return time_object -def datetime_to_grass_datetime_string(dt: datetime) -> str: +def datetime_to_grass_datetime_string(dt: datetime | None) -> str: """Convert a python datetime object into a GRASS datetime string .. code-block:: python @@ -875,7 +887,8 @@ def datetime_to_grass_datetime_string(dt: datetime) -> str: ] if dt is None: - raise Exception("Empty datetime object in datetime_to_grass_datetime_string") + msg = "Empty datetime object in datetime_to_grass_datetime_string" + raise Exception(msg) # Check for time zone info in the datetime object if dt.tzinfo is not None: diff --git a/python/grass/temporal/gui_support.py b/python/grass/temporal/gui_support.py index 07097946652..e7c52d6788d 100644 --- a/python/grass/temporal/gui_support.py +++ b/python/grass/temporal/gui_support.py @@ -11,6 +11,7 @@ """ import grass.script as gs +from grass.exceptions import ScriptError from .core import get_available_temporal_mapsets, init_dbif from .factory import dataset_factory @@ -45,7 +46,7 @@ def tlist_grouped(type, group_type: bool = False, dbif=None): for type_ in types: try: tlist_result = tlist(type=type_, dbif=dbif) - except gs.ScriptError as e: + except ScriptError as e: gs.warning(e) continue diff --git a/python/grass/temporal/list_stds.py b/python/grass/temporal/list_stds.py index ea01bfb5607..7385653476f 100644 --- a/python/grass/temporal/list_stds.py +++ b/python/grass/temporal/list_stds.py @@ -188,9 +188,7 @@ def default(self, o): dict_rows = [] for row in rows: - new_row = {} - for key, value in zip(column_names, row): - new_row[key] = value + new_row = dict(zip(column_names, row)) dict_rows.append(new_row) meta = {"column_names": column_names} with _open_output_file(file) as stream: @@ -221,9 +219,7 @@ def increase_indent(self, flow: bool = False, indentless: bool = False): dict_rows = [] for row in rows: - new_row = {} - for key, value in zip(column_names, row): - new_row[key] = value + new_row = dict(zip(column_names, row)) dict_rows.append(new_row) meta = {"column_names": column_names} with _open_output_file(file) as stream: @@ -275,7 +271,8 @@ def _write_table(rows, column_names, output_format, separator, file): separator = "," _write_csv(rows=rows, column_names=column_names, separator=separator, file=file) else: - raise ValueError(f"Unknown value '{output_format}' for output_format") + msg = f"Unknown value '{output_format}' for output_format" + raise ValueError(msg) def _get_get_registered_maps_as_objects_with_method(dataset, where, method, gran, dbif): @@ -285,17 +282,17 @@ def _get_get_registered_maps_as_objects_with_method(dataset, where, method, gran return dataset.get_registered_maps_as_objects( where=where, order="start_time", dbif=dbif ) - if method == "gran": - if where: - raise ValueError( - f"The where parameter is not supported with method={method}" - ) - if gran is not None and gran != "": - return dataset.get_registered_maps_as_objects_by_granularity( - gran=gran, dbif=dbif - ) - return dataset.get_registered_maps_as_objects_by_granularity(dbif=dbif) - raise ValueError(f"Invalid method '{method}'") + if method != "gran": + msg = f"Invalid method '{method}'" + raise ValueError(msg) + if where: + msg = f"The where parameter is not supported with method={method}" + raise ValueError(msg) + if gran is not None and gran != "": + return dataset.get_registered_maps_as_objects_by_granularity( + gran=gran, dbif=dbif + ) + return dataset.get_registered_maps_as_objects_by_granularity(dbif=dbif) def _get_get_registered_maps_as_objects_delta_gran( @@ -308,11 +305,10 @@ def _get_get_registered_maps_as_objects_delta_gran( return [] if isinstance(maps[0], list): - if len(maps[0]) > 0: - first_time, unused = maps[0][0].get_temporal_extent_as_tuple() - else: + if len(maps[0]) <= 0: msgr.warning(_("Empty map list")) return [] + first_time, unused = maps[0][0].get_temporal_extent_as_tuple() else: first_time, unused = maps[0].get_temporal_extent_as_tuple() @@ -363,7 +359,8 @@ def _get_list_of_maps_delta_gran(dataset, columns, where, method, gran, dbif, ms elif column == "distance_from_begin": row.append(delta_first) else: - raise ValueError(f"Unsupported column '{column}'") + msg = f"Unsupported column '{column}'" + raise ValueError(msg) rows.append(row) return rows @@ -386,14 +383,14 @@ def _get_list_of_maps_stds( def check_columns(column_names, output_format, element_type): if element_type != "stvds" and "layer" in columns: - raise ValueError( - f"Column 'layer' is not allowed with temporal type '{element_type}'" - ) + msg = f"Column 'layer' is not allowed with temporal type '{element_type}'" + raise ValueError(msg) if output_format == "line" and len(column_names) > 1: - raise ValueError( + msg = ( f"'{output_format}' output_format can have only 1 column, " f"not {len(column_names)}" ) + raise ValueError(msg) # This method expects a list of objects for gap detection if method in {"delta", "deltagaps", "gran"}: @@ -498,7 +495,7 @@ def list_maps_of_stds( e.g: start_time < "2001-01-01" and end_time > "2001-01-01" :param separator: The field separator character between the columns :param method: String identifier to select a method out of cols, - comma,delta or deltagaps + comma, delta or deltagaps :param dbif: The database interface to be used - "cols" Print preselected columns specified by columns diff --git a/python/grass/temporal/mapcalc.py b/python/grass/temporal/mapcalc.py index b1dbb5ce2db..ad5e501f7d8 100644 --- a/python/grass/temporal/mapcalc.py +++ b/python/grass/temporal/mapcalc.py @@ -25,6 +25,9 @@ from .datetime_math import time_delta_to_relative_time from .open_stds import check_new_stds, open_new_stds, open_old_stds +_TEMPORAL_OPERATOR_SUPPORTS_ONLY_ABSOLUTE_TIME = ( + "The temporal operators <%s> support only absolute time." +) ############################################################################ @@ -87,7 +90,7 @@ def dataset_mapcalculator( :param type: The type of the dataset: "raster" or "raster3d" :param expression: The r(3).mapcalc expression :param base: The base name of the new created maps in case a - mapclac expression is provided + mapcalc expression is provided :param method: The method to be used for temporal sampling :param nprocs: The number of parallel processes to be used for mapcalc processing @@ -312,19 +315,20 @@ def dataset_mapcalculator( proc_list[proc_count].start() proc_count += 1 - if proc_count in {nprocs, num} or count == num: - proc_count = 0 - exitcodes = 0 - for proc in proc_list: - proc.join() - exitcodes += proc.exitcode + if proc_count not in {nprocs, num} and count != num: + continue + proc_count = 0 + exitcodes = 0 + for proc in proc_list: + proc.join() + exitcodes += proc.exitcode - if exitcodes != 0: - dbif.close() - msgr.fatal(_("Error while mapcalc computation")) + if exitcodes != 0: + dbif.close() + msgr.fatal(_("Error while mapcalc computation")) - # Empty process list - proc_list = [] + # Empty process list + proc_list = [] # Register the new maps in the output space time dataset msgr.message(_("Starting map registration in temporal database...")) @@ -507,74 +511,47 @@ def _parse_start_operators(expr, is_time_absolute: bool, current): if expr.find("start_year()") >= 0: if not is_time_absolute: - msgr.fatal( - _("The temporal operators <%s> support only absolute time.") - % ("start_*") - ) + msgr.fatal(_(_TEMPORAL_OPERATOR_SUPPORTS_ONLY_ABSOLUTE_TIME) % ("start_*")) expr = expr.replace("start_year()", str(start.year)) if expr.find("start_month()") >= 0: if not is_time_absolute: - msgr.fatal( - _("The temporal operators <%s> support only absolute time.") - % ("start_*") - ) + msgr.fatal(_(_TEMPORAL_OPERATOR_SUPPORTS_ONLY_ABSOLUTE_TIME) % ("start_*")) expr = expr.replace("start_month()", str(start.month)) if expr.find("start_week()") >= 0: if not is_time_absolute: - msgr.fatal( - _("The temporal operators <%s> support only absolute time.") - % ("start_*") - ) + msgr.fatal(_(_TEMPORAL_OPERATOR_SUPPORTS_ONLY_ABSOLUTE_TIME) % ("start_*")) expr = expr.replace("start_week()", str(start.isocalendar()[1])) if expr.find("start_day()") >= 0: if not is_time_absolute: - msgr.fatal( - _("The temporal operators <%s> support only absolute time.") - % ("start_*") - ) + msgr.fatal(_(_TEMPORAL_OPERATOR_SUPPORTS_ONLY_ABSOLUTE_TIME) % ("start_*")) expr = expr.replace("start_day()", str(start.day)) if expr.find("start_hour()") >= 0: if not is_time_absolute: - msgr.fatal( - _("The temporal operators <%s> support only absolute time.") - % ("start_*") - ) + msgr.fatal(_(_TEMPORAL_OPERATOR_SUPPORTS_ONLY_ABSOLUTE_TIME) % ("start_*")) expr = expr.replace("start_hour()", str(start.hour)) if expr.find("start_minute()") >= 0: if not is_time_absolute: - msgr.fatal( - _("The temporal operators <%s> support only absolute time.") - % ("start_*") - ) + msgr.fatal(_(_TEMPORAL_OPERATOR_SUPPORTS_ONLY_ABSOLUTE_TIME) % ("start_*")) expr = expr.replace("start_minute()", str(start.minute)) if expr.find("start_second()") >= 0: if not is_time_absolute: - msgr.fatal( - _("The temporal operators <%s> support only absolute time.") - % ("start_*") - ) + msgr.fatal(_(_TEMPORAL_OPERATOR_SUPPORTS_ONLY_ABSOLUTE_TIME) % ("start_*")) expr = expr.replace("start_second()", str(start.second)) if expr.find("start_dow()") >= 0: if not is_time_absolute: - msgr.fatal( - _("The temporal operators <%s> support only absolute time.") - % ("start_*") - ) + msgr.fatal(_(_TEMPORAL_OPERATOR_SUPPORTS_ONLY_ABSOLUTE_TIME) % ("start_*")) expr = expr.replace("start_dow()", str(start.isoweekday())) if expr.find("start_doy()") >= 0: if not is_time_absolute: - msgr.fatal( - _("The temporal operators <%s> support only absolute time.") - % ("start_*") - ) + msgr.fatal(_(_TEMPORAL_OPERATOR_SUPPORTS_ONLY_ABSOLUTE_TIME) % ("start_*")) year = datetime(start.year, 1, 1) delta = start - year @@ -609,9 +586,7 @@ def _parse_end_operators(expr, is_time_absolute: bool, current): if expr.find("end_year()") >= 0: if not is_time_absolute: - msgr.fatal( - _("The temporal operators <%s> support only absolute time.") % ("end_*") - ) + msgr.fatal(_(_TEMPORAL_OPERATOR_SUPPORTS_ONLY_ABSOLUTE_TIME) % ("end_*")) if not end: expr = expr.replace("end_year()", "null()") else: @@ -619,9 +594,7 @@ def _parse_end_operators(expr, is_time_absolute: bool, current): if expr.find("end_month()") >= 0: if not is_time_absolute: - msgr.fatal( - _("The temporal operators <%s> support only absolute time.") % ("end_*") - ) + msgr.fatal(_(_TEMPORAL_OPERATOR_SUPPORTS_ONLY_ABSOLUTE_TIME) % ("end_*")) if not end: expr = expr.replace("end_month()", "null()") else: @@ -629,9 +602,7 @@ def _parse_end_operators(expr, is_time_absolute: bool, current): if expr.find("end_week()") >= 0: if not is_time_absolute: - msgr.fatal( - _("The temporal operators <%s> support only absolute time.") % ("end_*") - ) + msgr.fatal(_(_TEMPORAL_OPERATOR_SUPPORTS_ONLY_ABSOLUTE_TIME) % ("end_*")) if not end: expr = expr.replace("end_week()", "null()") else: @@ -639,9 +610,7 @@ def _parse_end_operators(expr, is_time_absolute: bool, current): if expr.find("end_day()") >= 0: if not is_time_absolute: - msgr.fatal( - _("The temporal operators <%s> support only absolute time.") % ("end_*") - ) + msgr.fatal(_(_TEMPORAL_OPERATOR_SUPPORTS_ONLY_ABSOLUTE_TIME) % ("end_*")) if not end: expr = expr.replace("end_day()", "null()") else: @@ -649,9 +618,7 @@ def _parse_end_operators(expr, is_time_absolute: bool, current): if expr.find("end_hour()") >= 0: if not is_time_absolute: - msgr.fatal( - _("The temporal operators <%s> support only absolute time.") % ("end_*") - ) + msgr.fatal(_(_TEMPORAL_OPERATOR_SUPPORTS_ONLY_ABSOLUTE_TIME) % ("end_*")) if not end: expr = expr.replace("end_hour()", "null()") else: @@ -659,9 +626,7 @@ def _parse_end_operators(expr, is_time_absolute: bool, current): if expr.find("end_minute()") >= 0: if not is_time_absolute: - msgr.fatal( - _("The temporal operators <%s> support only absolute time.") % ("end_*") - ) + msgr.fatal(_(_TEMPORAL_OPERATOR_SUPPORTS_ONLY_ABSOLUTE_TIME) % ("end_*")) if not end: expr = expr.replace("end_minute()", "null()") else: @@ -669,9 +634,7 @@ def _parse_end_operators(expr, is_time_absolute: bool, current): if expr.find("end_second()") >= 0: if not is_time_absolute: - msgr.fatal( - _("The temporal operators <%s> support only absolute time.") % ("end_*") - ) + msgr.fatal(_(_TEMPORAL_OPERATOR_SUPPORTS_ONLY_ABSOLUTE_TIME) % ("end_*")) if not end: expr = expr.replace("end_second()", "null()") else: @@ -679,9 +642,7 @@ def _parse_end_operators(expr, is_time_absolute: bool, current): if expr.find("end_dow()") >= 0: if not is_time_absolute: - msgr.fatal( - _("The temporal operators <%s> support only absolute time.") % ("end_*") - ) + msgr.fatal(_(_TEMPORAL_OPERATOR_SUPPORTS_ONLY_ABSOLUTE_TIME) % ("end_*")) if not end: expr = expr.replace("end_dow()", "null()") else: @@ -689,9 +650,7 @@ def _parse_end_operators(expr, is_time_absolute: bool, current): if expr.find("end_doy()") >= 0: if not is_time_absolute: - msgr.fatal( - _("The temporal operators <%s> support only absolute time.") % ("end_*") - ) + msgr.fatal(_(_TEMPORAL_OPERATOR_SUPPORTS_ONLY_ABSOLUTE_TIME) % ("end_*")) if not end: expr = expr.replace("end_doy()", "null()") else: diff --git a/python/grass/temporal/metadata.py b/python/grass/temporal/metadata.py index 8c85141e6aa..8fc3fe2047d 100644 --- a/python/grass/temporal/metadata.py +++ b/python/grass/temporal/metadata.py @@ -1430,20 +1430,20 @@ def get_semantic_labels(self): rows = dbif.fetchall(mapset=self.mapset) dbif.close() - if rows: - string = "" - count = 0 - for row in rows: - if row["semantic_label"]: - if count == 0: - string += row["semantic_label"] - else: - string += ",%s" % row["semantic_label"] - count += 1 - - if count > 0: - return string + if not rows: return None + string = "" + count = 0 + for row in rows: + if row["semantic_label"]: + if count == 0: + string += row["semantic_label"] + else: + string += ",%s" % row["semantic_label"] + count += 1 + + if count > 0: + return string return None raster_register = property(fget=get_raster_register, fset=set_raster_register) diff --git a/python/grass/temporal/register.py b/python/grass/temporal/register.py index 0868b1a2386..b00d6631619 100644 --- a/python/grass/temporal/register.py +++ b/python/grass/temporal/register.py @@ -126,8 +126,7 @@ def register_maps_in_space_time_dataset( # create new stds only in the current mapset # remove all connections to any other mapsets # ugly hack ! - currcon = {} - currcon[mapset] = dbif.connections[mapset] + currcon = {mapset: dbif.connections[mapset]} dbif.connections = currcon # The name of the space time dataset is optional diff --git a/python/grass/temporal/sampling.py b/python/grass/temporal/sampling.py index 5730901590a..2b7ca0880e9 100644 --- a/python/grass/temporal/sampling.py +++ b/python/grass/temporal/sampling.py @@ -49,10 +49,8 @@ def sample_stds_by_stds_topology( Attention: Do not use the comma as separator for printing - :param intype: Type of the input space time dataset (strds, stvds or - str3ds) - :param sampletype: Type of the sample space time datasets (strds, - stvds or str3ds) + :param intype: Type of the input space time dataset (strds, stvds or str3ds) + :param sampletype: Type of the sample space time datasets (strds, stvds or str3ds) :param inputs: Name or comma separated names of space time datasets or a list of map names :param sampler: Name of a space time dataset used for temporal sampling diff --git a/python/grass/temporal/space_time_datasets.py b/python/grass/temporal/space_time_datasets.py index 97678e5ce23..b2fe2c2404b 100644 --- a/python/grass/temporal/space_time_datasets.py +++ b/python/grass/temporal/space_time_datasets.py @@ -429,41 +429,37 @@ def load(self) -> bool: kvp = self.ciface.read_raster_info(self.get_name(), self.get_mapset()) - if kvp: - # Fill spatial extent - self.set_spatial_extent_from_values( - north=kvp["north"], - south=kvp["south"], - east=kvp["east"], - west=kvp["west"], - ) - - # Fill metadata - self.metadata.set_nsres(kvp["nsres"]) - self.metadata.set_ewres(kvp["ewres"]) - self.metadata.set_datatype(kvp["datatype"]) - self.metadata.set_min(kvp["min"]) - self.metadata.set_max(kvp["max"]) + if not kvp: + return False + # Fill spatial extent + self.set_spatial_extent_from_values( + north=kvp["north"], south=kvp["south"], east=kvp["east"], west=kvp["west"] + ) - rows = int(kvp["rows"]) - cols = int(kvp["cols"]) + # Fill metadata + self.metadata.set_nsres(kvp["nsres"]) + self.metadata.set_ewres(kvp["ewres"]) + self.metadata.set_datatype(kvp["datatype"]) + self.metadata.set_min(kvp["min"]) + self.metadata.set_max(kvp["max"]) - ncells = cols * rows + rows = int(kvp["rows"]) + cols = int(kvp["cols"]) - self.metadata.set_cols(cols) - self.metadata.set_rows(rows) - self.metadata.set_number_of_cells(ncells) + ncells = cols * rows - # Fill semantic label if defined - semantic_label = self.ciface.read_raster_semantic_label( - self.get_name(), self.get_mapset() - ) - if semantic_label: - self.metadata.set_semantic_label(semantic_label) + self.metadata.set_cols(cols) + self.metadata.set_rows(rows) + self.metadata.set_number_of_cells(ncells) - return True + # Fill semantic label if defined + semantic_label = self.ciface.read_raster_semantic_label( + self.get_name(), self.get_mapset() + ) + if semantic_label: + self.metadata.set_semantic_label(semantic_label) - return False + return True def set_semantic_label(self, semantic_label) -> None: """Set semantic label identifier @@ -828,38 +824,37 @@ def load(self) -> bool: # Fill spatial extent kvp = self.ciface.read_raster3d_info(self.get_name(), self.get_mapset()) - if kvp: - self.set_spatial_extent_from_values( - north=kvp["north"], - south=kvp["south"], - east=kvp["east"], - west=kvp["west"], - top=kvp["top"], - bottom=kvp["bottom"], - ) - - # Fill metadata - self.metadata.set_nsres(kvp["nsres"]) - self.metadata.set_ewres(kvp["ewres"]) - self.metadata.set_tbres(kvp["tbres"]) - self.metadata.set_datatype(kvp["datatype"]) - self.metadata.set_min(kvp["min"]) - self.metadata.set_max(kvp["max"]) + if not kvp: + return False + self.set_spatial_extent_from_values( + north=kvp["north"], + south=kvp["south"], + east=kvp["east"], + west=kvp["west"], + top=kvp["top"], + bottom=kvp["bottom"], + ) - rows = int(kvp["rows"]) - cols = int(kvp["cols"]) - depths = int(kvp["depths"]) + # Fill metadata + self.metadata.set_nsres(kvp["nsres"]) + self.metadata.set_ewres(kvp["ewres"]) + self.metadata.set_tbres(kvp["tbres"]) + self.metadata.set_datatype(kvp["datatype"]) + self.metadata.set_min(kvp["min"]) + self.metadata.set_max(kvp["max"]) - ncells = cols * rows * depths + rows = int(kvp["rows"]) + cols = int(kvp["cols"]) + depths = int(kvp["depths"]) - self.metadata.set_cols(cols) - self.metadata.set_rows(rows) - self.metadata.set_depths(depths) - self.metadata.set_number_of_cells(ncells) + ncells = cols * rows * depths - return True + self.metadata.set_cols(cols) + self.metadata.set_rows(rows) + self.metadata.set_depths(depths) + self.metadata.set_number_of_cells(ncells) - return False + return True ############################################################################### @@ -1167,35 +1162,34 @@ def load(self) -> bool: # Get the data from an existing vector map kvp = self.ciface.read_vector_info(self.get_name(), self.get_mapset()) - if kvp: - # Fill spatial extent - self.set_spatial_extent_from_values( - north=kvp["north"], - south=kvp["south"], - east=kvp["east"], - west=kvp["west"], - top=kvp["top"], - bottom=kvp["bottom"], - ) + if not kvp: + return False + # Fill spatial extent + self.set_spatial_extent_from_values( + north=kvp["north"], + south=kvp["south"], + east=kvp["east"], + west=kvp["west"], + top=kvp["top"], + bottom=kvp["bottom"], + ) - # Fill metadata - self.metadata.set_3d_info(kvp["map3d"]) - self.metadata.set_number_of_points(kvp["points"]) - self.metadata.set_number_of_lines(kvp["lines"]) - self.metadata.set_number_of_boundaries(kvp["boundaries"]) - self.metadata.set_number_of_centroids(kvp["centroids"]) - self.metadata.set_number_of_faces(kvp["faces"]) - self.metadata.set_number_of_kernels(kvp["kernels"]) - self.metadata.set_number_of_primitives(kvp["primitives"]) - self.metadata.set_number_of_nodes(kvp["nodes"]) - self.metadata.set_number_of_areas(kvp["areas"]) - self.metadata.set_number_of_islands(kvp["islands"]) - self.metadata.set_number_of_holes(kvp["holes"]) - self.metadata.set_number_of_volumes(kvp["volumes"]) - - return True + # Fill metadata + self.metadata.set_3d_info(kvp["map3d"]) + self.metadata.set_number_of_points(kvp["points"]) + self.metadata.set_number_of_lines(kvp["lines"]) + self.metadata.set_number_of_boundaries(kvp["boundaries"]) + self.metadata.set_number_of_centroids(kvp["centroids"]) + self.metadata.set_number_of_faces(kvp["faces"]) + self.metadata.set_number_of_kernels(kvp["kernels"]) + self.metadata.set_number_of_primitives(kvp["primitives"]) + self.metadata.set_number_of_nodes(kvp["nodes"]) + self.metadata.set_number_of_areas(kvp["areas"]) + self.metadata.set_number_of_islands(kvp["islands"]) + self.metadata.set_number_of_holes(kvp["holes"]) + self.metadata.set_number_of_volumes(kvp["volumes"]) - return False + return True ############################################################################### diff --git a/python/grass/temporal/spatial_extent.py b/python/grass/temporal/spatial_extent.py index 3a76b18ab46..9e94d4e6758 100644 --- a/python/grass/temporal/spatial_extent.py +++ b/python/grass/temporal/spatial_extent.py @@ -868,13 +868,13 @@ def cover_2d(self, extent) -> bool: # We check that at least one edge of extent is located in self edge_count = 0 - if eW > W and eW < E: + if W < eW < E: edge_count += 1 - if eE < E and eE > W: + if W < eE < E: edge_count += 1 - if eN < N and eN > S: + if S < eN < N: edge_count += 1 - if eS > S and eS < N: + if S < eS < N: edge_count += 1 return edge_count != 0 @@ -941,21 +941,21 @@ def cover(self, extent) -> bool: # We check that at least one edge of extent is located in self edge_count = 0 - if eW > W and eW < E: + if W < eW < E: edge_count += 1 - if eE < E and eE > W: + if W < eE < E: edge_count += 1 - if eN < N and eN > S: + if S < eN < N: edge_count += 1 - if eS > S and eS < N: + if S < eS < N: edge_count += 1 - if eN < N and eN > S: + if S < eN < N: edge_count += 1 - if eS > S and eS < N: + if S < eS < N: edge_count += 1 - if eT < T and eT > B: + if B < eT < T: edge_count += 1 - if eB > B and eB < T: + if B < eB < T: edge_count += 1 return edge_count != 0 @@ -1323,7 +1323,9 @@ def disjoint(self, extent) -> bool: or self.meet(extent) ) - def spatial_relation_2d(self, extent) -> Literal[ + def spatial_relation_2d( + self, extent + ) -> Literal[ "equivalent", "contain", "in", @@ -1370,7 +1372,9 @@ def spatial_relation_2d(self, extent) -> Literal[ return "unknown" - def spatial_relation(self, extent) -> Literal[ + def spatial_relation( + self, extent + ) -> Literal[ "equivalent", "contain", "in", @@ -1712,8 +1716,7 @@ def set_spatial_extent_from_values( def set_spatial_extent(self, spatial_extent) -> None: """Set the three dimensional spatial extent - :param spatial_extent: An object of type SpatialExtent or its - subclasses + :param spatial_extent: An object of type SpatialExtent or its subclasses """ self.set_north(spatial_extent.get_north()) @@ -1749,8 +1752,7 @@ def set_spatial_extent_from_values_2d(self, north, south, east, west) -> None: def set_spatial_extent_2d(self, spatial_extent) -> None: """Set the three dimensional spatial extent - :param spatial_extent: An object of type SpatialExtent or its - subclasses + :param spatial_extent: An object of type SpatialExtent or its subclasses """ self.set_north(spatial_extent.north) diff --git a/python/grass/temporal/spatial_topology_dataset_connector.py b/python/grass/temporal/spatial_topology_dataset_connector.py index 005ef2fcc45..f7d535e2816 100644 --- a/python/grass/temporal/spatial_topology_dataset_connector.py +++ b/python/grass/temporal/spatial_topology_dataset_connector.py @@ -118,31 +118,31 @@ def get_number_of_spatial_relations(self): relations = {} try: relations["equivalent"] = len(self._spatial_topology["EQUIVALENT"]) - except: + except KeyError: relations["equivalent"] = 0 try: relations["overlap"] = len(self._spatial_topology["OVERLAP"]) - except: + except KeyError: relations["overlap"] = 0 try: relations["in"] = len(self._spatial_topology["IN"]) - except: + except KeyError: relations["in"] = 0 try: relations["contain"] = len(self._spatial_topology["CONTAIN"]) - except: + except KeyError: relations["contain"] = 0 try: relations["meet"] = len(self._spatial_topology["MEET"]) - except: + except KeyError: relations["meet"] = 0 try: relations["cover"] = len(self._spatial_topology["COVER"]) - except: + except KeyError: relations["cover"] = 0 try: relations["covered"] = len(self._spatial_topology["COVERED"]) - except: + except KeyError: relations["covered"] = 0 return relations @@ -296,7 +296,7 @@ def _generate_map_list_string(self, map_list, line_wrap: bool = True): count = 0 string = "" for map_ in map_list: - if line_wrap and count > 0 and count % 3 == 0: + if line_wrap and count > 0 and (count % 3 == 0): string += "\n | ............................ " count = 0 if count == 0: diff --git a/python/grass/temporal/spatio_temporal_relationships.py b/python/grass/temporal/spatio_temporal_relationships.py index b04687bc5b0..acdbb72a400 100644 --- a/python/grass/temporal/spatio_temporal_relationships.py +++ b/python/grass/temporal/spatio_temporal_relationships.py @@ -417,7 +417,7 @@ def _build_internal_iteratable(self, maps, spatial) -> None: The maps will be added to the object, so they can be accessed using the iterator of this class - :param maps: A sorted (by start_time)list of abstract_dataset + :param maps: A sorted (by start_time) list of abstract_dataset objects with initiated temporal extent """ self._build_iteratable(maps, spatial) @@ -586,7 +586,7 @@ def build(self, mapsA, mapsB=None, spatial=None) -> None: A = mapsA[i] B = mapsB[j] - set_temoral_relationship(A, B, relation) + set_temporal_relationship(A, B, relation) if spatial is not None: relation = mapsB[j].spatial_relation(mapsA[i]) @@ -619,7 +619,7 @@ def __contains__(self, map_) -> bool: ############################################################################### -def set_temoral_relationship(A, B, relation) -> None: +def set_temporal_relationship(A, B, relation) -> None: if relation in {"equal", "equals"}: if A != B: if not B.get_equal() or (B.get_equal() and A not in B.get_equal()): diff --git a/python/grass/temporal/stds_export.py b/python/grass/temporal/stds_export.py index c5f546aa3a4..c5a68bb9d88 100644 --- a/python/grass/temporal/stds_export.py +++ b/python/grass/temporal/stds_export.py @@ -373,36 +373,31 @@ def export_stds( # Open the tar archive to add the files tar = tarfile.open(tmp_tar_file_name, flag) - list_file = open(list_file_name, "w") - fs = "|" - - if rows: - if type_ == "strds": - if format_ in {"GTiff", "AAIGrid"}: - _export_raster_maps_as_gdal( - rows, tar, list_file, new_cwd, fs, format_, datatype, **kwargs - ) - else: - _export_raster_maps(rows, tar, list_file, new_cwd, fs) - elif type_ == "stvds": - if format_ == "GML": - _export_vector_maps_as_gml(rows, tar, list_file, new_cwd, fs) - elif format_ == "GPKG": - _export_vector_maps_as_gpkg(rows, tar, list_file, new_cwd, fs) - else: - _export_vector_maps(rows, tar, list_file, new_cwd, fs) - elif type_ == "str3ds": - _export_raster3d_maps(rows, tar, list_file, new_cwd, fs) - - list_file.close() + with open(list_file_name, "w") as list_file: + if rows: + if type_ == "strds": + if format_ in {"GTiff", "AAIGrid"}: + _export_raster_maps_as_gdal( + rows, tar, list_file, new_cwd, fs, format_, datatype, **kwargs + ) + else: + _export_raster_maps(rows, tar, list_file, new_cwd, fs) + elif type_ == "stvds": + if format_ == "GML": + _export_vector_maps_as_gml(rows, tar, list_file, new_cwd, fs) + elif format_ == "GPKG": + _export_vector_maps_as_gpkg(rows, tar, list_file, new_cwd, fs) + else: + _export_vector_maps(rows, tar, list_file, new_cwd, fs) + elif type_ == "str3ds": + _export_raster3d_maps(rows, tar, list_file, new_cwd, fs) # Write projection and metadata proj = gs.read_command("g.proj", flags="j") Path(proj_file_name).write_text(proj) - init_file = open(init_file_name, "w") # Create the init string string = "" # This is optional, if not present strds will be assumed for backward @@ -423,62 +418,66 @@ def export_stds( string += "%s=%s\n" % ("south", south) string += "%s=%s\n" % ("east", east) string += "%s=%s\n" % ("west", west) - init_file.write(string) - init_file.close() + Path(init_file_name).write_text(string) metadata = gs.read_command("t.info", type=type_, input=sp.get_id()) Path(metadata_file_name).write_text(metadata) - read_file = open(read_file_name, "w") - if type_ == "strds": + with open(read_file_name, "w") as read_file: + if type_ == "strds": + read_file.write( + "This space time raster dataset was exported with " + "t.rast.export of GRASS GIS 8\n" + ) + elif type_ == "stvds": + read_file.write( + "This space time vector dataset was exported with " + "t.vect.export of GRASS GIS 8\n" + ) + elif type_ == "str3ds": + read_file.write( + "This space time 3D raster dataset was exported " + "with t.rast3d.export of GRASS GIS 8\n" + ) + read_file.write("\n") + read_file.write("Files:\n") + if type_ == "strds": + if format_ == "GTiff": + # 123456789012345678901234567890 + read_file.write(" *.tif -- GeoTIFF raster files\n") + read_file.write(" *.color -- GRASS GIS raster color rules\n") + elif format_ == "pack": + read_file.write( + " *.pack -- GRASS raster files packed with r.pack\n" + ) + elif type_ == "stvds": + # 123456789012345678901234567890 + if format_ == "GML": + read_file.write(" *.xml -- Vector GML files\n") + else: + read_file.write( + " *.pack -- GRASS vector files packed with v.pack\n" + ) + elif type_ == "str3ds": + read_file.write( + " *.pack -- GRASS 3D raster files packed with r3.pack\n" + ) read_file.write( - "This space time raster dataset was exported with " - "t.rast.export of GRASS GIS 8\n" + "%13s -- Projection information in PROJ.4 format\n" % (proj_file_name) ) - elif type_ == "stvds": read_file.write( - "This space time vector dataset was exported with " - "t.vect.export of GRASS GIS 8\n" + "%13s -- GRASS GIS space time %s dataset information\n" + % (init_file_name, sp.get_new_map_instance(None).get_type()) ) - elif type_ == "str3ds": read_file.write( - "This space time 3D raster dataset was exported " - "with t.rast3d.export of GRASS GIS 8\n" + "%13s -- Time series file, lists all maps by name " + "with interval\n" % (list_file_name) ) - read_file.write("\n") - read_file.write("Files:\n") - if type_ == "strds": - if format_ == "GTiff": - # 123456789012345678901234567890 - read_file.write(" *.tif -- GeoTIFF raster files\n") - read_file.write(" *.color -- GRASS GIS raster color rules\n") - elif format_ == "pack": - read_file.write(" *.pack -- GRASS raster files packed with r.pack\n") - elif type_ == "stvds": - # 123456789012345678901234567890 - if format_ == "GML": - read_file.write(" *.xml -- Vector GML files\n") - else: - read_file.write(" *.pack -- GRASS vector files packed with v.pack\n") - elif type_ == "str3ds": - read_file.write(" *.pack -- GRASS 3D raster files packed with r3.pack\n") - read_file.write( - "%13s -- Projection information in PROJ.4 format\n" % (proj_file_name) - ) - read_file.write( - "%13s -- GRASS GIS space time %s dataset information\n" - % (init_file_name, sp.get_new_map_instance(None).get_type()) - ) - read_file.write( - "%13s -- Time series file, lists all maps by name " - "with interval\n" % (list_file_name) - ) - read_file.write( - " time stamps in ISO-Format. Field separator is |\n" - ) - read_file.write("%13s -- The output of t.info\n" % (metadata_file_name)) - read_file.write("%13s -- This file\n" % (read_file_name)) - read_file.close() + read_file.write( + " time stamps in ISO-Format. Field separator is |\n" + ) + read_file.write("%13s -- The output of t.info\n" % (metadata_file_name)) + read_file.write("%13s -- This file\n" % (read_file_name)) # Append the file list tar.add(list_file_name) diff --git a/python/grass/temporal/stds_import.py b/python/grass/temporal/stds_import.py index f7030b66d31..d401aceea55 100644 --- a/python/grass/temporal/stds_import.py +++ b/python/grass/temporal/stds_import.py @@ -230,8 +230,7 @@ def import_stds( :param output: The name of the output space time dataset :param directory: The extraction directory :param title: The title of the new created space time dataset - :param descr: The description of the new created - space time dataset + :param descr: The description of the new created space time dataset :param location: The name of the location that should be created, maps are imported into this location :param link: Switch to link raster maps instead importing them @@ -240,8 +239,7 @@ def import_stds( :param create: Create the location specified by the "location" parameter and exit. Do not import the space time datasets. - :param stds_type: The type of the space time dataset that - should be imported + :param stds_type: The type of the space time dataset that should be imported :param base: The base name of the new imported maps, it will be extended using a numerical index. :param memory: Cache size for raster rows, used in r.in.gdal @@ -301,7 +299,6 @@ def import_stds( # Check projection information if not location: temp_name = gs.tempfile() - temp_file = open(temp_name, "w") proj_name = os.path.abspath(proj_file_name) # We need to convert projection strings generated @@ -315,9 +312,9 @@ def import_stds( proj_name_tmp = f"{temp_name}_in_projection" Path(proj_name_tmp).write_text(proj_content) - p = gs.start_command("g.proj", flags="j", stdout=temp_file) - p.communicate() - temp_file.close() + with open(temp_name, "w") as temp_file: + p = gs.start_command("g.proj", flags="j", stdout=temp_file) + p.communicate() if not gs.compare_key_value_text_files(temp_name, proj_name_tmp, sep="="): if overr: @@ -411,13 +408,14 @@ def import_stds( mapname = filename mapid = mapname + "@" + mapset - row = {} - row["filename"] = filename - row["name"] = mapname - row["id"] = mapid - row["start"] = line_list[1].strip() - row["end"] = line_list[2].strip() - row["semantic_label"] = line_list[3].strip() if len(line_list) == 4 else "" + row = { + "filename": filename, + "name": mapname, + "id": mapid, + "start": line_list[1].strip(), + "end": line_list[2].strip(), + "semantic_label": line_list[3].strip() if len(line_list) == 4 else "", + } new_list_file.write( f"{mapname}{fs}{row['start']}{fs}{row['end']}" diff --git a/python/grass/temporal/temporal_algebra.py b/python/grass/temporal/temporal_algebra.py index 4a01af910cc..8e2d7b799e0 100644 --- a/python/grass/temporal/temporal_algebra.py +++ b/python/grass/temporal/temporal_algebra.py @@ -445,7 +445,7 @@ try: from ply import lex, yacc -except: +except ImportError: pass import copy @@ -592,7 +592,9 @@ class TemporalAlgebraLexer: # Regular expression rules for simple tokens t_T_SELECT_OPERATOR = r"\{[!]?[:][,]?[a-zA-Z\| ]*([,])?([lrudi]|left|right|union|disjoint|intersect)?\}" # noqa: E501 - t_T_HASH_OPERATOR = r"\{[#][,]?[a-zA-Z\| ]*([,])?([lrudi]|left|right|union|disjoint|intersect)?\}" # noqa: E501 + t_T_HASH_OPERATOR = ( + r"\{[#][,]?[a-zA-Z\| ]*([,])?([lrudi]|left|right|union|disjoint|intersect)?\}" # noqa: E501 + ) t_T_COMP_OPERATOR = r"\{(\|\||&&)[,][a-zA-Z\| ]*[,]?[\|&]?([,])?([lrudi]|left|right|union|disjoint|intersect)?\}" # noqa: E501 t_T_REL_OPERATOR = r"\{([a-zA-Z\| ])+\}" t_T_SELECT = r":" @@ -2532,8 +2534,7 @@ def p_statement_assign(self, t) -> None: success = resultstds.register_map(map_i, dbif) if not success: self.msgr.warning( - "Unabe to register map layers " - "in STDS %s" % (t[1]) + "Unabe to register map layers in STDS %s" % (t[1]) ) if self.dry_run is False: @@ -2857,7 +2858,7 @@ def p_t_var_expr_td_hash(self, t) -> None: map_i.condition_value.append(boolname) else: map_i.condition_value = boolname - except: + except (IndexError, AttributeError, SyntaxError): self.msgr.fatal( "Error: the given expression does not contain a correct time " "difference object." @@ -3354,7 +3355,6 @@ def p_expr_time_const(self, t) -> None: # start_doy(A, -1) # Get the start DOY from the preceding map # of the time series as a numerical constant # for the mapcalculator expression - """ expr : t_var LPAREN NAME COMMA INT RPAREN """ @@ -3371,7 +3371,7 @@ def p_expr_time_const(self, t) -> None: # Get map index and temporal extent. map_index = map_list.index(map_i) new_index = map_index + t_neighbour - if new_index < max_index and new_index >= 0: + if 0 <= new_index < max_index: # Get neighbouring map and set temporal extent. map_n = map_list[new_index] map_i_t_extent = map_i.get_temporal_extent() @@ -3398,7 +3398,8 @@ def p_error(self, t): "syntax error on line %d, position %i token %s near '%s' expression " "'%s'" % (t.lineno, t.lexpos, t.type, t.value, self.expression) ) - raise SyntaxError("Unexpected syntax error") + msg = "Unexpected syntax error" + raise SyntaxError(msg) if __name__ == "__main__": diff --git a/python/grass/temporal/temporal_granularity.py b/python/grass/temporal/temporal_granularity.py index 537868eee31..830950e40b2 100644 --- a/python/grass/temporal/temporal_granularity.py +++ b/python/grass/temporal/temporal_granularity.py @@ -90,19 +90,19 @@ def check_granularity_string(granularity, temporal_type) -> bool: if temporal_type == "absolute": try: num, unit = granularity.split(" ") - except: + except (ValueError, AttributeError): return False if unit not in SUPPORTED_GRAN: return False try: int(num) - except: + except ValueError: return False elif temporal_type == "relative": try: int(granularity) - except: + except ValueError: return False else: return False diff --git a/python/grass/temporal/temporal_operator.py b/python/grass/temporal/temporal_operator.py index 6eabede2959..34f872508c1 100644 --- a/python/grass/temporal/temporal_operator.py +++ b/python/grass/temporal/temporal_operator.py @@ -350,7 +350,7 @@ def p_relation_operator(self, t): | CLPAREN relationlist CRPAREN """ # Check for correct type. - if not self.optype == "relation": + if self.optype != "relation": raise SyntaxError('Wrong optype "%s" must be "relation"' % self.optype) # Set three operator components. @@ -372,7 +372,7 @@ def p_relation_bool_operator(self, t): | CLPAREN OR OR COMMA relationlist CRPAREN | CLPAREN AND AND COMMA relationlist CRPAREN """ - if not self.optype == "boolean": + if self.optype != "boolean": raise SyntaxError('Wrong optype "%s" must be "boolean"' % self.optype) # Set three operator components. @@ -399,7 +399,7 @@ def p_relation_bool_combi_operator(self, t): | CLPAREN AND AND COMMA relationlist COMMA OR CRPAREN | CLPAREN AND AND COMMA relationlist COMMA AND CRPAREN """ - if not self.optype == "boolean": + if self.optype != "boolean": raise SyntaxError('Wrong optype "%s" must be "boolean"' % self.optype) # Set three operator components. @@ -422,7 +422,7 @@ def p_relation_bool_combi_operator2(self, t): | CLPAREN OR OR COMMA relationlist COMMA temporal CRPAREN | CLPAREN AND AND COMMA relationlist COMMA temporal CRPAREN """ - if not self.optype == "boolean": + if self.optype != "boolean": raise SyntaxError('Wrong optype "%s" must be "boolean"' % self.optype) # Set three operator components. @@ -449,7 +449,7 @@ def p_relation_bool_combi_operator3(self, t): | CLPAREN AND AND COMMA relationlist COMMA OR COMMA temporal CRPAREN | CLPAREN AND AND COMMA relationlist COMMA AND COMMA temporal CRPAREN """ - if not self.optype == "boolean": + if self.optype != "boolean": raise SyntaxError('Wrong optype "%s" must be "relation"' % self.optype) # Set three operator components. @@ -475,7 +475,7 @@ def p_select_relation_operator(self, t): | CLPAREN select COMMA relation COMMA temporal CRPAREN | CLPAREN select COMMA relationlist COMMA temporal CRPAREN """ - if not self.optype == "select": + if self.optype != "select": raise SyntaxError('Wrong optype "%s" must be "select"' % self.optype) if len(t) == 4: @@ -512,7 +512,7 @@ def p_hash_relation_operator(self, t): | CLPAREN HASH COMMA relation COMMA temporal CRPAREN | CLPAREN HASH COMMA relationlist COMMA temporal CRPAREN """ - if not self.optype == "hash": + if self.optype != "hash": raise SyntaxError('Wrong optype "%s" must be "hash"' % self.optype) if len(t) == 4: @@ -549,7 +549,7 @@ def p_raster_relation_operator(self, t): | CLPAREN arithmetic COMMA relation COMMA temporal CRPAREN | CLPAREN arithmetic COMMA relationlist COMMA temporal CRPAREN """ - if not self.optype == "raster": + if self.optype != "raster": raise SyntaxError('Wrong optype "%s" must be "raster"' % self.optype) if len(t) == 4: @@ -586,7 +586,7 @@ def p_overlay_relation_operator(self, t): | CLPAREN overlay COMMA relation COMMA temporal CRPAREN | CLPAREN overlay COMMA relationlist COMMA temporal CRPAREN """ - if not self.optype == "overlay": + if self.optype != "overlay": raise SyntaxError('Wrong optype "%s" must be "overlay"' % self.optype) if len(t) == 4: diff --git a/python/grass/temporal/temporal_raster3d_algebra.py b/python/grass/temporal/temporal_raster3d_algebra.py index 93817952fff..fdf80661429 100644 --- a/python/grass/temporal/temporal_raster3d_algebra.py +++ b/python/grass/temporal/temporal_raster3d_algebra.py @@ -124,7 +124,7 @@ def p_ts_neighbor_operation(self, t) -> None: # Get map index and temporal extent. map_index = maplist.index(map_i) new_index = map_index + t_neighbor - if new_index < max_index and new_index >= 0: + if 0 <= new_index < max_index: map_i_t_extent = map_i.get_temporal_extent() # Get neighboring map and set temporal extent. map_n = maplist[new_index] diff --git a/python/grass/temporal/temporal_raster_algebra.py b/python/grass/temporal/temporal_raster_algebra.py index 5b49da5841a..053c768ae73 100644 --- a/python/grass/temporal/temporal_raster_algebra.py +++ b/python/grass/temporal/temporal_raster_algebra.py @@ -168,7 +168,7 @@ def p_ts_neighbour_operation(self, t) -> None: # Get map index and temporal extent. map_index = maplist.index(map_i) new_index = map_index + t_neighbour - if new_index < max_index and new_index >= 0: + if 0 <= new_index < max_index: map_i_t_extent = map_i.get_temporal_extent() # Get neighbouring map and set temporal extent. map_n = maplist[new_index] diff --git a/python/grass/temporal/temporal_raster_base_algebra.py b/python/grass/temporal/temporal_raster_base_algebra.py index b533d96e6f6..26243756b41 100644 --- a/python/grass/temporal/temporal_raster_base_algebra.py +++ b/python/grass/temporal/temporal_raster_base_algebra.py @@ -227,9 +227,9 @@ def build_spatio_temporal_topology_list( :param count_map: Boolean if the number of topological related maps should be returned. :param compare_bool: Boolean for comparing boolean map values based on - related map list and compariosn operator. + related map list and comparison operator. :param compare_cmd: Boolean for comparing command list values based on - related map list and compariosn operator. + related map list and comparison operator. :param compop: Comparison operator, && or ||. :param aggregate: Aggregation operator for relation map list, & or |. :param new: Boolean if new temporary maps should be created. @@ -332,26 +332,26 @@ def build_spatio_temporal_topology_list( spatial_relations = map_i.get_spatial_relations() for temporal_topology in temporal_topo_list: - if temporal_topology.upper() in temporal_relations.keys(): - if ( - self._check_spatial_topology_entries( - spatial_topo_list, spatial_relations - ) - is True - ): - if count_map: - relationmaplist = temporal_relations[ - temporal_topology.upper() - ] - gvar = GlobalTemporalVar() - gvar.td = len(relationmaplist) - if "map_value" in dir(map_i): - map_i.map_value.append(gvar) - else: - map_i.map_value = gvar - # Use unique identifier, since map names may be equal - resultdict[map_i.uid] = map_i - # map_i.print_info() + if temporal_topology.upper() not in temporal_relations.keys(): + continue + if ( + self._check_spatial_topology_entries( + spatial_topo_list, spatial_relations + ) + is not True + ): + continue + if count_map: + relationmaplist = temporal_relations[temporal_topology.upper()] + gvar = GlobalTemporalVar() + gvar.td = len(relationmaplist) + if "map_value" in dir(map_i): + map_i.map_value.append(gvar) + else: + map_i.map_value = gvar + # Use unique identifier, since map names may be equal + resultdict[map_i.uid] = map_i + # map_i.print_info() resultlist = resultdict.values() @@ -461,33 +461,36 @@ def compare_cmd_value( temporal_relations = map_i.get_temporal_relations() for topo in temporal_topo_list: - if topo.upper() in temporal_relations.keys(): - relationmaplist = temporal_relations[topo.upper()] - if count == 0 and "cmd_list" in dir(map_i): - cmd_value_list.extend((compop, "(")) - for relationmap in relationmaplist: - if ( - self._check_spatial_topology_relation( - spatial_topo_list, map_i, relationmap - ) - is True - ): - if convert and "condition_value" in dir(relationmap): - if relationmap.condition_value != []: - cmdstring = str(int(relationmap.condition_value[0])) - relationmap.cmd_list = cmdstring - if "cmd_list" in dir(relationmap): - if count > 0: - cmd_value_list.append(aggregate + aggregate) - cmd_value_list.append(relationmap.cmd_list) - count += 1 - if self.debug: - print( - "compare_cmd_value", - map_i.get_id(), - relationmap.get_id(), - relationmap.cmd_list, - ) + if topo.upper() not in temporal_relations.keys(): + continue + relationmaplist = temporal_relations[topo.upper()] + if count == 0 and "cmd_list" in dir(map_i): + cmd_value_list.extend((compop, "(")) + for relationmap in relationmaplist: + if ( + self._check_spatial_topology_relation( + spatial_topo_list, map_i, relationmap + ) + is not True + ): + continue + if convert and "condition_value" in dir(relationmap): + if relationmap.condition_value != []: + cmdstring = str(int(relationmap.condition_value[0])) + relationmap.cmd_list = cmdstring + if "cmd_list" in dir(relationmap): + if count > 0: + cmd_value_list.append(aggregate + aggregate) + cmd_value_list.append(relationmap.cmd_list) + count += 1 + if self.debug: + print( + "compare_cmd_value", + map_i.get_id(), + relationmap.get_id(), + relationmap.cmd_list, + ) + if count > 0: cmd_value_list.append(")") cmd_value_str = "".join(map(str, cmd_value_list)) @@ -519,28 +522,30 @@ def operator_cmd_value( leftcmd = map_i cmdstring = "" for topo in temporal_topo_list: - if topo.upper() in temporal_relations.keys(): - relationmaplist = temporal_relations[topo.upper()] - for relationmap in relationmaplist: - if ( - self._check_spatial_topology_relation( - spatial_topo_list, map_i, relationmap - ) - is True - ): - # Create r.mapcalc expression string for the operation. - cmdstring = self.build_command_string( - leftcmd, relationmap, operator=operator, cmd_type="operator" + if topo.upper() not in temporal_relations.keys(): + continue + relationmaplist = temporal_relations[topo.upper()] + for relationmap in relationmaplist: + if ( + self._check_spatial_topology_relation( + spatial_topo_list, map_i, relationmap + ) + is True + ): + # Create r.mapcalc expression string for the operation. + cmdstring = self.build_command_string( + leftcmd, relationmap, operator=operator, cmd_type="operator" + ) + leftcmd = cmdstring + + if self.debug: + print( + "operator_cmd_value", + map_i.get_id(), + operator, + relationmap.get_id(), ) - leftcmd = cmdstring - if self.debug: - print( - "operator_cmd_value", - map_i.get_id(), - operator, - relationmap.get_id(), - ) # Add command list to result map. map_i.cmd_list = cmdstring if self.debug: @@ -583,56 +588,54 @@ def set_temporal_extent_list( base_map=map_i, bool_op="and", copy=True, rename=True ) - # Combine temporal and spatial extents of intermediate map with related - # maps. + # Combine temporal and spatial extents of intermediate map with related maps for topo in topolist: - if topo in tbrelations.keys(): - for map_j in tbrelations[topo]: - if ( - self._check_spatial_topology_relation( - spatial_topo_list, map_i, map_j - ) - is True - ): - if temporal == "r": - # Generate an intermediate map for the result map list. - map_new = self.generate_new_map( - base_map=map_i, - bool_op="and", - copy=True, - rename=True, - ) - # Create overlaid map extent. - returncode = self.overlay_map_extent( - map_new, map_j, "and", temp_op=temporal - ) + if topo not in tbrelations.keys(): + continue + for map_j in tbrelations[topo]: + if ( + self._check_spatial_topology_relation( + spatial_topo_list, map_i, map_j + ) + is not True + ): + continue + if temporal == "r": + # Generate an intermediate map for the result map list. + map_new = self.generate_new_map( + base_map=map_i, + bool_op="and", + copy=True, + rename=True, + ) + # Create overlaid map extent. + returncode = self.overlay_map_extent( + map_new, map_j, "and", temp_op=temporal + ) - # Stop the loop if no temporal or spatial relationship - # exist. - if returncode == 0: - break - # Append map to result map list. - if returncode == 1: - # print(map_new.cmd_list) - # resultlist.append(map_new) - if cmd_bool: - # Create r.mapcalc expression string for the - # operation. - cmdstring = self.build_command_string( - map_i, - map_j, - operator=operator, - cmd_type=cmd_type, - ) - # Conditional append of module command. - map_new.cmd_list = cmdstring - # Write map object to result dictionary. - resultdict[map_new.uid] = map_new + # Stop the loop if no temporal or spatial relationship + # exist. if returncode == 0: break - # Append map to result map list. - # if returncode == 1: - # resultlist.append(map_new) + # Append map to result map list. + if returncode == 1: + # print(map_new.cmd_list) + # resultlist.append(map_new) + if cmd_bool: + # Create r.mapcalc expression string for the + # operation. + cmdstring = self.build_command_string( + map_i, + map_j, + operator=operator, + cmd_type=cmd_type, + ) + # Conditional append of module command. + map_new.cmd_list = cmdstring + # Write map object to result dictionary. + resultdict[map_new.uid] = map_new + if returncode == 0: + break # Get sorted map objects as values from result dictionary. resultlist = resultdict.values() return sorted(resultlist, key=AbstractDatasetComparisonKeyStartTime) diff --git a/python/grass/temporal/temporal_vector_algebra.py b/python/grass/temporal/temporal_vector_algebra.py index 099a56b89e5..6fc88303919 100644 --- a/python/grass/temporal/temporal_vector_algebra.py +++ b/python/grass/temporal/temporal_vector_algebra.py @@ -272,17 +272,18 @@ def build_spatio_temporal_topology_list( self.overlay_cmd_value(map_i, tbrelations, compop, topolist) for topo in topolist: - if topo.upper() in tbrelations.keys(): - if count_map: - relationmaplist = tbrelations[topo.upper()] - gvar = GlobalTemporalVar() - gvar.td = len(relationmaplist) - if "map_value" in dir(map_i): - map_i.map_value.append(gvar) - else: - map_i.map_value = gvar - # Use unique identifier, since map names may be equal - resultdict[map_i.uid] = map_i + if topo.upper() not in tbrelations.keys(): + continue + if count_map: + relationmaplist = tbrelations[topo.upper()] + gvar = GlobalTemporalVar() + gvar.td = len(relationmaplist) + if "map_value" in dir(map_i): + map_i.map_value.append(gvar) + else: + map_i.map_value = gvar + # Use unique identifier, since map names may be equal + resultdict[map_i.uid] = map_i resultlist = resultdict.values() # Sort list of maps chronological. @@ -297,7 +298,7 @@ def overlay_cmd_value(self, map_i, tbrelations, function, topolist=["EQUAL"]): :param function: Overlay operator, &|+^~. :return: Map object with command list with operators that has been - evaluated by implicit aggregration. + evaluated by implicit aggregation. """ # Build comandlist list with elements from related maps and given relation # operator. @@ -311,41 +312,38 @@ def overlay_cmd_value(self, map_i, tbrelations, function, topolist=["EQUAL"]): if "cmd_list" in dir(map_i): resultlist += map_i.cmd_list for topo in topolist: - if topo.upper() in tbrelations.keys(): - relationmaplist = tbrelations[topo.upper()] - for relationmap in relationmaplist: - # Append command list of given map to result command list. - if "cmd_list" in dir(relationmap): - resultlist += relationmap.cmd_list - # Generate an intermediate name - name = self.generate_map_name() - # Put it into the removalbe map list - self.removable_maps[name] = VectorDataset( - name + "@%s" % (self.mapset) - ) - map_i.set_id(name + "@" + self.mapset) - # Set second input for overlay module. - mapbinput = relationmap.get_id() - # Create module command in PyGRASS for v.overlay and v.patch. - if operator != "disor": - m = copy.deepcopy(self.m_overlay) - m.run_ = False - m.inputs["operator"].value = operator - m.inputs["ainput"].value = str(mapainput) - m.inputs["binput"].value = str(mapbinput) - m.outputs["output"].value = name - m.flags["overwrite"].value = self.overwrite - else: - patchinput = str(mapainput) + "," + str(mapbinput) - m = copy.deepcopy(self.m_patch) - m.run_ = False - m.inputs["input"].value = patchinput - m.outputs["output"].value = name - m.flags["overwrite"].value = self.overwrite - # Conditional append of module command. - resultlist.append(m) - # Set new map name to temporary map name. - mapainput = name + if topo.upper() not in tbrelations.keys(): + continue + relationmaplist = tbrelations[topo.upper()] + for relationmap in relationmaplist: + # Append command list of given map to result command list. + if "cmd_list" in dir(relationmap): + resultlist += relationmap.cmd_list + # Generate an intermediate name + name = self.generate_map_name() + # Put it into the removalbe map list + self.removable_maps[name] = VectorDataset(name + "@%s" % (self.mapset)) + map_i.set_id(name + "@" + self.mapset) + # Set second input for overlay module. + mapbinput = relationmap.get_id() + # Create module command in PyGRASS for v.overlay and v.patch. + if operator != "disor": + m = copy.deepcopy(self.m_overlay) + m.run_ = False + m.inputs["operator"].value = operator + m.inputs["ainput"].value = str(mapainput) + m.inputs["binput"].value = str(mapbinput) + else: + patchinput = str(mapainput) + "," + str(mapbinput) + m = copy.deepcopy(self.m_patch) + m.run_ = False + m.inputs["input"].value = patchinput + m.outputs["output"].value = name + m.flags["overwrite"].value = self.overwrite + # Conditional append of module command. + resultlist.append(m) + # Set new map name to temporary map name. + mapainput = name # Add command list to result map. map_i.cmd_list = resultlist @@ -375,30 +373,31 @@ def set_temporal_extent_list(self, maplist, topolist=["EQUAL"], temporal="l"): ) # Combine temporal and spatial extents of intermediate map with related maps for topo in topolist: - if topo in tbrelations.keys(): - for map_j in tbrelations[topo]: - if temporal == "r": - # Generate an intermediate map for the result map list. - map_new = self.generate_new_map( - base_map=map_i, - bool_op="and", - copy=True, - rename=False, - remove=True, - ) - # Create overlaid map extent. - returncode = self.overlay_map_extent( - map_new, map_j, "and", temp_op=temporal + if topo not in tbrelations.keys(): + continue + for map_j in tbrelations[topo]: + if temporal == "r": + # Generate an intermediate map for the result map list. + map_new = self.generate_new_map( + base_map=map_i, + bool_op="and", + copy=True, + rename=False, + remove=True, ) - # Stop the loop if no temporal or spatial relationship exist. - if returncode == 0: - break - # Append map to result map list. - if returncode == 1: - # resultlist.append(map_new) - resultdict[map_new.get_id()] = map_new + # Create overlaid map extent. + returncode = self.overlay_map_extent( + map_new, map_j, "and", temp_op=temporal + ) + # Stop the loop if no temporal or spatial relationship exist. if returncode == 0: break + # Append map to result map list. + if returncode == 1: + # resultlist.append(map_new) + resultdict[map_new.get_id()] = map_new + if returncode == 0: + break # Append map to result map list. # if returncode == 1: # resultlist.append(map_new) @@ -496,42 +495,43 @@ def p_statement_assign(self, t): map_i.set_id(newident + "@" + mapset) count += 1 register_list.append(map_i) - else: - # Test if temporal extents have been changed by temporal - # relation operators (i|r). This is a code copy from - # temporal_algebra.py - map_i_extent = map_i.get_temporal_extent_as_tuple() - map_test = map_i.get_new_instance(map_i.get_id()) - map_test.select(dbif) - map_test_extent = map_test.get_temporal_extent_as_tuple() - if map_test_extent != map_i_extent: - # Create new map with basename - newident = self.basename + "_" + str(count).zfill(leadzero) - map_result = map_i.get_new_instance( - newident + "@" + self.mapset - ) - if map_test.map_exists() and self.overwrite is False: - self.msgr.fatal( - "Error raster maps with basename %s exist. " - "Use --o flag to overwrite existing file" - % (mapname) - ) + continue + + # Test if temporal extents have been changed by temporal + # relation operators (i|r). This is a code copy from + # temporal_algebra.py + map_i_extent = map_i.get_temporal_extent_as_tuple() + map_test = map_i.get_new_instance(map_i.get_id()) + map_test.select(dbif) + map_test_extent = map_test.get_temporal_extent_as_tuple() + if map_test_extent != map_i_extent: + # Create new map with basename + newident = self.basename + "_" + str(count).zfill(leadzero) + map_result = map_i.get_new_instance( + newident + "@" + self.mapset + ) - map_result.set_temporal_extent(map_i.get_temporal_extent()) - map_result.set_spatial_extent(map_i.get_spatial_extent()) - # Attention we attach a new attribute - map_result.is_new = True - count += 1 - register_list.append(map_result) + if map_test.map_exists() and self.overwrite is False: + self.msgr.fatal( + "Error raster maps with basename %s exist. " + "Use --o flag to overwrite existing file" % (mapname) + ) - # Copy the map - m = copy.deepcopy(self.m_copy) - m.inputs["vector"].value = map_i.get_id(), newident - m.flags["overwrite"].value = self.overwrite - m.run() - else: - register_list.append(map_i) + map_result.set_temporal_extent(map_i.get_temporal_extent()) + map_result.set_spatial_extent(map_i.get_spatial_extent()) + # Attention we attach a new attribute + map_result.is_new = True + count += 1 + register_list.append(map_result) + + # Copy the map + m = copy.deepcopy(self.m_copy) + m.inputs["vector"].value = map_i.get_id(), newident + m.flags["overwrite"].value = self.overwrite + m.run() + else: + register_list.append(map_i) if len(register_list) > 0: # Create result space time dataset. @@ -547,9 +547,9 @@ def p_statement_assign(self, t): ) for map_i in register_list: # Check if modules should be executed from command list. + map_i.load() if hasattr(map_i, "cmd_list") or hasattr(map_i, "is_new"): # Get meta data from grass database. - map_i.load() if map_i.is_in_db(dbif=dbif) and self.overwrite: # Update map in temporal database. map_i.update_all(dbif=dbif) @@ -568,7 +568,7 @@ def p_statement_assign(self, t): map_i.insert(dbif=dbif) else: # Map is original from an input STVDS - map_i.load() + pass # Register map in result space time dataset. if self.debug: print(map_i.get_temporal_extent_as_tuple()) diff --git a/python/grass/temporal/tests/test_datetime_math_compute_datetime_delta_test.py b/python/grass/temporal/tests/test_datetime_math_compute_datetime_delta_test.py new file mode 100644 index 00000000000..dbcbaa2830f --- /dev/null +++ b/python/grass/temporal/tests/test_datetime_math_compute_datetime_delta_test.py @@ -0,0 +1,289 @@ +from datetime import datetime +from grass.temporal.datetime_math import compute_datetime_delta, datetime_delta + + +def test_datetime_delta_typeddict_dict() -> None: + assert datetime_delta( + month=3, day=2, minute=5, year=1, hour=8, max_days=455, second=30 + ) == dict( # noqa: C408 + month=3, day=2, minute=5, year=1, hour=8, max_days=455, second=30 + ) + + +def test_datetime_delta_typeddict_dict_literal() -> None: + assert datetime_delta( + month=3, day=2, minute=5, year=1, hour=8, max_days=455, second=30 + ) == { + "month": 3, + "day": 2, + "minute": 5, + "year": 1, + "hour": 8, + "max_days": 455, + "second": 30, + } + + +def test_datetime_delta_typeddict_dict_different_order() -> None: + assert datetime_delta( + month=3, day=2, minute=5, year=1, hour=8, max_days=455, second=30 + ) == { + "max_days": 455, + "month": 3, + "minute": 5, + "year": 1, + "day": 2, + "hour": 8, + "second": 30, + } + + +def test_datetime_delta_typeddict_typeddict_different_order() -> None: + assert datetime_delta( + year=1, + month=3, + day=2, + hour=8, + minute=5, + second=30, + max_days=455, + ) == datetime_delta( + month=3, + day=2, + minute=5, + year=1, + hour=8, + max_days=455, + second=30, + ) + + +def test_datetime_delta_typeddict_dict_missing() -> None: + assert datetime_delta( + month=3, day=2, minute=5, year=1, hour=8, max_days=455, second=30 + ) != {"month": 3, "day": 2, "minute": 5, "hour": 8, "max_days": 455} + + +def test_compute_datetime_delta_same_date() -> None: + start = datetime(2001, 1, 1, 0, 0, 0) + end = datetime(2001, 1, 1, 0, 0, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=0, day=0, hour=0, minute=0, second=0, max_days=0 + ) + + +def test_compute_datetime_delta_seconds_only() -> None: + start = datetime(2001, 1, 1, 0, 0, 14) + end = datetime(2001, 1, 1, 0, 0, 44) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=0, day=0, hour=0, minute=0, second=30, max_days=0 + ) + + +def test_compute_datetime_delta_minute_seconds() -> None: + start = datetime(2001, 1, 1, 0, 0, 44) + end = datetime(2001, 1, 1, 0, 1, 14) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=0, day=0, hour=0, minute=1, second=30, max_days=0 + ) + + +def test_compute_datetime_delta_minutes_seconds_same() -> None: + start = datetime(2001, 1, 1, 0, 0, 30) + end = datetime(2001, 1, 1, 0, 5, 30) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=0, day=0, hour=0, minute=5, second=300, max_days=0 + ) + + +def test_compute_datetime_delta_minute_only() -> None: + start = datetime(2001, 1, 1, 0, 0, 0) + end = datetime(2001, 1, 1, 0, 1, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=0, day=0, hour=0, minute=1, second=0, max_days=0 + ) + + +def test_compute_datetime_delta_hour_minutes_same() -> None: + start = datetime(2011, 10, 31, 0, 45, 0) + end = datetime(2011, 10, 31, 1, 45, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=0, day=0, hour=1, minute=60, second=0, max_days=0 + ) + + +def test_compute_datetime_delta_hour_minutes() -> None: + start = datetime(2011, 10, 31, 0, 45, 0) + end = datetime(2011, 10, 31, 1, 15, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=0, day=0, hour=1, minute=30, second=0, max_days=0 + ) + + +def test_compute_datetime_delta_hours_minutes() -> None: + start = datetime(2011, 10, 31, 0, 45, 0) + end = datetime(2011, 10, 31, 12, 15, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=0, day=0, hour=12, minute=690, second=0, max_days=0 + ) + + +def test_compute_datetime_delta_hour_only() -> None: + start = datetime(2011, 10, 31, 0, 0, 0) + end = datetime(2011, 10, 31, 1, 0, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=0, day=0, hour=1, minute=0, second=0, max_days=0 + ) + + +def test_compute_datetime_delta_month_day_hour() -> None: + start = datetime(2011, 10, 31, 0, 0, 0) + end = datetime(2011, 11, 1, 1, 0, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=0, day=1, hour=25, minute=0, second=0, max_days=1 + ) + + +def test_compute_datetime_delta_month_day_hours() -> None: + start = datetime(2011, 10, 31, 12, 0, 0) + end = datetime(2011, 11, 1, 6, 0, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=0, day=0, hour=18, minute=0, second=0, max_days=0 + ) + + +def test_compute_datetime_delta_month_hour() -> None: + start = datetime(2011, 11, 1, 0, 0, 0) + end = datetime(2011, 12, 1, 1, 0, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=1, day=0, hour=721, minute=0, second=0, max_days=30 + ) + + +def test_compute_datetime_delta_days_only() -> None: + start = datetime(2011, 11, 1, 0, 0, 0) + end = datetime(2011, 11, 5, 0, 0, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=0, day=4, hour=0, minute=0, second=0, max_days=4 + ) + + +def test_compute_datetime_delta_month_days_less_than_month() -> None: + start = datetime(2011, 10, 6, 0, 0, 0) + end = datetime(2011, 11, 5, 0, 0, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=0, day=30, hour=0, minute=0, second=0, max_days=30 + ) + + +def test_compute_datetime_delta_year_month_days_less_than_month() -> None: + start = datetime(2011, 12, 2, 0, 0, 0) + end = datetime(2012, 1, 1, 0, 0, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=1, month=0, day=30, hour=0, minute=0, second=0, max_days=30 + ) + + +def test_compute_datetime_delta_month_only() -> None: + start = datetime(2011, 1, 1, 0, 0, 0) + end = datetime(2011, 2, 1, 0, 0, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=1, day=0, hour=0, minute=0, second=0, max_days=31 + ) + + +def test_compute_datetime_delta_year_month() -> None: + start = datetime(2011, 12, 1, 0, 0, 0) + end = datetime(2012, 1, 1, 0, 0, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=1, month=1, day=0, hour=0, minute=0, second=0, max_days=31 + ) + + +def test_compute_datetime_delta_year_months() -> None: + start = datetime(2011, 12, 1, 0, 0, 0) + end = datetime(2012, 6, 1, 0, 0, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=1, month=6, day=0, hour=0, minute=0, second=0, max_days=183 + ) + + +def test_compute_datetime_delta_years_only() -> None: + start = datetime(2011, 6, 1, 0, 0, 0) + end = datetime(2021, 6, 1, 0, 0, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=10, month=120, day=0, hour=0, minute=0, second=0, max_days=3653 + ) + + +def test_compute_datetime_delta_year_hours() -> None: + start = datetime(2011, 6, 1, 0, 0, 0) + end = datetime(2012, 6, 1, 12, 0, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=1, month=12, day=0, hour=8796, minute=0, second=0, max_days=366 + ) + + +def test_compute_datetime_delta_year_hours_minutes() -> None: + start = datetime(2011, 6, 1, 0, 0, 0) + end = datetime(2012, 6, 1, 12, 30, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=1, month=12, day=0, hour=8796, minute=527790, second=0, max_days=366 + ) + + +def test_compute_datetime_delta_year_hours_seconds() -> None: + start = datetime(2011, 6, 1, 0, 0, 0) + end = datetime(2012, 6, 1, 12, 0, 5) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=1, month=12, day=0, hour=8796, minute=0, second=31665605, max_days=366 + ) + + +def test_compute_datetime_delta_year_minutes() -> None: + start = datetime(2011, 6, 1, 0, 0, 0) + end = datetime(2012, 6, 1, 0, 30, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=1, month=12, day=0, hour=0, minute=527070, second=0, max_days=366 + ) + + +def test_compute_datetime_delta_year_seconds() -> None: + start = datetime(2011, 6, 1, 0, 0, 0) + end = datetime(2012, 6, 1, 0, 0, 5) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=1, month=12, day=0, hour=0, minute=0, second=31622405, max_days=366 + ) + + +def test_compute_datetime_delta_month_full() -> None: + start = datetime(2011, 7, 3, 0, 0, 0) + end = datetime(2011, 8, 2, 0, 0, 0) + comp: datetime_delta = compute_datetime_delta(start, end) + assert comp == datetime_delta( + year=0, month=0, day=30, hour=0, minute=0, second=0, max_days=30 + ) diff --git a/python/grass/temporal/testsuite/test_temporal_doctests.py b/python/grass/temporal/testsuite/test_temporal_doctests.py index cc80c3225cb..608bf9fff38 100644 --- a/python/grass/temporal/testsuite/test_temporal_doctests.py +++ b/python/grass/temporal/testsuite/test_temporal_doctests.py @@ -8,8 +8,7 @@ import grass.gunittest.case import grass.gunittest.main import grass.gunittest.utils - -import grass.temporal +import grass.temporal as tgis doctest.DocFileCase = type( "DocFileCase", (grass.gunittest.case.TestCase,), dict(doctest.DocFileCase.__dict__) @@ -23,35 +22,31 @@ def load_tests(loader, tests, ignore): grass.gunittest.utils.do_doctest_gettext_workaround() - tests.addTests(doctest.DocTestSuite(grass.temporal.abstract_dataset)) - tests.addTests(doctest.DocTestSuite(grass.temporal.abstract_map_dataset)) - tests.addTests(doctest.DocTestSuite(grass.temporal.abstract_space_time_dataset)) - tests.addTests(doctest.DocTestSuite(grass.temporal.base)) - tests.addTests(doctest.DocTestSuite(grass.temporal.core)) - tests.addTests(doctest.DocTestSuite(grass.temporal.datetime_math)) + tests.addTests(doctest.DocTestSuite(tgis.abstract_dataset)) + tests.addTests(doctest.DocTestSuite(tgis.abstract_map_dataset)) + tests.addTests(doctest.DocTestSuite(tgis.abstract_space_time_dataset)) + tests.addTests(doctest.DocTestSuite(tgis.base)) + tests.addTests(doctest.DocTestSuite(tgis.core)) + tests.addTests(doctest.DocTestSuite(tgis.datetime_math)) # Unexpected error here - # tests.addTests(doctest.DocTestSuite(grass.temporal.list_stds)) - tests.addTests(doctest.DocTestSuite(grass.temporal.metadata)) - tests.addTests(doctest.DocTestSuite(grass.temporal.register)) - tests.addTests(doctest.DocTestSuite(grass.temporal.space_time_datasets)) - tests.addTests(doctest.DocTestSuite(grass.temporal.spatial_extent)) - tests.addTests( - doctest.DocTestSuite(grass.temporal.spatial_topology_dataset_connector) - ) - tests.addTests(doctest.DocTestSuite(grass.temporal.spatio_temporal_relationships)) - tests.addTests(doctest.DocTestSuite(grass.temporal.temporal_extent)) - tests.addTests(doctest.DocTestSuite(grass.temporal.temporal_granularity)) - tests.addTests( - doctest.DocTestSuite(grass.temporal.temporal_topology_dataset_connector) - ) + # tests.addTests(doctest.DocTestSuite(tgis.list_stds)) + tests.addTests(doctest.DocTestSuite(tgis.metadata)) + tests.addTests(doctest.DocTestSuite(tgis.register)) + tests.addTests(doctest.DocTestSuite(tgis.space_time_datasets)) + tests.addTests(doctest.DocTestSuite(tgis.spatial_extent)) + tests.addTests(doctest.DocTestSuite(tgis.spatial_topology_dataset_connector)) + tests.addTests(doctest.DocTestSuite(tgis.spatio_temporal_relationships)) + tests.addTests(doctest.DocTestSuite(tgis.temporal_extent)) + tests.addTests(doctest.DocTestSuite(tgis.temporal_granularity)) + tests.addTests(doctest.DocTestSuite(tgis.temporal_topology_dataset_connector)) # Algebra is still very experimental - tests.addTests(doctest.DocTestSuite(grass.temporal.temporal_algebra)) - tests.addTests(doctest.DocTestSuite(grass.temporal.temporal_raster3d_algebra)) - tests.addTests(doctest.DocTestSuite(grass.temporal.temporal_raster_algebra)) - tests.addTests(doctest.DocTestSuite(grass.temporal.temporal_raster_base_algebra)) - tests.addTests(doctest.DocTestSuite(grass.temporal.temporal_operator)) - tests.addTests(doctest.DocTestSuite(grass.temporal.temporal_vector_algebra)) - tests.addTests(doctest.DocTestSuite(grass.temporal.c_libraries_interface)) + tests.addTests(doctest.DocTestSuite(tgis.temporal_algebra)) + tests.addTests(doctest.DocTestSuite(tgis.temporal_raster3d_algebra)) + tests.addTests(doctest.DocTestSuite(tgis.temporal_raster_algebra)) + tests.addTests(doctest.DocTestSuite(tgis.temporal_raster_base_algebra)) + tests.addTests(doctest.DocTestSuite(tgis.temporal_operator)) + tests.addTests(doctest.DocTestSuite(tgis.temporal_vector_algebra)) + tests.addTests(doctest.DocTestSuite(tgis.c_libraries_interface)) return tests diff --git a/python/grass/temporal/testsuite/unittests_temporal_raster_algebra.py b/python/grass/temporal/testsuite/unittests_temporal_raster_algebra.py index ad4114d4e3e..6dcf9267632 100644 --- a/python/grass/temporal/testsuite/unittests_temporal_raster_algebra.py +++ b/python/grass/temporal/testsuite/unittests_temporal_raster_algebra.py @@ -677,8 +677,8 @@ def test_map_function2(self) -> None: self.assertEqual(D.check_temporal_topology(), True) self.assertEqual(D.get_granularity(), "1 day") - def test_temporal_select(self) -> None: - """Testing the temporal select operator.""" + def test_temporal_select_same_left_right(self) -> None: + """Testing the temporal select operator with the same map for left and right.""" tra = tgis.TemporalRasterAlgebraParser(run=True, debug=True) tra.parse(expression="R = A : A", basename="r", overwrite=True) diff --git a/python/grass/temporal/testsuite/unittests_temporal_raster_algebra_grs.py b/python/grass/temporal/testsuite/unittests_temporal_raster_algebra_grs.py index 15c95d6ea27..4e575d97377 100644 --- a/python/grass/temporal/testsuite/unittests_temporal_raster_algebra_grs.py +++ b/python/grass/temporal/testsuite/unittests_temporal_raster_algebra_grs.py @@ -148,9 +148,8 @@ def tearDown(self) -> None: @classmethod def tearDownClass(cls) -> None: + """Remove the temporary region""" return - """Remove the temporary region - """ cls.runModule("t.remove", flags="rf", inputs="A,B,C,D", quiet=True) cls.runModule("t.unregister", maps="singletmap", quiet=True) cls.del_temp_region() diff --git a/python/grass/temporal/unit_tests.py b/python/grass/temporal/unit_tests.py index 8275e012f25..63ff4a0778d 100644 --- a/python/grass/temporal/unit_tests.py +++ b/python/grass/temporal/unit_tests.py @@ -21,7 +21,7 @@ AbstractDatasetComparisonKeyStartTime, ) from .core import init -from .datetime_math import compute_datetime_delta, increment_datetime_by_string +from .datetime_math import increment_datetime_by_string from .space_time_datasets import RasterDataset from .spatial_extent import SpatialExtent from .spatio_temporal_relationships import SpatioTemporalTopologyBuilder @@ -223,349 +223,6 @@ def test_adjust_datetime_to_granularity() -> None: ############################################################################### -def test_compute_datetime_delta() -> None: - print("Test 1") - start = datetime(2001, 1, 1, 0, 0, 0) - end = datetime(2001, 1, 1, 0, 0, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["second"] - correct = 0 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 2") - start = datetime(2001, 1, 1, 0, 0, 14) - end = datetime(2001, 1, 1, 0, 0, 44) - - comp = compute_datetime_delta(start, end) - - result = comp["second"] - correct = 30 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 3") - start = datetime(2001, 1, 1, 0, 0, 44) - end = datetime(2001, 1, 1, 0, 1, 14) - - comp = compute_datetime_delta(start, end) - - result = comp["second"] - correct = 30 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 4") - start = datetime(2001, 1, 1, 0, 0, 30) - end = datetime(2001, 1, 1, 0, 5, 30) - - comp = compute_datetime_delta(start, end) - - result = comp["second"] - correct = 300 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 5") - start = datetime(2001, 1, 1, 0, 0, 0) - end = datetime(2001, 1, 1, 0, 1, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["minute"] - correct = 1 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 6") - start = datetime(2011, 10, 31, 0, 45, 0) - end = datetime(2011, 10, 31, 1, 45, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["minute"] - correct = 60 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 7") - start = datetime(2011, 10, 31, 0, 45, 0) - end = datetime(2011, 10, 31, 1, 15, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["minute"] - correct = 30 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 8") - start = datetime(2011, 10, 31, 0, 45, 0) - end = datetime(2011, 10, 31, 12, 15, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["minute"] - correct = 690 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 9") - start = datetime(2011, 10, 31, 0, 0, 0) - end = datetime(2011, 10, 31, 1, 0, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["hour"] - correct = 1 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 10") - start = datetime(2011, 10, 31, 0, 0, 0) - end = datetime(2011, 11, 1, 1, 0, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["hour"] - correct = 25 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 11") - start = datetime(2011, 10, 31, 12, 0, 0) - end = datetime(2011, 11, 1, 6, 0, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["hour"] - correct = 18 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 12") - start = datetime(2011, 11, 1, 0, 0, 0) - end = datetime(2011, 12, 1, 1, 0, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["hour"] - correct = 30 * 24 + 1 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 13") - start = datetime(2011, 11, 1, 0, 0, 0) - end = datetime(2011, 11, 5, 0, 0, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["day"] - correct = 4 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 14") - start = datetime(2011, 10, 6, 0, 0, 0) - end = datetime(2011, 11, 5, 0, 0, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["day"] - correct = 30 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 15") - start = datetime(2011, 12, 2, 0, 0, 0) - end = datetime(2012, 1, 1, 0, 0, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["day"] - correct = 30 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 16") - start = datetime(2011, 1, 1, 0, 0, 0) - end = datetime(2011, 2, 1, 0, 0, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["month"] - correct = 1 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 17") - start = datetime(2011, 12, 1, 0, 0, 0) - end = datetime(2012, 1, 1, 0, 0, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["month"] - correct = 1 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 18") - start = datetime(2011, 12, 1, 0, 0, 0) - end = datetime(2012, 6, 1, 0, 0, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["month"] - correct = 6 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 19") - start = datetime(2011, 6, 1, 0, 0, 0) - end = datetime(2021, 6, 1, 0, 0, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["year"] - correct = 10 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 20") - start = datetime(2011, 6, 1, 0, 0, 0) - end = datetime(2012, 6, 1, 12, 0, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["hour"] - d = end - start - correct = 12 + d.days * 24 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 21") - start = datetime(2011, 6, 1, 0, 0, 0) - end = datetime(2012, 6, 1, 12, 30, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["minute"] - d = end - start - correct = d.days * 24 * 60 + 12 * 60 + 30 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 22") - start = datetime(2011, 6, 1, 0, 0, 0) - end = datetime(2012, 6, 1, 12, 0, 5) - - comp = compute_datetime_delta(start, end) - - result = comp["second"] - d = end - start - correct = 5 + 60 * 60 * 12 + d.days * 24 * 60 * 60 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 23") - start = datetime(2011, 6, 1, 0, 0, 0) - end = datetime(2012, 6, 1, 0, 30, 0) - - comp = compute_datetime_delta(start, end) - - result = comp["minute"] - d = end - start - correct = 30 + d.days * 24 * 60 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - print("Test 24") - start = datetime(2011, 6, 1, 0, 0, 0) - end = datetime(2012, 6, 1, 0, 0, 5) - - comp = compute_datetime_delta(start, end) - - result = comp["second"] - d = end - start - correct = 5 + d.days * 24 * 60 * 60 - - delta = correct - result - - if delta != 0: - core.fatal("Compute datetime delta is wrong %s" % (delta)) - - def test_compute_absolute_time_granularity() -> None: # First we test intervals print("Test 1") @@ -1669,7 +1326,6 @@ def test_4d_rtree() -> None: test_adjust_datetime_to_granularity() test_spatial_extent_intersection() test_compute_absolute_time_granularity() - test_compute_datetime_delta() test_spatial_extent_intersection() test_spatial_relations() test_temporal_topology_builder() diff --git a/python/grass/temporal/univar_statistics.py b/python/grass/temporal/univar_statistics.py index f5b25414b43..9e2dfb6e467 100755 --- a/python/grass/temporal/univar_statistics.py +++ b/python/grass/temporal/univar_statistics.py @@ -92,15 +92,15 @@ def compute_univar_stats( eol = "\n" else: eol = "" - string += f'{fs}{stats["mean"]}{fs}{stats["min"]}' - string += f'{fs}{stats["max"]}{fs}{stats["mean_of_abs"]}' - string += f'{fs}{stats["stddev"]}{fs}{stats["variance"]}' - string += f'{fs}{stats["coeff_var"]}{fs}{stats["sum"]}' - string += f'{fs}{stats["null_cells"]}{fs}{stats["n"]}' - string += f'{fs}{stats["n"]}' + string += f"{fs}{stats['mean']}{fs}{stats['min']}" + string += f"{fs}{stats['max']}{fs}{stats['mean_of_abs']}" + string += f"{fs}{stats['stddev']}{fs}{stats['variance']}" + string += f"{fs}{stats['coeff_var']}{fs}{stats['sum']}" + string += f"{fs}{stats['null_cells']}{fs}{stats['n']}" + string += f"{fs}{stats['n']}" if "median" in stats: - string += f'{fs}{stats["first_quartile"]}{fs}{stats["median"]}' - string += f'{fs}{stats["third_quartile"]}' + string += f"{fs}{stats['first_quartile']}{fs}{stats['median']}" + string += f"{fs}{stats['third_quartile']}" if stats_module.inputs.percentile: for perc in stats_module.inputs.percentile: perc_value = stats[ diff --git a/raster/r.buffer/r.buffer.html b/raster/r.buffer/r.buffer.html index e0033f0345c..c8a3913b95a 100644 --- a/raster/r.buffer/r.buffer.html +++ b/raster/r.buffer/r.buffer.html @@ -32,10 +32,10 @@

    NOTES

    The user has the option of identifying up to 250 continuous zones. The zones are identified by specifying the upper limit of each desired -zone (r.buffer assumes that 0 is the starting +zone (r.buffer assumes that 0 is the starting point). "Continuous" is used in the sense that each category zone's lower value is the previous zone's upper value. The first buffer zone -always has distance 0 as its lower bound. Buffer distances +always has distance 0 as its lower bound. Buffer distances can be specified using one of five units with the units parameter.

    diff --git a/raster/r.buffer/testsuite/test_buffer.py b/raster/r.buffer/testsuite/test_buffer.py index 97fea861104..e207a7f5e8b 100644 --- a/raster/r.buffer/testsuite/test_buffer.py +++ b/raster/r.buffer/testsuite/test_buffer.py @@ -5,7 +5,6 @@ class TestRBuffer(TestCase): - @classmethod def setUpClass(cls): """Set up a temporary region for testing.""" diff --git a/raster/r.buildvrt/r.buildvrt.html b/raster/r.buildvrt/r.buildvrt.html index d5118ba5150..f69ebd1e8ef 100644 --- a/raster/r.buildvrt/r.buildvrt.html +++ b/raster/r.buildvrt/r.buildvrt.html @@ -22,7 +22,7 @@

    NOTES

    A GRASS virtual raster can be regarded as a simplified version of GDAL's -virtual raster format. +virtual raster format. The GRASS equivalent is simpler because issues like nodata, projection, resolution, resampling, masking are already handled by native GRASS raster routines. @@ -72,7 +72,7 @@

    SEE ALSO

    The equivalent GDAL utility -gdalbuildvrt +gdalbuildvrt

    AUTHOR

    diff --git a/raster/r.carve/r.carve.html b/raster/r.carve/r.carve.html index f10fdcb6312..3d36aa45256 100644 --- a/raster/r.carve/r.carve.html +++ b/raster/r.carve/r.carve.html @@ -118,7 +118,7 @@

    KNOWN ISSUES

    REFERENCES

    -Terrain +Terrain modeling and Soil Erosion Simulations for Fort Hood and Fort Polk test areas, by Helena Mitasova, Lubos Mitas, William M. Brown, Douglas M. Johnston, GMSL (Report for CERL 1999) diff --git a/raster/r.category/r.category.html b/raster/r.category/r.category.html index b0d5dc7c9bb..7c40974aa51 100644 --- a/raster/r.category/r.category.html +++ b/raster/r.category/r.category.html @@ -45,7 +45,7 @@

    Input from a file

    val1:val2:Label -If the filename is given as "-", the category labels are read from stdin +If the filename is given as "-", the category labels are read from stdin

    Default and dynamic category labels

    @@ -68,26 +68,26 @@

    Default and dynamic category labels

    In the format line

      -
    • $1 refers to the value num*5.0+1000 (ie, using the first 2 coefficients)
    • -
    • $2 refers to the value num*5.0+1005 (ie, using the last 2 coefficients)
    • +
    • $1 refers to the value num*5.0+1000 (ie, using the first 2 coefficients)
    • +
    • $2 refers to the value num*5.0+1005 (ie, using the last 2 coefficients)
    - $1.2 will print $1 with 2 decimal places. -

    Also, the form $?xxx$yyy$ translates into yyy if the category is 1, xxx -otherwise. The $yyy$ is optional. Thus -

    $1 meter$?s + $1.2 will print $1 with 2 decimal places. +

    Also, the form $?xxx$yyy$ translates into yyy if the category is 1, xxx +otherwise. The $yyy$ is optional. Thus +

    $1 meter$?s

    will become:
    - 1 meter (for category 1)
    - 2 meters (for category 2), etc. + 1 meter (for category 1)
    + 2 meters (for category 2), etc.

    -format='Elevation: $1.2 to $2.2 feet' ## Format Statement
    -coefficients="5.0,1000,5.0,1005" ## Coefficients +format='Elevation: $1.2 to $2.2 feet' ## Format Statement
    +coefficients="5.0,1000,5.0,1005" ## Coefficients

    The format and coefficients above would be used to generate the following statement in creation of the format appropriate category string for category "num":

    -sprintf(buff,"Elevation: %.2f to %.2f feet", num*5.0+1000, num*5.0*1005) +sprintf(buff,"Elevation: %.2f to %.2f feet", num*5.0+1000, num*5.0*1005)

    Note: while both the format and coefficient lines must be present a blank line for the format string will effectively suppress @@ -99,9 +99,9 @@

    Default and dynamic category labels

    know that i-th rule maps fp range to i, thus we know for sure that cats.labels[i] corresponds to i-th quant rule --> -

    To use a "$" in the label without triggering the plural test, -put "$$" in the format string. -

    Use 'single quotes' when using a "$" on the command line to +

    To use a "$" in the label without triggering the plural test, +put "$$" in the format string. +

    Use 'single quotes' when using a "$" on the command line to avoid unwanted shell substitution.

    EXAMPLES

    @@ -133,7 +133,7 @@

    Printing categories

    prints only the category values and labels for landclass96 map layer -categories 2 and 5 through 7. +categories 2 and 5 through 7.

    @@ -143,7 +143,7 @@ 

    Printing categories

    prints the values and labels for landclass96 map layer categories -3 and 4, but uses "," (instead of a tab) +3 and 4, but uses "," (instead of a tab) as the character separating the category values from the category values in the output. diff --git a/raster/r.circle/r.circle.html b/raster/r.circle/r.circle.html index b3ed725fc84..dc1b4c9d34f 100644 --- a/raster/r.circle/r.circle.html +++ b/raster/r.circle/r.circle.html @@ -1,6 +1,6 @@

    DESCRIPTION

    -This module creates an output raster map centered on the x,y values specified +r.circle creates an output raster map centered on the x,y values specified with the coordinate parameter, out to the edge of the current region. The output cell values increase linearly from the specified center. The min and max parameters control the inner and outer output raster map radii, respectively. diff --git a/raster/r.circle/testsuite/test_circle.py b/raster/r.circle/testsuite/test_circle.py index 1c0a4b62f54..e6fd2019e03 100644 --- a/raster/r.circle/testsuite/test_circle.py +++ b/raster/r.circle/testsuite/test_circle.py @@ -5,7 +5,6 @@ class TestRCircle(TestCase): - @classmethod def setUpClass(cls): """Set up a temporary region for testing.""" diff --git a/raster/r.coin/r.coin.html b/raster/r.coin/r.coin.html index bbaf260df83..4b7dc3af0eb 100644 --- a/raster/r.coin/r.coin.html +++ b/raster/r.coin/r.coin.html @@ -18,17 +18,16 @@

    DESCRIPTION

    units of measure in which the report results can be given. These units are: -

    -

    -
    c
    cells -
    p
    percent cover of region -
    x
    percent of <map name> category (column) -
    y
    percent of <map name> category (row) -
    a
    acres -
    h
    hectares -
    k
    square kilometers -
    m
    square miles -
    +
      +
    • c: cells
    • +
    • p: percent cover of region
    • +
    • x: percent of <map name> category (column)
    • +
    • y: percent of <map name> category (row)
    • +
    • a: acres
    • +
    • h: hectares
    • +
    • k: square kilometers
    • +
    • m: square miles
    • +

    Note that three of these options give results as percentage @@ -188,7 +187,7 @@

    SEE ALSO

    g.region, - + r.category, r.describe, r.reclass, diff --git a/raster/r.coin/tests/conftest.py b/raster/r.coin/tests/conftest.py new file mode 100644 index 00000000000..29c9eb0c3a2 --- /dev/null +++ b/raster/r.coin/tests/conftest.py @@ -0,0 +1,57 @@ +import os +import pytest +import grass.script as gs + + +@pytest.fixture +def setup_maps(tmp_path): + """Set up a GRASS session and create test raster maps.""" + + # Initialize GRASS project + project = tmp_path / "r_coin_project" + gs.create_project(project) + with gs.setup.init(project, env=os.environ.copy()) as session: + # Set the region + gs.run_command( + "g.region", + n=3, + s=0, + e=3, + w=0, + res=1, + env=session.env, + ) + + # Create the raster maps + # map1: + # 1 1 2 + # 1 2 3 + # 2 2 3 + gs.mapcalc( + "map1 = " + "if(row() == 1 && col() <= 2, 1, " + "if(row() == 1 && col() == 3, 2, " + "if(row() == 2 && col() == 1, 1, " + "if(row() == 2 && col() == 2, 2, " + "if(row() == 2 && col() == 3, 3, " + "if(row() == 3 && col() <= 2, 2, 3))))))", + overwrite=True, + env=session.env, + ) + + # map2: + # 1 2 2 + # 2 1 3 + # 3 3 3 + gs.mapcalc( + "map2 = " + "if(row() == 1 && col() == 1, 1, " + "if(row() == 1 && col() >= 2, 2, " + "if(row() == 2 && col() == 1, 2, " + "if(row() == 2 && col() == 2, 1, " + "if(row() == 2 && col() == 3, 3, " + "if(row() >= 3, 3, null()))))))", + overwrite=True, + env=session.env, + ) + yield session # Pass the session to tests diff --git a/raster/r.coin/tests/test_coin.py b/raster/r.coin/tests/test_coin.py new file mode 100644 index 00000000000..82137f6f9f1 --- /dev/null +++ b/raster/r.coin/tests/test_coin.py @@ -0,0 +1,63 @@ +import grass.script as gs + + +def validate_r_coin_output(actual_results, expected_results): + """Validate r.coin output against expected results.""" + assert set(actual_results) == set(expected_results), ( + f"Expected {expected_results}, but got {actual_results}" + ) + + +def test_r_coin(setup_maps): + """Test the r.coin module.""" + session = setup_maps + coin_output = gs.parse_command( + "r.coin", first="map1", second="map2", units="c", env=session.env + ) + + # Start parsing the output + actual_results = [] + is_data_section = False + + for line in coin_output: + # Detect the start of the data section + if "| cat# |" in line: + is_data_section = True + continue + + # Stop parsing after the data section ends + if is_data_section and line.startswith("+"): + break + + # Parse rows in the data section + if is_data_section: + columns = line.split("|") + if len(columns) > 2: + try: + # Extract categories and counts from the table + cat1 = int(columns[1].strip()[1:]) # Category from the row header + val1 = int(columns[2].strip()) # Value in column 1 + val2 = int(columns[3].strip()) # Value in column 2 + val3 = int(columns[4].strip()) # Value in column 3 + + actual_results.extend( + [(cat1, 1, val1), (cat1, 2, val2), (cat1, 3, val3)] + ) + except ValueError: + pass # Ignore lines that cannot be parsed as numbers + + # Expected results + expected_results = [ + (1, 1, 1), + (1, 2, 1), + (1, 3, 0), + (2, 1, 2), + (2, 2, 1), + (2, 3, 0), + (3, 1, 0), + (3, 2, 2), + (3, 3, 2), + ] + + # Validate results + validate_r_coin_output(actual_results, expected_results) diff --git a/raster/r.colors.out/Makefile b/raster/r.colors.out/Makefile index e2acf03ca41..b6717d12937 100644 --- a/raster/r.colors.out/Makefile +++ b/raster/r.colors.out/Makefile @@ -1,13 +1,13 @@ MODULE_TOPDIR = ../.. -LIBES2 = $(RASTERLIB) $(GISLIB) $(PARSONLIB) -LIBES3 = $(RASTER3DLIB) $(RASTERLIB) $(GISLIB) $(PARSONLIB) +LIBES2 = $(RASTERLIB) $(GISLIB) +LIBES3 = $(RASTER3DLIB) $(RASTERLIB) $(GISLIB) DEPENDENCIES = $(RASTER3DDEP) $(GISDEP) $(RASTERDEP) PROGRAMS = r.colors.out r3.colors.out -r_colors_out_OBJS = raster_main.o prt_json.o -r3_colors_out_OBJS = raster3d_main.o prt_json.o +r_colors_out_OBJS = raster_main.o +r3_colors_out_OBJS = raster3d_main.o include $(MODULE_TOPDIR)/include/Make/Multi.make diff --git a/raster/r.colors.out/local_proto.h b/raster/r.colors.out/local_proto.h deleted file mode 100644 index 5ed09a06e69..00000000000 --- a/raster/r.colors.out/local_proto.h +++ /dev/null @@ -1,7 +0,0 @@ -#include -#include - -enum ColorFormat { RGB, HEX, HSV, TRIPLET }; - -void print_json_colors(struct Colors *colors, DCELL min, DCELL max, FILE *fp, - int perc, enum ColorFormat clr_frmt); diff --git a/raster/r.colors.out/raster3d_main.c b/raster/r.colors.out/raster3d_main.c index 904db24a91f..e988663d665 100644 --- a/raster/r.colors.out/raster3d_main.c +++ b/raster/r.colors.out/raster3d_main.c @@ -21,9 +21,6 @@ #include #include #include -#include - -#include "local_proto.h" /* Run in raster3d mode */ int main(int argc, char **argv) @@ -41,7 +38,7 @@ int main(int argc, char **argv) struct Colors colors; struct FPRange range; - enum ColorFormat clr_frmt; + ColorFormat clr_frmt; G_gisinit(argv[0]); @@ -102,8 +99,8 @@ int main(int argc, char **argv) else { clr_frmt = HEX; } - print_json_colors(&colors, range.min, range.max, fp, - flag.p->answer ? 1 : 0, clr_frmt); + Rast_print_json_colors(&colors, range.min, range.max, fp, + flag.p->answer ? 1 : 0, clr_frmt); } else { Rast_print_colors(&colors, range.min, range.max, fp, diff --git a/raster/r.colors.out/raster_main.c b/raster/r.colors.out/raster_main.c index 6b4a8ed36ee..60807c06383 100644 --- a/raster/r.colors.out/raster_main.c +++ b/raster/r.colors.out/raster_main.c @@ -20,9 +20,6 @@ #include #include #include -#include - -#include "local_proto.h" /* Run in raster mode */ int main(int argc, char **argv) @@ -40,7 +37,7 @@ int main(int argc, char **argv) struct Colors colors; struct FPRange range; - enum ColorFormat clr_frmt; + ColorFormat clr_frmt; G_gisinit(argv[0]); @@ -101,8 +98,8 @@ int main(int argc, char **argv) else { clr_frmt = HEX; } - print_json_colors(&colors, range.min, range.max, fp, - flag.p->answer ? 1 : 0, clr_frmt); + Rast_print_json_colors(&colors, range.min, range.max, fp, + flag.p->answer ? 1 : 0, clr_frmt); } else { Rast_print_colors(&colors, range.min, range.max, fp, diff --git a/raster/r.colors.out/tests/r3_colors_out_test.py b/raster/r.colors.out/tests/r3_colors_out_test.py index 670c1ef13a6..7aa9d8bc217 100644 --- a/raster/r.colors.out/tests/r3_colors_out_test.py +++ b/raster/r.colors.out/tests/r3_colors_out_test.py @@ -17,9 +17,9 @@ def validate_plain_text_output(data): "nv 255:255:255", "default 255:255:255", } - assert ( - expected == data.keys() - ), f"test failed: expected {expected} but got {data.keys()}" + assert expected == data.keys(), ( + f"test failed: expected {expected} but got {data.keys()}" + ) def test_r3_colors_out_plain_output(raster3_color_dataset): @@ -52,17 +52,17 @@ def test_r3_colors_out_with_p_flag(raster3_color_dataset): "nv 255:255:255", "default 255:255:255", } - assert ( - expected == data.keys() - ), f"test failed: expected {expected} but got {data.keys()}" + assert expected == data.keys(), ( + f"test failed: expected {expected} but got {data.keys()}" + ) def validate_common_json_structure(data): """Validate the common structure and content of the JSON output.""" assert isinstance(data, list), "Output data should be a list of entries." - assert ( - len(data) == 8 - ), "The length of the output JSON does not match the expected value of 8." + assert len(data) == 8, ( + "The length of the output JSON does not match the expected value of 8." + ) def test_r3_colors_out_json_with_default_option(raster3_color_dataset): @@ -71,14 +71,14 @@ def test_r3_colors_out_json_with_default_option(raster3_color_dataset): data = gs.parse_command("r3.colors.out", map="b", format="json", env=session.env) validate_common_json_structure(data) expected = [ - {"value": 1, "hex": "#00BFBF"}, - {"value": 1.8, "hex": "#00FF00"}, - {"value": 2.6, "hex": "#FFFF00"}, - {"value": 3.4, "hex": "#FF7F00"}, - {"value": 4.2, "hex": "#BF7F3F"}, - {"value": 5, "hex": "#C8C8C8"}, - {"value": "nv", "hex": "#FFFFFF"}, - {"value": "default", "hex": "#FFFFFF"}, + {"value": 1, "color": "#00BFBF"}, + {"value": 1.8, "color": "#00FF00"}, + {"value": 2.6, "color": "#FFFF00"}, + {"value": 3.4, "color": "#FF7F00"}, + {"value": 4.2, "color": "#BF7F3F"}, + {"value": 5, "color": "#C8C8C8"}, + {"value": "nv", "color": "#FFFFFF"}, + {"value": "default", "color": "#FFFFFF"}, ] assert expected == data, f"test failed: expected {expected} but got {data}" @@ -91,14 +91,14 @@ def test_r3_colors_out_json_with_triplet_option(raster3_color_dataset): ) validate_common_json_structure(data) expected = [ - {"value": 1, "triplet": "0:191:191"}, - {"value": 1.8, "triplet": "0:255:0"}, - {"value": 2.6, "triplet": "255:255:0"}, - {"value": 3.4, "triplet": "255:127:0"}, - {"value": 4.2, "triplet": "191:127:63"}, - {"value": 5, "triplet": "200:200:200"}, - {"value": "nv", "triplet": "255:255:255"}, - {"value": "default", "triplet": "255:255:255"}, + {"value": 1, "color": "0:191:191"}, + {"value": 1.8, "color": "0:255:0"}, + {"value": 2.6, "color": "255:255:0"}, + {"value": 3.4, "color": "255:127:0"}, + {"value": 4.2, "color": "191:127:63"}, + {"value": 5, "color": "200:200:200"}, + {"value": "nv", "color": "255:255:255"}, + {"value": "default", "color": "255:255:255"}, ] assert expected == data, f"test failed: expected {expected} but got {data}" @@ -115,14 +115,14 @@ def test_r3_colors_out_json_with_rgb_option(raster3_color_dataset): ) validate_common_json_structure(data) expected = [ - {"value": 1, "rgb": "rgb(0, 191, 191)"}, - {"value": 1.8, "rgb": "rgb(0, 255, 0)"}, - {"value": 2.6, "rgb": "rgb(255, 255, 0)"}, - {"value": 3.4, "rgb": "rgb(255, 127, 0)"}, - {"value": 4.2, "rgb": "rgb(191, 127, 63)"}, - {"value": 5, "rgb": "rgb(200, 200, 200)"}, - {"value": "nv", "rgb": "rgb(255, 255, 255)"}, - {"value": "default", "rgb": "rgb(255, 255, 255)"}, + {"value": 1, "color": "rgb(0, 191, 191)"}, + {"value": 1.8, "color": "rgb(0, 255, 0)"}, + {"value": 2.6, "color": "rgb(255, 255, 0)"}, + {"value": 3.4, "color": "rgb(255, 127, 0)"}, + {"value": 4.2, "color": "rgb(191, 127, 63)"}, + {"value": 5, "color": "rgb(200, 200, 200)"}, + {"value": "nv", "color": "rgb(255, 255, 255)"}, + {"value": "default", "color": "rgb(255, 255, 255)"}, ] assert expected == data, f"test failed: expected {expected} but got {data}" @@ -139,14 +139,14 @@ def test_r3_colors_out_json_with_hex_option(raster3_color_dataset): ) validate_common_json_structure(data) expected = [ - {"value": 1, "hex": "#00BFBF"}, - {"value": 1.8, "hex": "#00FF00"}, - {"value": 2.6, "hex": "#FFFF00"}, - {"value": 3.4, "hex": "#FF7F00"}, - {"value": 4.2, "hex": "#BF7F3F"}, - {"value": 5, "hex": "#C8C8C8"}, - {"value": "nv", "hex": "#FFFFFF"}, - {"value": "default", "hex": "#FFFFFF"}, + {"value": 1, "color": "#00BFBF"}, + {"value": 1.8, "color": "#00FF00"}, + {"value": 2.6, "color": "#FFFF00"}, + {"value": 3.4, "color": "#FF7F00"}, + {"value": 4.2, "color": "#BF7F3F"}, + {"value": 5, "color": "#C8C8C8"}, + {"value": "nv", "color": "#FFFFFF"}, + {"value": "default", "color": "#FFFFFF"}, ] assert expected == data, f"test failed: expected {expected} but got {data}" @@ -163,13 +163,13 @@ def test_r3_colors_out_json_with_hsv_option(raster3_color_dataset): ) validate_common_json_structure(data) expected = [ - {"value": 1, "hsv": "hsv(180, 100, 74)"}, - {"value": 1.8, "hsv": "hsv(120, 100, 100)"}, - {"value": 2.6, "hsv": "hsv(60, 100, 100)"}, - {"value": 3.4, "hsv": "hsv(29, 100, 100)"}, - {"value": 4.2, "hsv": "hsv(30, 67, 74)"}, - {"value": 5, "hsv": "hsv(0, 0, 78)"}, - {"value": "nv", "hsv": "hsv(0, 0, 100)"}, - {"value": "default", "hsv": "hsv(0, 0, 100)"}, + {"value": 1, "color": "hsv(180, 100, 74)"}, + {"value": 1.8, "color": "hsv(120, 100, 100)"}, + {"value": 2.6, "color": "hsv(60, 100, 100)"}, + {"value": 3.4, "color": "hsv(29, 100, 100)"}, + {"value": 4.2, "color": "hsv(30, 67, 74)"}, + {"value": 5, "color": "hsv(0, 0, 78)"}, + {"value": "nv", "color": "hsv(0, 0, 100)"}, + {"value": "default", "color": "hsv(0, 0, 100)"}, ] assert expected == data, f"test failed: expected {expected} but got {data}" diff --git a/raster/r.colors.out/tests/r_colors_out_test.py b/raster/r.colors.out/tests/r_colors_out_test.py index d1abec82592..10656943954 100644 --- a/raster/r.colors.out/tests/r_colors_out_test.py +++ b/raster/r.colors.out/tests/r_colors_out_test.py @@ -17,9 +17,9 @@ def validate_plain_text_output(data): "nv 255:255:255", "default 255:255:255", } - assert ( - expected == data.keys() - ), f"test failed: expected {expected} but got {data.keys()}" + assert expected == data.keys(), ( + f"test failed: expected {expected} but got {data.keys()}" + ) def test_r_colors_out_plain_output(raster_color_dataset): @@ -52,17 +52,17 @@ def test_r_colors_out_with_p_flag(raster_color_dataset): "nv 255:255:255", "default 255:255:255", } - assert ( - expected == data.keys() - ), f"test failed: expected {expected} but got {data.keys()}" + assert expected == data.keys(), ( + f"test failed: expected {expected} but got {data.keys()}" + ) def validate_common_json_structure(data): """Validate the common structure and content of the JSON output.""" assert isinstance(data, list), "Output data should be a list of entries." - assert ( - len(data) == 8 - ), "The length of the output JSON does not match the expected value of 8." + assert len(data) == 8, ( + "The length of the output JSON does not match the expected value of 8." + ) def test_r_colors_out_json_with_default_option(raster_color_dataset): @@ -71,14 +71,14 @@ def test_r_colors_out_json_with_default_option(raster_color_dataset): data = gs.parse_command("r.colors.out", map="a", format="json", env=session.env) validate_common_json_structure(data) expected = [ - {"value": 1, "hex": "#00BFBF"}, - {"value": 1.4, "hex": "#00FF00"}, - {"value": 1.8, "hex": "#FFFF00"}, - {"value": 2.2, "hex": "#FF7F00"}, - {"value": 2.6, "hex": "#BF7F3F"}, - {"value": 3, "hex": "#C8C8C8"}, - {"value": "nv", "hex": "#FFFFFF"}, - {"value": "default", "hex": "#FFFFFF"}, + {"value": 1, "color": "#00BFBF"}, + {"value": 1.4, "color": "#00FF00"}, + {"value": 1.8, "color": "#FFFF00"}, + {"value": 2.2, "color": "#FF7F00"}, + {"value": 2.6, "color": "#BF7F3F"}, + {"value": 3, "color": "#C8C8C8"}, + {"value": "nv", "color": "#FFFFFF"}, + {"value": "default", "color": "#FFFFFF"}, ] assert expected == data, f"test failed: expected {expected} but got {data}" @@ -91,14 +91,14 @@ def test_r_colors_out_json_with_triplet_option(raster_color_dataset): ) validate_common_json_structure(data) expected = [ - {"value": 1, "triplet": "0:191:191"}, - {"value": 1.4, "triplet": "0:255:0"}, - {"value": 1.8, "triplet": "255:255:0"}, - {"value": 2.2, "triplet": "255:127:0"}, - {"value": 2.6, "triplet": "191:127:63"}, - {"value": 3, "triplet": "200:200:200"}, - {"value": "nv", "triplet": "255:255:255"}, - {"value": "default", "triplet": "255:255:255"}, + {"value": 1, "color": "0:191:191"}, + {"value": 1.4, "color": "0:255:0"}, + {"value": 1.8, "color": "255:255:0"}, + {"value": 2.2, "color": "255:127:0"}, + {"value": 2.6, "color": "191:127:63"}, + {"value": 3, "color": "200:200:200"}, + {"value": "nv", "color": "255:255:255"}, + {"value": "default", "color": "255:255:255"}, ] assert expected == data, f"test failed: expected {expected} but got {data}" @@ -115,14 +115,14 @@ def test_r_colors_out_json_with_rgb_option(raster_color_dataset): ) validate_common_json_structure(data) expected = [ - {"value": 1, "rgb": "rgb(0, 191, 191)"}, - {"value": 1.4, "rgb": "rgb(0, 255, 0)"}, - {"value": 1.8, "rgb": "rgb(255, 255, 0)"}, - {"value": 2.2, "rgb": "rgb(255, 127, 0)"}, - {"value": 2.6, "rgb": "rgb(191, 127, 63)"}, - {"value": 3, "rgb": "rgb(200, 200, 200)"}, - {"value": "nv", "rgb": "rgb(255, 255, 255)"}, - {"value": "default", "rgb": "rgb(255, 255, 255)"}, + {"value": 1, "color": "rgb(0, 191, 191)"}, + {"value": 1.4, "color": "rgb(0, 255, 0)"}, + {"value": 1.8, "color": "rgb(255, 255, 0)"}, + {"value": 2.2, "color": "rgb(255, 127, 0)"}, + {"value": 2.6, "color": "rgb(191, 127, 63)"}, + {"value": 3, "color": "rgb(200, 200, 200)"}, + {"value": "nv", "color": "rgb(255, 255, 255)"}, + {"value": "default", "color": "rgb(255, 255, 255)"}, ] assert expected == data, f"test failed: expected {expected} but got {data}" @@ -139,14 +139,14 @@ def test_r_colors_out_json_with_hex_option(raster_color_dataset): ) validate_common_json_structure(data) expected = [ - {"value": 1, "hex": "#00BFBF"}, - {"value": 1.4, "hex": "#00FF00"}, - {"value": 1.8, "hex": "#FFFF00"}, - {"value": 2.2, "hex": "#FF7F00"}, - {"value": 2.6, "hex": "#BF7F3F"}, - {"value": 3, "hex": "#C8C8C8"}, - {"value": "nv", "hex": "#FFFFFF"}, - {"value": "default", "hex": "#FFFFFF"}, + {"value": 1, "color": "#00BFBF"}, + {"value": 1.4, "color": "#00FF00"}, + {"value": 1.8, "color": "#FFFF00"}, + {"value": 2.2, "color": "#FF7F00"}, + {"value": 2.6, "color": "#BF7F3F"}, + {"value": 3, "color": "#C8C8C8"}, + {"value": "nv", "color": "#FFFFFF"}, + {"value": "default", "color": "#FFFFFF"}, ] assert expected == data, f"test failed: expected {expected} but got {data}" @@ -163,13 +163,13 @@ def test_r_colors_out_json_with_hsv_option(raster_color_dataset): ) validate_common_json_structure(data) expected = [ - {"value": 1, "hsv": "hsv(180, 100, 74)"}, - {"value": 1.4, "hsv": "hsv(120, 100, 100)"}, - {"value": 1.8, "hsv": "hsv(60, 100, 100)"}, - {"value": 2.2, "hsv": "hsv(29, 100, 100)"}, - {"value": 2.6, "hsv": "hsv(30, 67, 74)"}, - {"value": 3, "hsv": "hsv(0, 0, 78)"}, - {"value": "nv", "hsv": "hsv(0, 0, 100)"}, - {"value": "default", "hsv": "hsv(0, 0, 100)"}, + {"value": 1, "color": "hsv(180, 100, 74)"}, + {"value": 1.4, "color": "hsv(120, 100, 100)"}, + {"value": 1.8, "color": "hsv(60, 100, 100)"}, + {"value": 2.2, "color": "hsv(29, 100, 100)"}, + {"value": 2.6, "color": "hsv(30, 67, 74)"}, + {"value": 3, "color": "hsv(0, 0, 78)"}, + {"value": "nv", "color": "hsv(0, 0, 100)"}, + {"value": "default", "color": "hsv(0, 0, 100)"}, ] assert expected == data, f"test failed: expected {expected} but got {data}" diff --git a/raster/r.colors/r.colors.html b/raster/r.colors/r.colors.html index 8fdf83d1313..3762adda465 100644 --- a/raster/r.colors/r.colors.html +++ b/raster/r.colors/r.colors.html @@ -71,7 +71,7 @@

    DESCRIPTION

    certain ranges. One can get a rough idea of the applicability of a colour table by reading the -corresponding rules file ($GISBASE/etc/colors/<name>). +corresponding rules file ($GISBASE/etc/colors/<name>). For example the slope rule is defined as:
    @@ -231,14 +231,14 @@ 

    DESCRIPTION

    NOTES

    -All color tables are stored in $GISBASE/etc/colors/. Further +All color tables are stored in $GISBASE/etc/colors/. Further user-defined color tables can also be stored in this directory for access from the color parameter or in a user defined directory. See also r.colors.out for printing color tables easily to the terminal.

    The color table assigned to a raster map is stored in -$GISDBASE/location/mapset/colr/. +$GISDBASE/location/mapset/colr/.

    EXAMPLES

    @@ -309,7 +309,7 @@

    SEE ALSO

    page Color tables (from GRASS User Wiki) -

    ColorBrewer is an online tool designed to +

    ColorBrewer is an online tool designed to help people select good color schemes for maps and other graphics.

    AUTHORS

    diff --git a/raster/r.colors/r3.colors.html b/raster/r.colors/r3.colors.html index c4e65a62bee..16b35b76b1d 100644 --- a/raster/r.colors/r3.colors.html +++ b/raster/r.colors/r3.colors.html @@ -24,7 +24,7 @@

    SEE ALSO

    page Color tables (from GRASS User Wiki) -

    ColorBrewer is an online tool designed to +

    ColorBrewer is an online tool designed to help people select good color schemes for maps and other graphics.

    AUTHORS

    diff --git a/raster/r.compress/r.compress.html b/raster/r.compress/r.compress.html index 5a95a970bbf..665015e2cc4 100644 --- a/raster/r.compress/r.compress.html +++ b/raster/r.compress/r.compress.html @@ -10,13 +10,13 @@

    DESCRIPTION

    method if available, otherwise ZLIB compression is used (see below). Related no data files (i.e.: NULL files), if present, are compressed by default unless a specific environment variable is set to explicitly -disable NULL file compression (GRASS_COMPRESS_NULLS, see +disable NULL file compression (GRASS_COMPRESS_NULLS, see below).

    During compression or re-compression, r.compress compresses raster maps using the method specified by means of the environment -variable GRASS_COMPRESSOR. The default compression method is +variable GRASS_COMPRESSOR. The default compression method is ZSTD if available, otherwise ZLIB's "deflate" algorithm (LZ77-based). Raster maps that contain very little information (such as boundary, geology, soils and land use maps) can be greatly reduced in size. Some @@ -37,10 +37,10 @@

    DESCRIPTION

    Raster maps that are already compressed might be compressed again, -either by setting a different method with GRASS_COMPRESSOR +either by setting a different method with GRASS_COMPRESSOR (supported methods: RLE, ZLIB, LZ4, BZIP2, ZSTD) or, for the case of ZLIB compression, by changing the compression level with the -environment variable GRASS_ZLIB_LEVEL. +environment variable GRASS_ZLIB_LEVEL.

    Compressed raster maps may be decompressed using r.compress @@ -51,7 +51,7 @@

    DESCRIPTION

    Information about the compression method and data type of the input raster map(s) can be printed in shell style with the -g flag. In -this case, the module prints to stdout one line per input map +this case, the module prints to stdout one line per input map with the fields "input map name", "data type", "name of data compression method", "NULL file compression" separated by the pipe character. NULL file @@ -73,27 +73,27 @@

    TERMINOLOGY

    OVERVIEW OF AVAILABLE COMPRESSION ALGORITHMS

    The following compression methods are available (set by -export GRASS_COMPRESSOR=method): +export GRASS_COMPRESSOR=method):
      -
    • NONE (uncompressed)
    • -
    • RLE (generic Run-Length Encoding of single bytes; deprecated)
    • -
    • ZLIB (DEFLATE, good speed and compression) +
    • NONE (uncompressed)
    • +
    • RLE (generic Run-Length Encoding of single bytes; deprecated)
    • +
    • ZLIB (DEFLATE, good speed and compression)
        -
      • with zlib compression levels (export GRASS_ZLIB_LEVEL=X): -1..9 +
      • with zlib compression levels (export GRASS_ZLIB_LEVEL=X): -1..9 (-1 is default which corresponds to ZLIB level 6)
      • -
      • note: export GRASS_ZLIB_LEVEL=0 is equal to copying the data +
      • note: export GRASS_ZLIB_LEVEL=0 is equal to copying the data as-is from source to destination
    • -
    • LZ4 (fastest, low compression)
    • -
    • BZIP2 (slowest, high compression)
    • -
    • ZSTD (compared to ZLIB, faster and higher compression, +
    • LZ4 (fastest, low compression)
    • +
    • BZIP2 (slowest, high compression)
    • +
    • ZSTD (compared to ZLIB, faster and higher compression, much faster decompression - default compression)
    Important: the NULL file compression can be turned off with -export GRASS_COMPRESS_NULLS=0. Raster maps with NULL file +export GRASS_COMPRESS_NULLS=0. Raster maps with NULL file compression can only be opened with GRASS GIS 7.2.0 or later. NULL file compression for a particular raster map can be managed with r.null -z. The NULL file compression is using the LZ4 method as being the best compromise @@ -104,11 +104,11 @@

    COMPRESSION ALGORITHM DETAILS

    All GRASS GIS raster map types are by default ZSTD compressed if available, otherwise ZLIB compressed. Through the environment variable -GRASS_COMPRESSOR the compression method can be set to RLE, +GRASS_COMPRESSOR the compression method can be set to RLE, ZLIB, LZ4, BZIP2, or ZSTD.

    Integer (CELL type) raster maps can be compressed with RLE if -the environment variable GRASS_COMPRESSOR exists and is set to +the environment variable GRASS_COMPRESSOR exists and is set to RLE. However, this is not recommended.

    Floating point (FCELL, DCELL) raster maps never use RLE compression; @@ -133,7 +133,7 @@

    COMPRESSION ALGORITHM DETAILS

    compression level which is the best compromise between speed and compression ratio, also when compared to other available compression methods. Valid levels are in the range [1, 9] and can be set with the -environment variable GRASS_ZLIB_LEVEL. +environment variable GRASS_ZLIB_LEVEL.
    LZ4
    LZ4 is a very fast compression method, about as fast as no compression. Decompression is also very fast. The compression ratio is @@ -160,7 +160,7 @@

    NOTES

    Compression method number scheme

    The used compression method is encoded with numbers. In the internal -cellhd file, the value for "compressed" is 1 for RLE, 2 +cellhd file, the value for "compressed" is 1 for RLE, 2 for ZLIB, 3 for LZ4, 4 for BZIP2, and 5 for ZSTD.

    Obviously, decompression is controlled by the raster map's compression, @@ -185,7 +185,7 @@

    Formats

    ZLIB compression levels

    -If the environment variable GRASS_ZLIB_LEVEL exists and +If the environment variable GRASS_ZLIB_LEVEL exists and its value can be parsed as an integer, it determines the compression level used when newly generated raster maps are compressed using ZLIB compression. This applies to all raster map types (CELL, FCELL, DCELL). @@ -273,9 +273,9 @@

    SEE ALSO

    Compression algorithms: bzip2, -LZ4, -zlib, -zstd +LZ4, +zlib, +zstd

    AUTHORS

    diff --git a/raster/r.cost/r.cost.html b/raster/r.cost/r.cost.html index e5118d98aea..db5343ec650 100644 --- a/raster/r.cost/r.cost.html +++ b/raster/r.cost/r.cost.html @@ -53,10 +53,10 @@

    OPTIONS

    use the --v verbose flag to track progress.

    The Knight's move (-k flag) may be used to improve the accuracy of -the output. In the diagram below, the center location (O) represents a +the output. In the diagram below, the center location (O) represents a grid cell from which cumulative distances are calculated. Those -neighbors marked with an X are always considered for cumulative cost -updates. With the -k option, the neighbors marked with a K are +neighbors marked with an X are always considered for cumulative cost +updates. With the -k option, the neighbors marked with a K are also considered.

    @@ -135,7 +135,7 @@ 

    NULL CELLS

    the algorithm, and thus retained in the output map.

    If one wants r.cost to transparently cross any region of null cells, -the null_cost=0.0 option should be used. Then null cells just +the null_cost=0.0 option should be used. Then null cells just propagate the adjacent costs. These cells can be retained as null cells in the output map by using the -n flag. diff --git a/raster/r.covar/r.covar.html b/raster/r.covar/r.covar.html index 87f1345d7e7..a510bb39c7a 100644 --- a/raster/r.covar/r.covar.html +++ b/raster/r.covar/r.covar.html @@ -17,9 +17,9 @@

    NOTES

    N real eigen values and N eigen vectors (each composed of N real numbers).

    -The module m.eigensystem +The module m.eigensystem in GRASS GIS Addons -can be compiled and used to generate the eigen values and vectors. +can be installed and used to generate the eigenvalues and vectors.

    EXAMPLE

    diff --git a/raster/r.describe/Makefile b/raster/r.describe/Makefile index ddd265e6d58..52ba3114f4d 100644 --- a/raster/r.describe/Makefile +++ b/raster/r.describe/Makefile @@ -2,7 +2,7 @@ MODULE_TOPDIR = ../.. PGM = r.describe -LIBES = $(RASTERLIB) $(GISLIB) +LIBES = $(RASTERLIB) $(GISLIB) $(PARSONLIB) DEPENDENCIES = $(RASTERDEP) $(GISDEP) include $(MODULE_TOPDIR)/include/Make/Module.make diff --git a/raster/r.describe/describe.c b/raster/r.describe/describe.c index 69b65aeec6e..11540d93267 100644 --- a/raster/r.describe/describe.c +++ b/raster/r.describe/describe.c @@ -18,10 +18,12 @@ #include #include #include + #include "local_proto.h" int describe(const char *name, int compact, char *no_data_str, int range, - int windowed, int nsteps, int as_int, int skip_nulls) + int windowed, int nsteps, int as_int, int skip_nulls, + enum OutputFormat format) { int fd; struct Cell_stats statf; @@ -127,20 +129,20 @@ int describe(const char *name, int compact, char *no_data_str, int range, if (range) { if (compact) compact_range_list(negmin, negmax, zero, posmin, posmax, null, - no_data_str, skip_nulls); + no_data_str, skip_nulls, format); else range_list(negmin, negmax, zero, posmin, posmax, null, no_data_str, - skip_nulls); + skip_nulls, format); } else { Rast_rewind_cell_stats(&statf); if (compact) compact_list(&statf, dmin, dmax, no_data_str, skip_nulls, map_type, - nsteps); + nsteps, format); else long_list(&statf, dmin, dmax, no_data_str, skip_nulls, map_type, - nsteps); + nsteps, format); Rast_free_cell_stats(&statf); } diff --git a/raster/r.describe/dumplist.c b/raster/r.describe/dumplist.c index 7191679463b..890e875d32e 100644 --- a/raster/r.describe/dumplist.c +++ b/raster/r.describe/dumplist.c @@ -18,43 +18,176 @@ #include #include #include +#include -static int show(CELL, CELL, int *, DCELL, DCELL, RASTER_MAP_TYPE, int); +#include "local_proto.h" + +static void initialize_json_object(JSON_Value **, JSON_Object **); +static void initialize_json_array(JSON_Value **, JSON_Array **); +static void append_category_ranges(JSON_Array *range_array, long min, long max); +static void output_pretty_json(JSON_Value *); +static int show(CELL, CELL, int *, DCELL, DCELL, RASTER_MAP_TYPE, int, + enum OutputFormat, JSON_Array *); + +void initialize_json_object(JSON_Value **root_value, JSON_Object **root_object) +{ + *root_value = json_value_init_object(); + if (*root_value == NULL) { + G_fatal_error(_("Failed to initialize JSON object. Out of memory?")); + } + *root_object = json_object(*root_value); +} + +void initialize_json_array(JSON_Value **root_value, JSON_Array **root_array) +{ + *root_value = json_value_init_array(); + if (*root_value == NULL) { + G_fatal_error(_("Failed to initialize JSON array. Out of memory?")); + } + *root_array = json_array(*root_value); +} + +void append_category_ranges(JSON_Array *range_array, long min, long max) +{ + JSON_Object *cat_object; + JSON_Value *cat_value; + initialize_json_object(&cat_value, &cat_object); + + json_object_set_number(cat_object, "min", min); + json_object_set_number(cat_object, "max", max); + + json_array_append_value(range_array, cat_value); +} + +void output_pretty_json(JSON_Value *root_value) +{ + char *serialized_string = json_serialize_to_string_pretty(root_value); + if (!serialized_string) { + json_value_free(root_value); + G_fatal_error(_("Failed to initialize pretty JSON string.")); + } + puts(serialized_string); + + json_free_serialized_string(serialized_string); + json_value_free(root_value); +} int long_list(struct Cell_stats *statf, DCELL dmin, DCELL dmax, char *no_data_str, int skip_nulls, RASTER_MAP_TYPE map_type, - int nsteps) + int nsteps, enum OutputFormat format) { CELL cat; long count; /* not used, but required by cell stats call */ + JSON_Value *root_value, *range_value; + JSON_Object *root_object; + JSON_Array *range_array; + + if (format == JSON) { + initialize_json_object(&root_value, &root_object); + initialize_json_array(&range_value, &range_array); + } Rast_get_stats_for_null_value(&count, statf); - if (count != 0 && !skip_nulls) - fprintf(stdout, "%s\n", no_data_str); + if (!skip_nulls) { + if (count != 0) { + switch (format) { + case PLAIN: + fprintf(stdout, "%s\n", no_data_str); + break; + case JSON: + json_object_set_boolean(root_object, "has_nulls", true); + break; + } + } + else { + if (format == JSON) { + json_object_set_boolean(root_object, "has_nulls", false); + } + } + } while (Rast_next_cell_stat(&cat, &count, statf)) { - if (map_type != CELL_TYPE) - fprintf(stdout, "%f-%f\n", - dmin + (double)(cat - 1) * (dmax - dmin) / nsteps, - dmin + (double)cat * (dmax - dmin) / nsteps); - else - fprintf(stdout, "%ld\n", (long)cat); + switch (format) { + case PLAIN: + if (map_type != CELL_TYPE) + fprintf(stdout, "%f-%f\n", + dmin + (double)(cat - 1) * (dmax - dmin) / nsteps, + dmin + (double)cat * (dmax - dmin) / nsteps); + else + fprintf(stdout, "%ld\n", (long)cat); + break; + case JSON: + if (map_type != CELL_TYPE) { + JSON_Object *cat_object; + JSON_Value *cat_value; + initialize_json_object(&cat_value, &cat_object); + + json_object_set_number(cat_object, "min", + dmin + (double)(cat - 1) * + (dmax - dmin) / nsteps); + json_object_set_number(cat_object, "max", + dmin + (double)cat * (dmax - dmin) / + nsteps); + + json_array_append_value(range_array, cat_value); + } + else { + json_array_append_number(range_array, (long)cat); + } + + break; + } } + + if (format == JSON) { + if (map_type != CELL_TYPE) { + json_object_set_value(root_object, "ranges", range_value); + } + else { + json_object_set_value(root_object, "values", range_value); + } + + output_pretty_json(root_value); + } + return (0); } int compact_list(struct Cell_stats *statf, DCELL dmin, DCELL dmax, char *no_data_str, int skip_nulls, RASTER_MAP_TYPE map_type, - int nsteps) + int nsteps, enum OutputFormat format) { CELL cat1, cat2, temp; int len; long count; /* not used, but required by cell stats call */ + JSON_Value *root_value, *range_value; + JSON_Object *root_object; + JSON_Array *range_array; + + if (format == JSON) { + initialize_json_object(&root_value, &root_object); + initialize_json_array(&range_value, &range_array); + } len = 0; Rast_get_stats_for_null_value(&count, statf); - if (count != 0 && !skip_nulls) - fprintf(stdout, "%s ", no_data_str); + if (!skip_nulls) { + if (count != 0) { + switch (format) { + case PLAIN: + fprintf(stdout, "%s ", no_data_str); + break; + case JSON: + json_object_set_boolean(root_object, "has_nulls", true); + break; + } + } + else { + if (format == JSON) { + json_object_set_boolean(root_object, "has_nulls", false); + } + } + } if (!Rast_next_cell_stat(&cat1, &count, statf)) /* map doesn't contain any non-null data */ @@ -63,94 +196,244 @@ int compact_list(struct Cell_stats *statf, DCELL dmin, DCELL dmax, cat2 = cat1; while (Rast_next_cell_stat(&temp, &count, statf)) { if (temp != cat2 + (CELL)1) { - show(cat1, cat2, &len, dmin, dmax, map_type, nsteps); + show(cat1, cat2, &len, dmin, dmax, map_type, nsteps, format, + range_array); cat1 = temp; } cat2 = temp; } - show(cat1, cat2, &len, dmin, dmax, map_type, nsteps); - fprintf(stdout, "\n"); + show(cat1, cat2, &len, dmin, dmax, map_type, nsteps, format, range_array); + + switch (format) { + case PLAIN: + fprintf(stdout, "\n"); + break; + case JSON: + json_object_set_value(root_object, "ranges", range_value); + output_pretty_json(root_value); + break; + } + return (0); } static int show(CELL low, CELL high, int *len, DCELL dmin, DCELL dmax, - RASTER_MAP_TYPE map_type, int nsteps) + RASTER_MAP_TYPE map_type, int nsteps, enum OutputFormat format, + JSON_Array *root_array) { char text[100]; char xlen; + JSON_Object *cat_object; + JSON_Value *cat_value; if (low + 1 == high) { - show(low, low, len, dmin, dmax, map_type, nsteps); - show(high, high, len, dmin, dmax, map_type, nsteps); + show(low, low, len, dmin, dmax, map_type, nsteps, format, root_array); + show(high, high, len, dmin, dmax, map_type, nsteps, format, root_array); return 0; } + if (format == JSON) + initialize_json_object(&cat_value, &cat_object); + if (map_type != CELL_TYPE) { - sprintf(text, "%f%s%f ", dmin + (low - 1) * (dmax - dmin) / nsteps, - dmin < 0 ? " thru " : "-", - dmin + high * (dmax - dmin) / nsteps); + switch (format) { + case PLAIN: + sprintf(text, "%f%s%f ", dmin + (low - 1) * (dmax - dmin) / nsteps, + dmin < 0 ? " thru " : "-", + dmin + high * (dmax - dmin) / nsteps); + break; + case JSON: + json_object_set_number(cat_object, "min", + dmin + (low - 1) * (dmax - dmin) / nsteps); + json_object_set_number(cat_object, "max", + dmin + high * (dmax - dmin) / nsteps); + break; + } } else { - if (low == high) - sprintf(text, "%ld ", (long)low); - else - sprintf(text, "%ld%s%ld ", (long)low, low < 0 ? " thru " : "-", - (long)high); + switch (format) { + case PLAIN: + if (low == high) + sprintf(text, "%ld ", (long)low); + else + sprintf(text, "%ld%s%ld ", (long)low, low < 0 ? " thru " : "-", + (long)high); + break; + case JSON: + json_object_set_number(cat_object, "min", (long)low); + json_object_set_number(cat_object, "max", (long)high); + break; + } } xlen = strlen(text); if (xlen + *len > 78) { - fprintf(stdout, "\n"); + if (format == PLAIN) + fprintf(stdout, "\n"); *len = 0; } - fprintf(stdout, "%s", text); + + switch (format) { + case PLAIN: + fprintf(stdout, "%s", text); + break; + case JSON: + json_array_append_value(root_array, cat_value); + break; + } + *len += xlen; return (0); } int compact_range_list(CELL negmin, CELL negmax, CELL zero, CELL posmin, CELL posmax, CELL null, char *no_data_str, - int skip_nulls) + int skip_nulls, enum OutputFormat format) { + JSON_Value *root_value, *range_value; + JSON_Object *root_object; + JSON_Array *range_array; + + if (format == JSON) { + initialize_json_object(&root_value, &root_object); + initialize_json_array(&range_value, &range_array); + } + if (negmin) { - fprintf(stdout, "%ld", (long)negmin); - if (negmin != negmax) - fprintf(stdout, " thru %ld", (long)negmax); - fprintf(stdout, "\n"); + switch (format) { + case PLAIN: + fprintf(stdout, "%ld", (long)negmin); + if (negmin != negmax) + fprintf(stdout, " thru %ld", (long)negmax); + fprintf(stdout, "\n"); + break; + case JSON: + append_category_ranges(range_array, (long)negmin, (long)negmax); + break; + } + } + if (zero) { + switch (format) { + case PLAIN: + fprintf(stdout, "0\n"); + break; + case JSON: + append_category_ranges(range_array, 0, 0); + break; + } } - if (zero) - fprintf(stdout, "0\n"); if (posmin) { - fprintf(stdout, "%ld", (long)posmin); - if (posmin != posmax) - fprintf(stdout, " thru %ld", (long)posmax); - fprintf(stdout, "\n"); + switch (format) { + case PLAIN: + fprintf(stdout, "%ld", (long)posmin); + if (posmin != posmax) + fprintf(stdout, " thru %ld", (long)posmax); + fprintf(stdout, "\n"); + break; + case JSON: + append_category_ranges(range_array, (long)posmin, (long)posmax); + break; + } } - if (null && !skip_nulls) - fprintf(stdout, "%s\n", no_data_str); + if (!skip_nulls) { + if (null) { + switch (format) { + case PLAIN: + fprintf(stdout, "%s\n", no_data_str); + break; + case JSON: + json_object_set_boolean(root_object, "has_nulls", true); + break; + } + } + else { + if (format == JSON) { + json_object_set_boolean(root_object, "has_nulls", false); + } + } + } + + if (format == JSON) { + json_object_set_value(root_object, "ranges", range_value); + output_pretty_json(root_value); + } return (0); } int range_list(CELL negmin, CELL negmax, CELL zero, CELL posmin, CELL posmax, - CELL null, char *no_data_str, int skip_nulls) + CELL null, char *no_data_str, int skip_nulls, + enum OutputFormat format) { + JSON_Value *root_value, *range_value; + JSON_Object *root_object; + JSON_Array *range_array; + + if (format == JSON) { + initialize_json_object(&root_value, &root_object); + initialize_json_array(&range_value, &range_array); + } + if (negmin) { - fprintf(stdout, "%ld\n", (long)negmin); - if (negmin != negmax) - fprintf(stdout, "%ld\n", (long)negmax); + switch (format) { + case PLAIN: + fprintf(stdout, "%ld\n", (long)negmin); + if (negmin != negmax) + fprintf(stdout, "%ld\n", (long)negmax); + break; + case JSON: + append_category_ranges(range_array, (long)negmin, (long)negmax); + break; + } } - if (zero) - fprintf(stdout, "0\n"); + + if (zero) { + switch (format) { + case PLAIN: + fprintf(stdout, "0\n"); + break; + case JSON: + append_category_ranges(range_array, 0, 0); + break; + } + } + if (posmin) { - fprintf(stdout, "%ld\n", (long)posmin); - if (posmin != posmax) - fprintf(stdout, "%ld\n", (long)posmax); + switch (format) { + case PLAIN: + fprintf(stdout, "%ld\n", (long)posmin); + if (posmin != posmax) + fprintf(stdout, "%ld\n", (long)posmax); + break; + case JSON: + append_category_ranges(range_array, (long)posmin, (long)posmax); + break; + } } - if (null && !skip_nulls) - fprintf(stdout, "%s\n", no_data_str); + if (!skip_nulls) { + if (null) { + switch (format) { + case PLAIN: + fprintf(stdout, "%s\n", no_data_str); + break; + case JSON: + json_object_set_boolean(root_object, "has_nulls", true); + break; + } + } + else { + if (format == JSON) { + json_object_set_boolean(root_object, "has_nulls", false); + } + } + } + + if (format == JSON) { + json_object_set_value(root_object, "ranges", range_value); + output_pretty_json(root_value); + } return (0); } diff --git a/raster/r.describe/local_proto.h b/raster/r.describe/local_proto.h index 530f7e0565b..ecf51473899 100644 --- a/raster/r.describe/local_proto.h +++ b/raster/r.describe/local_proto.h @@ -19,17 +19,23 @@ #define __R_DESC_LOCAL_PROTO_H__ #include +#include + +enum OutputFormat { PLAIN, JSON }; /* describe.c */ -int describe(const char *, int, char *, int, int, int, int, int); +int describe(const char *, int, char *, int, int, int, int, int, + enum OutputFormat); /* dumplist.c */ int long_list(struct Cell_stats *, DCELL, DCELL, char *, int, RASTER_MAP_TYPE, - int); + int, enum OutputFormat); int compact_list(struct Cell_stats *, DCELL, DCELL, char *, int, - RASTER_MAP_TYPE, int); -int compact_range_list(CELL, CELL, CELL, CELL, CELL, CELL, char *, int); -int range_list(CELL, CELL, CELL, CELL, CELL, CELL, char *, int); + RASTER_MAP_TYPE, int, enum OutputFormat); +int compact_range_list(CELL, CELL, CELL, CELL, CELL, CELL, char *, int, + enum OutputFormat); +int range_list(CELL, CELL, CELL, CELL, CELL, CELL, char *, int, + enum OutputFormat); /* main.c */ int main(int, char *[]); diff --git a/raster/r.describe/main.c b/raster/r.describe/main.c index 394a228871b..c4e204ba0cf 100644 --- a/raster/r.describe/main.c +++ b/raster/r.describe/main.c @@ -19,9 +19,10 @@ #include #include #include -#include "local_proto.h" #include +#include "local_proto.h" + int main(int argc, char *argv[]) { int as_int; @@ -42,7 +43,9 @@ int main(int argc, char *argv[]) struct Option *map; struct Option *nv; struct Option *nsteps; + struct Option *format; } option; + enum OutputFormat format; G_gisinit(argv[0]); @@ -66,6 +69,9 @@ int main(int argc, char *argv[]) option.nsteps->answer = "255"; option.nsteps->description = _("Number of quantization steps"); + option.format = G_define_standard_option(G_OPT_F_FORMAT); + option.format->guisection = _("Print"); + /*define the different flags */ flag.one = G_define_flag(); @@ -97,12 +103,19 @@ int main(int argc, char *argv[]) as_int = flag.i->answer; no_data_str = option.nv->answer; + if (strcmp(option.format->answer, "json") == 0) { + format = JSON; + } + else { + format = PLAIN; + } + if (sscanf(option.nsteps->answer, "%d", &nsteps) != 1 || nsteps < 1) G_fatal_error(_("%s = %s -- must be greater than zero"), option.nsteps->key, option.nsteps->answer); describe(option.map->answer, compact, no_data_str, range, windowed, nsteps, - as_int, flag.n->answer); + as_int, flag.n->answer, format); return EXIT_SUCCESS; } diff --git a/raster/r.describe/r.describe.html b/raster/r.describe/r.describe.html index d0f98692bad..a8627d4f329 100644 --- a/raster/r.describe/r.describe.html +++ b/raster/r.describe/r.describe.html @@ -12,7 +12,7 @@

    DESCRIPTION

    also ignore the current geographic region and mask. -

    The nv parameter sets the string to be used to represent NULL +

    The nv parameter sets the string to be used to represent NULL values in the module output; the default is '*'.

    The nsteps parameter sets the number of quantisation steps to divide into @@ -36,33 +36,36 @@

    FLAGS

    If the -1 flag is specified, the output appears with one category value/range per line. -

    The -n flag suppresses the reporting of NULL values. +

    The -n flag suppresses the reporting of NULL values.

    EXAMPLES

    The following examples are from the Spearfish60 sample dataset: -

    -# Print the full list of raster map categories: +

    Print the full list of raster map categories

    +
     r.describe landcover.30m
     * 11 21-23 31 32 41-43 51 71 81-83 85 91 92
     
    -

    -# Print the raster range only: + +

    Print the raster range only

    +
     r.describe -r landcover.30m
     11 thru 92
     *
     
    -# Print raster map category range, suppressing nulls: +

    Print raster map category range, suppressing nulls

    +
     r.describe -n landcover.30m
     11 21-23 31 32 41-43 51 71 81-83 85 91 92
     
    -

    -# Print raster map categories, one category per line: + +

    Print raster map categories, one category per line

    +
     r.describe -1 geology
     
    @@ -77,7 +80,7 @@ 

    EXAMPLES

    8 9
    -

    +

    SEE ALSO

    diff --git a/raster/r.describe/testsuite/test_r_describe.py b/raster/r.describe/testsuite/test_r_describe.py new file mode 100644 index 00000000000..b160cd287b2 --- /dev/null +++ b/raster/r.describe/testsuite/test_r_describe.py @@ -0,0 +1,454 @@ +import json + +from grass.gunittest.case import TestCase +from grass.gunittest.main import test +from grass.gunittest.gmodules import SimpleModule + + +class TestRDescribe(TestCase): + @classmethod + def setUpClass(cls): + """Set up a temporary region and generate test data.""" + cls.use_temp_region() + cls.runModule("g.region", n=10, s=0, e=10, w=0, res=1) + + cls.runModule( + "r.mapcalc", + expression="int_map = if(row() % 2 == 0, col(), -col())", + overwrite=True, + ) + cls.runModule( + "r.mapcalc", + expression="float_map = if(row() == 5 && col() == 5, null(), if(row() % 2 == 0, col() * 0.5, -col() * 0.5))", + overwrite=True, + ) + + @classmethod + def tearDownClass(cls): + """Clean up after tests.""" + cls.runModule( + "g.remove", flags="f", type="raster", name=["int_map", "float_map"] + ) + cls.del_temp_region() + + def test_plain_describe_float(self): + """Test r.describe with the default output format and a float-type map.""" + module = SimpleModule("r.describe", map="float_map") + self.assertModule(module) + + result = module.outputs.stdout.strip().splitlines() + + expected_results = [ + "* -5.000000 thru -4.960784 -4.529412 thru -4.490196 -4.019608 thru -3.980392 ", + "-3.509804 thru -3.470588 -3.039216 thru -3.000000 -2.529412 thru -2.490196 ", + "-2.019608 thru -1.980392 -1.549020 thru -1.509804 -1.039216 thru -1.000000 ", + "-0.529412 thru -0.490196 0.450980 thru 0.490196 0.960784 thru 1.000000 ", + "1.470588 thru 1.509804 1.941176 thru 1.980392 2.450980 thru 2.490196 ", + "2.960784 thru 3.000000 3.431373 thru 3.470588 3.941176 thru 3.980392 ", + "4.450980 thru 4.490196 4.960784 thru 5.000000", + ] + + for i, component in enumerate(result): + self.assertEqual( + component, expected_results[i], f"Mismatch at line {i + 1}" + ) + + def test_plain_describe_with_one_flag_float(self): + """Test r.describe with the plain output format, the -1 flag, and a float-type map""" + module = SimpleModule("r.describe", map="float_map", flags="1") + self.assertModule(module) + + result = module.outputs.stdout.strip().splitlines() + + expected_results = [ + "*", + "-5.000000--4.960784", + "-4.529412--4.490196", + "-4.019608--3.980392", + "-3.509804--3.470588", + "-3.039216--3.000000", + "-2.529412--2.490196", + "-2.019608--1.980392", + "-1.549020--1.509804", + "-1.039216--1.000000", + "-0.529412--0.490196", + "0.450980-0.490196", + "0.960784-1.000000", + "1.470588-1.509804", + "1.941176-1.980392", + "2.450980-2.490196", + "2.960784-3.000000", + "3.431373-3.470588", + "3.941176-3.980392", + "4.450980-4.490196", + "4.960784-5.000000", + ] + + for i, component in enumerate(result): + self.assertEqual( + component, expected_results[i], f"Mismatch at line {i + 1}" + ) + + def test_plain_describe_with_r_flag_float(self): + """Test r.describe with the plain output format, the -r flag, and a float-type map""" + module = SimpleModule("r.describe", map="float_map", flags="r") + self.assertModule(module) + + result = module.outputs.stdout.strip().splitlines() + + expected_results = ["* -5.000000 thru 5.000000"] + + for i, component in enumerate(result): + self.assertEqual( + component, expected_results[i], f"Mismatch at line {i + 1}" + ) + + def test_plain_describe_with_i_flag_float(self): + """Test r.describe with the plain output format, the -i flag, and a float-type map""" + module = SimpleModule("r.describe", map="float_map", flags="i") + self.assertModule(module) + + result = module.outputs.stdout.strip().splitlines() + + expected_results = ["* -5 thru -1 1-5"] + + for i, component in enumerate(result): + self.assertEqual( + component, expected_results[i], f"Mismatch at line {i + 1}" + ) + + def test_plain_describe_with_n_flag_float(self): + """Test r.describe with the plain output format, the -n flag, and a float-type map""" + module = SimpleModule("r.describe", map="float_map", flags="n") + self.assertModule(module) + + result = module.outputs.stdout.strip().splitlines() + + expected_results = [ + "-5.000000 thru -4.960784 -4.529412 thru -4.490196 -4.019608 thru -3.980392 ", + "-3.509804 thru -3.470588 -3.039216 thru -3.000000 -2.529412 thru -2.490196 ", + "-2.019608 thru -1.980392 -1.549020 thru -1.509804 -1.039216 thru -1.000000 ", + "-0.529412 thru -0.490196 0.450980 thru 0.490196 0.960784 thru 1.000000 ", + "1.470588 thru 1.509804 1.941176 thru 1.980392 2.450980 thru 2.490196 ", + "2.960784 thru 3.000000 3.431373 thru 3.470588 3.941176 thru 3.980392 ", + "4.450980 thru 4.490196 4.960784 thru 5.000000", + ] + + for i, component in enumerate(result): + self.assertEqual( + component, expected_results[i], f"Mismatch at line {i + 1}" + ) + + def test_json_describe_float(self): + """Test r.describe with the json output format, and a float-type map""" + module = SimpleModule("r.describe", map="float_map", format="json") + self.assertModule(module) + + result = json.loads(module.outputs.stdout) + + expected_results = { + "has_nulls": True, + "ranges": [ + {"min": -5, "max": -4.96078431372549}, + {"min": -4.529411764705882, "max": -4.490196078431373}, + {"min": -4.019607843137255, "max": -3.980392156862745}, + {"min": -3.5098039215686274, "max": -3.4705882352941178}, + {"min": -3.0392156862745097, "max": -3}, + {"min": -2.5294117647058822, "max": -2.4901960784313726}, + {"min": -2.019607843137255, "max": -1.9803921568627452}, + {"min": -1.549019607843137, "max": -1.5098039215686274}, + {"min": -1.0392156862745097, "max": -1}, + {"min": -0.5294117647058822, "max": -0.4901960784313726}, + {"min": 0.4509803921568629, "max": 0.4901960784313726}, + {"min": 0.9607843137254903, "max": 1}, + {"min": 1.4705882352941178, "max": 1.5098039215686274}, + {"min": 1.9411764705882355, "max": 1.9803921568627452}, + {"min": 2.450980392156863, "max": 2.4901960784313726}, + {"min": 2.9607843137254903, "max": 3}, + {"min": 3.431372549019608, "max": 3.4705882352941178}, + {"min": 3.9411764705882355, "max": 3.980392156862745}, + {"min": 4.450980392156863, "max": 4.490196078431373}, + {"min": 4.96078431372549, "max": 5}, + ], + } + + self.assertDictEqual(expected_results, result) + + def test_json_describe_with_one_flag_float(self): + """Test r.describe with the json output format, the -1 flag, and a float-type map""" + module = SimpleModule("r.describe", map="float_map", flags="1", format="json") + self.assertModule(module) + + result = json.loads(module.outputs.stdout) + + expected_results = { + "has_nulls": True, + "ranges": [ + {"min": -5, "max": -4.96078431372549}, + {"min": -4.529411764705882, "max": -4.490196078431373}, + {"min": -4.019607843137255, "max": -3.980392156862745}, + {"min": -3.5098039215686274, "max": -3.4705882352941178}, + {"min": -3.0392156862745097, "max": -3}, + {"min": -2.5294117647058822, "max": -2.4901960784313726}, + {"min": -2.019607843137255, "max": -1.9803921568627452}, + {"min": -1.549019607843137, "max": -1.5098039215686274}, + {"min": -1.0392156862745097, "max": -1}, + {"min": -0.5294117647058822, "max": -0.4901960784313726}, + {"min": 0.4509803921568629, "max": 0.4901960784313726}, + {"min": 0.9607843137254903, "max": 1}, + {"min": 1.4705882352941178, "max": 1.5098039215686274}, + {"min": 1.9411764705882355, "max": 1.9803921568627452}, + {"min": 2.450980392156863, "max": 2.4901960784313726}, + {"min": 2.9607843137254903, "max": 3}, + {"min": 3.431372549019608, "max": 3.4705882352941178}, + {"min": 3.9411764705882355, "max": 3.980392156862745}, + {"min": 4.450980392156863, "max": 4.490196078431373}, + {"min": 4.96078431372549, "max": 5}, + ], + } + + self.assertDictEqual(expected_results, result) + + def test_json_describe_with_r_flag_float(self): + """Test r.describe with the json output format, the -r flag, and a float-type map""" + module = SimpleModule("r.describe", map="float_map", flags="r", format="json") + self.assertModule(module) + + result = json.loads(module.outputs.stdout) + + expected_results = {"has_nulls": True, "ranges": [{"min": -5, "max": 5}]} + + self.assertDictEqual(expected_results, result) + + def test_json_describe_with_i_flag_float(self): + """Test r.describe with the json output format and the -i flag.""" + module = SimpleModule("r.describe", map="float_map", flags="i", format="json") + self.assertModule(module) + + result = json.loads(module.outputs.stdout) + + expected_results = { + "has_nulls": True, + "ranges": [{"min": -5, "max": -1}, {"min": 1, "max": 5}], + } + + self.assertDictEqual(expected_results, result) + + def test_json_describe_with_n_flag_float(self): + """Test r.describe with the json output format, the -n flag, and a float-type map""" + module = SimpleModule("r.describe", map="float_map", flags="n", format="json") + self.assertModule(module) + + result = json.loads(module.outputs.stdout) + + expected_results = { + "ranges": [ + {"min": -5, "max": -4.96078431372549}, + {"min": -4.529411764705882, "max": -4.490196078431373}, + {"min": -4.019607843137255, "max": -3.980392156862745}, + {"min": -3.5098039215686274, "max": -3.4705882352941178}, + {"min": -3.0392156862745097, "max": -3}, + {"min": -2.5294117647058822, "max": -2.4901960784313726}, + {"min": -2.019607843137255, "max": -1.9803921568627452}, + {"min": -1.549019607843137, "max": -1.5098039215686274}, + {"min": -1.0392156862745097, "max": -1}, + {"min": -0.5294117647058822, "max": -0.4901960784313726}, + {"min": 0.4509803921568629, "max": 0.4901960784313726}, + {"min": 0.9607843137254903, "max": 1}, + {"min": 1.4705882352941178, "max": 1.5098039215686274}, + {"min": 1.9411764705882355, "max": 1.9803921568627452}, + {"min": 2.450980392156863, "max": 2.4901960784313726}, + {"min": 2.9607843137254903, "max": 3}, + {"min": 3.431372549019608, "max": 3.4705882352941178}, + {"min": 3.9411764705882355, "max": 3.980392156862745}, + {"min": 4.450980392156863, "max": 4.490196078431373}, + {"min": 4.96078431372549, "max": 5}, + ] + } + + self.assertDictEqual(expected_results, result) + + def test_plain_describe_int(self): + """Test r.describe with the default output format, and a int-type map""" + module = SimpleModule("r.describe", map="int_map") + self.assertModule(module) + + result = module.outputs.stdout.strip().splitlines() + + expected_results = [ + "-10 thru -1 1-10", + ] + + for i, component in enumerate(result): + self.assertEqual( + component, expected_results[i], f"Mismatch at line {i + 1}" + ) + + def test_plain_describe_with_one_flag_int(self): + """Test r.describe with the plain output format, the -1 flag, and a int-type map""" + module = SimpleModule("r.describe", map="int_map", flags="1") + self.assertModule(module) + + result = module.outputs.stdout.strip().splitlines() + + expected_results = [ + "-10", + "-9", + "-8", + "-7", + "-6", + "-5", + "-4", + "-3", + "-2", + "-1", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10", + ] + + for i, component in enumerate(result): + self.assertEqual( + component, expected_results[i], f"Mismatch at line {i + 1}" + ) + + def test_plain_describe_with_r_flag_int(self): + """Test r.describe with the plain output format, the -r flag, and a int-type map""" + module = SimpleModule("r.describe", map="int_map", flags="r") + self.assertModule(module) + + result = module.outputs.stdout.strip().splitlines() + + expected_results = ["-10 thru -1", "1 thru 10"] + + for i, component in enumerate(result): + self.assertEqual( + component, expected_results[i], f"Mismatch at line {i + 1}" + ) + + def test_plain_describe_with_i_flag_int(self): + """Test r.describe with the plain output format, the -i flag, and a int-type map""" + module = SimpleModule("r.describe", map="int_map", flags="i") + self.assertModule(module) + + result = module.outputs.stdout.strip().splitlines() + + expected_results = ["-10 thru -1 1-10"] + + for i, component in enumerate(result): + self.assertEqual( + component, expected_results[i], f"Mismatch at line {i + 1}" + ) + + def test_plain_describe_with_n_flag_int(self): + """Test r.describe with the plain output format, the -n flag, and a int-type map""" + module = SimpleModule("r.describe", map="int_map", flags="n") + self.assertModule(module) + + result = module.outputs.stdout.strip().splitlines() + + expected_results = ["-10 thru -1 1-10"] + + for i, component in enumerate(result): + self.assertEqual( + component, expected_results[i], f"Mismatch at line {i + 1}" + ) + + def test_json_describe_int(self): + """Test r.describe with the json output format, and a int-type map""" + module = SimpleModule("r.describe", map="int_map", format="json") + self.assertModule(module) + + result = json.loads(module.outputs.stdout) + + expected_results = { + "has_nulls": False, + "ranges": [{"min": -10, "max": -1}, {"min": 1, "max": 10}], + } + + self.assertDictEqual(expected_results, result) + + def test_json_describe_with_one_flag_int(self): + """Test r.describe with the json output format, the -1 flag, and a int-type map""" + module = SimpleModule("r.describe", map="int_map", flags="1", format="json") + self.assertModule(module) + + result = json.loads(module.outputs.stdout) + + expected_results = { + "has_nulls": False, + "values": [ + -10, + -9, + -8, + -7, + -6, + -5, + -4, + -3, + -2, + -1, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + ], + } + + self.assertDictEqual(expected_results, result) + + def test_json_describe_with_r_flag_int(self): + """Test r.describe with the json output format, the -r flag, and a int-type map""" + module = SimpleModule("r.describe", map="int_map", flags="r", format="json") + self.assertModule(module) + + result = json.loads(module.outputs.stdout) + + expected_results = { + "has_nulls": False, + "ranges": [{"min": -10, "max": -1}, {"min": 1, "max": 10}], + } + + self.assertDictEqual(expected_results, result) + + def test_json_describe_with_i_flag_int(self): + """Test r.describe with the json output format, the -i flag, and a int-type map""" + module = SimpleModule("r.describe", map="int_map", flags="i", format="json") + self.assertModule(module) + + result = json.loads(module.outputs.stdout) + + expected_results = { + "has_nulls": False, + "ranges": [{"min": -10, "max": -1}, {"min": 1, "max": 10}], + } + + self.assertDictEqual(expected_results, result) + + def test_json_describe_with_n_flag_int(self): + """Test r.describe with the json output format, the -n flag, and a int-type map""" + module = SimpleModule("r.describe", map="int_map", flags="n", format="json") + self.assertModule(module) + + result = json.loads(module.outputs.stdout) + + expected_results = {"ranges": [{"min": -10, "max": -1}, {"min": 1, "max": 10}]} + + self.assertDictEqual(expected_results, result) + + +if __name__ == "__main__": + test() diff --git a/raster/r.distance/testsuite/test_distance.py b/raster/r.distance/testsuite/test_distance.py new file mode 100644 index 00000000000..3773677c909 --- /dev/null +++ b/raster/r.distance/testsuite/test_distance.py @@ -0,0 +1,80 @@ +from grass.gunittest.case import TestCase +from grass.gunittest.main import test +from grass.gunittest.gmodules import SimpleModule + + +class TestRDistance(TestCase): + @classmethod + def setUpClass(cls): + """Set up a temporary region and generate test data.""" + cls.use_temp_region() + cls.runModule("g.region", n=10, s=0, e=10, w=0, res=1) + # Create 'map1' with a block at the top-left corner + cls.runModule( + "r.mapcalc", + expression="map1 = if(row() <= 2 && col() <= 2, 1, null())", + overwrite=True, + ) + # Create 'map2' with a block in the center + cls.runModule( + "r.mapcalc", + expression="map2 = if(row() >= 4 && row() <=6 && col() >= 4 && col() <= 6, 1, null())", + overwrite=True, + ) + cls.runModule("r.mapcalc", expression="map3 = null()", overwrite=True) + + @classmethod + def tearDownClass(cls): + """Clean up after tests.""" + cls.runModule( + "g.remove", flags="f", type="raster", name=["map1", "map2", "map3"] + ) + cls.del_temp_region() + + def test_distance(self): + """Test distance calculation between map1 and map2.""" + module = SimpleModule("r.distance", map=("map1", "map2")) + self.assertModule(module) + + result = module.outputs.stdout.strip().splitlines() + + expected_results = ["1:1:2.8284271247:1.5:8.5:3.5:6.5"] + + for i, component in enumerate(result): + self.assertEqual( + component, expected_results[i], f"Mismatch at line {i + 1}" + ) + + def test_overlap_distance(self): + """Test r.distance when comparing a map to itself with overlapping features.""" + module = SimpleModule("r.distance", map=("map1", "map1"), flags="o") + self.assertModule(module) + + result = module.outputs.stdout.strip().splitlines() + + expected_results = ["1:1:0:0.5:9.5:0.5:9.5"] + + self.assertEqual( + result, + expected_results, + "Mismatch in r.distance output for overlapping features", + ) + + def test_null_distance(self): + """Test r.distance when reporting null values with -n flag.""" + module = SimpleModule("r.distance", map=("map3", "map2"), flags="n") + self.assertModule(module) + + result = module.outputs.stdout.strip().splitlines() + + expected_results = ["*:*:0:0.5:9.5:0.5:9.5", "*:1:2:3.5:8.5:3.5:6.5"] + + self.assertEqual( + result, + expected_results, + "Mismatch in r.distance output for reporting null objects as *", + ) + + +if __name__ == "__main__": + test() diff --git a/raster/r.fill.stats/r.fill.stats.html b/raster/r.fill.stats/r.fill.stats.html index bd966fa3f05..c8414796e73 100644 --- a/raster/r.fill.stats/r.fill.stats.html +++ b/raster/r.fill.stats/r.fill.stats.html @@ -77,7 +77,7 @@

    DESCRIPTION

    The images below show a gradiometer dataset with gaps and its interpolated equivalent, produced using the spatially weighted mean -operator (mode="wmean"). +operator (mode="wmean").

    @@ -132,7 +132,7 @@

    Usage

    increasing distance from the latter. Another way of explaining this effect is to state that larger "power" settings result in more localized interpolation, smaller ones in more globalized interpolation. -The default setting is power=2.0. +The default setting is power=2.0.

    @@ -171,7 +171,7 @@

    Usage

    neighborhood (defined by the search radius distance) that contain a value in the input map, multiplied by their weights, and dividing the result by the sum of all weights in the neighborhood. -For mode=wmean, this means that interpolated output cells that +For mode=wmean, this means that interpolated output cells that were computed from many nearby input cells have very low uncertainty and vice versa. For all other modes, all weights in the neighborhood are constant "1" and the uncertainty measure is a simple measure of how @@ -198,7 +198,7 @@

    Smoothing

    Effect of smoothing the original data: The top row shows a gap-filled surface computed from a rasterized Lidar point -cloud (using mode=wmean and power=2), and the derived slope, aspect, +cloud (using mode=wmean and power=2), and the derived slope, aspect, and profile curvature maps. The smoothing effect is clearly visible. The bottom row shows the effect of setting the -k flag: Preserving the original cell values in the interpolated output produces and unsmoothed, noisy surface, and likewise @@ -292,11 +292,11 @@

    Spatial weighting scheme

    Note that the weights in such a small window drop rapidly for the -default setting of power=2. +default setting of power=2.

    -If the distance is given in map units (flag -m), then the +If the distance is given in map units (flag -m), then the search window can be modeled more accurately as a circle. The illustration below shows the precomputed weights for a distance in map units that is approximately equivalent to four cells from the center diff --git a/raster/r.flow/r.flow.html b/raster/r.flow/r.flow.html index 28ea66a6c23..da06e63bf5c 100644 --- a/raster/r.flow/r.flow.html +++ b/raster/r.flow/r.flow.html @@ -32,8 +32,8 @@

    NOTES

    or vice-versa), cross a barrier, or arrive at a cell with undefined elevation or aspect. Another option, skip, indicates that only the flowlines from every val-th cell are to be included -in flowline. The default skip is max(1, <rows -in elevation>/50, <cols in elevation>/50). A +in flowline. The default skip is max(1, <rows +in elevation>/50, <cols in elevation>/50). A high skip usually speeds up processing time and often improves the readability of a visualization of flowline.

    diff --git a/raster/r.geomorphon/r.geomorphon.html b/raster/r.geomorphon/r.geomorphon.html index 57f744392bd..36b85ce51fd 100644 --- a/raster/r.geomorphon/r.geomorphon.html +++ b/raster/r.geomorphon/r.geomorphon.html @@ -1,5 +1,8 @@

    DESCRIPTION

    +r.geomorphon calculates terrain forms using machine-vison +technique called geomorphons. +

    What is geomorphon:

    What is geomorphon
    @@ -32,7 +35,7 @@

    What is geomorphon:

    There are 3**8 = 6561 possible ternary patterns (8-tuples). However by eliminating all patterns that are results of either rotation or reflection -of other patterns wa set of 498 patterns remain referred as geomorphons. +of other patterns we get a set of 498 patterns remain referred as geomorphons. This is a comprehensive and exhaustive set of idealized landforms that are independent of the size, relief, and orientation of the actual landform. @@ -184,7 +187,7 @@

    REFERENCES

  • Stepinski, T., Jasiewicz, J., 2011, Geomorphons - a new approach to classification of landform, in : Eds: Hengl, T., Evans, I.S., Wilson, J.P., and Gould, M., Proceedings of Geomorphometry 2011, Redlands, -109-112 (PDF)
  • +109-112 (PDF)
  • Jasiewicz, J., Stepinski, T., 2013, Geomorphons - a pattern recognition approach to classification and mapping of landforms, Geomorphology, vol. 182, 147-156 (DOI: 10.1016/j.geomorph.2012.11.005)
  • diff --git a/raster/r.gwflow/r.gwflow.html b/raster/r.gwflow/r.gwflow.html index 05dbe88d878..3edc76354eb 100644 --- a/raster/r.gwflow/r.gwflow.html +++ b/raster/r.gwflow/r.gwflow.html @@ -1,7 +1,7 @@

    DESCRIPTION

    -This numerical program calculates implicit transient, confined and -unconfined groundwater flow in two dimensions based on +r.gwflow is a numerical program which calculates implicit transient, +confined and unconfined groundwater flow in two dimensions based on raster maps and the current region settings. All initial and boundary conditions must be provided as raster maps. The unit of the current project's coordinate reference system must be meters. diff --git a/raster/r.his/r.his.html b/raster/r.his/r.his.html index 0dd2c429e04..964ac2d2273 100644 --- a/raster/r.his/r.his.html +++ b/raster/r.his/r.his.html @@ -1,7 +1,7 @@

    DESCRIPTION

    HIS stands for hue, intensity, and saturation. -This program produces red, green and blue raster map layers +r.his produces red, green and blue raster map layers providing a visually pleasing combination of hue, intensity, and saturation values from two or three user-specified raster map layers. diff --git a/raster/r.horizon/r.horizon.html b/raster/r.horizon/r.horizon.html index 71f8599dd70..e7ebcf68783 100644 --- a/raster/r.horizon/r.horizon.html +++ b/raster/r.horizon/r.horizon.html @@ -258,18 +258,18 @@

    REFERENCES

    Hofierka J., Huld T., Cebecauer T., Suri M., 2007. Open Source Solar Radiation Tools for Environmental and Renewable Energy Applications, -International Symposium on +International Symposium on Environmental Software Systems, Prague, 2007

    Neteler M., Mitasova H., 2004. Open Source GIS: A GRASS GIS -Approach, Springer, New York. +Approach, Springer, New York. ISBN: 1-4020-8064-6, 2nd Edition 2004 (reprinted 2005), 424 pages -

    Project PVGIS, European +

    Project PVGIS, European Commission, DG Joint Research Centre 2001-2007

    Suri M., Hofierka J., 2004. A New GIS-based Solar Radiation Model and Its Application for -Photovoltaic Assessments. Transactions -in GIS, 8(2), 175-190 +Photovoltaic Assessments. Transactions +in GIS, 8(2), 175-190 (DOI: 10.1111/j.1467-9671.2004.00174.x)

    SEE ALSO

    diff --git a/raster/r.in.ascii/r.in.ascii.html b/raster/r.in.ascii/r.in.ascii.html index 0f450de5fd4..eacf22ef81e 100644 --- a/raster/r.in.ascii/r.in.ascii.html +++ b/raster/r.in.ascii/r.in.ascii.html @@ -52,7 +52,7 @@

    NOTES

    The data (which follows the header section) must contain -r x c values, but it is not necessary +r x c values, but it is not necessary that all the data for a row be on one line. A row may be split over many lines. diff --git a/raster/r.in.bin/r.in.bin.html b/raster/r.in.bin/r.in.bin.html index 9c57e38f98b..2d457b1680e 100644 --- a/raster/r.in.bin/r.in.bin.html +++ b/raster/r.in.bin/r.in.bin.html @@ -10,7 +10,7 @@

    DESCRIPTION

    The north, south, east, and west field values are the coordinates of the edges of the geographic region. The rows and cols values describe the dimensions of the matrix of data to follow. If the input is a -GMT binary array +GMT binary array (-h flag), the six dimension fields (north, south, east, west, rows and cols) are obtained from the GMT header. If the bytes field is entered incorrectly an error will be generated suggesting a closer bytes value. diff --git a/raster/r.in.gdal/r.in.gdal.html b/raster/r.in.gdal/r.in.gdal.html index 76de4e4eec5..7083a48410e 100644 --- a/raster/r.in.gdal/r.in.gdal.html +++ b/raster/r.in.gdal/r.in.gdal.html @@ -20,7 +20,7 @@

    GDAL supported raster formats

    Full details on all GDAL supported formats are available at:

    -https://gdal.org/formats_list.html +https://gdal.org/en/stable/drivers/raster/

    Selected formats out of the more than 140 supported formats: @@ -132,8 +132,8 @@

    Project Creation

    information when importing datasets if the source format includes CRS information, and if the GDAL driver supports it. If the CRS of the source dataset does not match the CRS of the current project r.in.gdal will -report an error message (Coordinate reference system of dataset does not appear to -match current project) and then report the PROJ_INFO parameters of +report an error message (Coordinate reference system of dataset does not appear to +match current project) and then report the PROJ_INFO parameters of the source dataset.

    @@ -177,8 +177,8 @@

    Map names: Management of offset and leading zeros

    across different input files.

    The num_digits parameter allows defining the number of leading zeros -(zero padding) in case of band numbers (e.g., to turn band.1 into -band.001). +(zero padding) in case of band numbers (e.g., to turn band.1 into +band.001).

    NOTES

    @@ -252,7 +252,7 @@

    Error Messages

    "ERROR: Input map is rotated - cannot import."
    In this case the image must be first externally rotated, applying the rotation info stored in the metadata field of the raster image file. For example, the -gdalwarp software can be used +gdalwarp software can be used to transform the map to North-up (note, there are several gdalwarp parameters to select the resampling algorithm): @@ -296,9 +296,8 @@

    EXAMPLES

    ECAD Data

    -The European Climate Assessment and Dataset (ECAD) project -provides climate data for Europe ranging from 1950 - 2015 or later -(Terms of use). +The European Climate Assessment and Dataset (ECAD) project +provides climate data for Europe ranging from 1950 - 2015 or later. To import the different chunks of data provided by the project as netCDF files, the offset parameter can be used to properly assign numbers to the series of daily raster maps from 1st Jan 1950 (in case of importing the ECAD data @@ -363,8 +362,8 @@

    GLOBE DEM

    Raster file import over network

    Since GDAL 2.x it is possible to import raster data over the network -(see GDAL Virtual File Systems) -including Cloud Optimized GeoTIFF, +(see GDAL Virtual File Systems) +including Cloud Optimized GeoTIFF, i.e. access uncompressed and compressed raster data via a http(s) or ftp connection. As an example the import of the global SRTMGL1 V003 tiles at 1 arc second (about 30 meters) resolution, void-filled: @@ -398,7 +397,7 @@

    HDF

    REFERENCES

    -GDAL Pages: https://gdal.org/ +GDAL Pages: https://gdal.org

    SEE ALSO

    @@ -416,5 +415,5 @@

    SEE ALSO

    AUTHOR

    -Frank Warmerdam +Frank Warmerdam (email). diff --git a/raster/r.in.lidar/r.in.lidar.html b/raster/r.in.lidar/r.in.lidar.html index f92ad0485a8..a3346047bec 100644 --- a/raster/r.in.lidar/r.in.lidar.html +++ b/raster/r.in.lidar/r.in.lidar.html @@ -125,7 +125,7 @@

    Statistics

    coeff_var
    This computes the coefficient of variance of point values for each cell. Coefficient of variance is given in percentage and defined as -(stddev/mean)*100.
    +(stddev/mean)*100.
    median
    This computes the median of point values for each cell
    percentile
    @@ -210,7 +210,7 @@

    Filtering

    The user can use a combination of r.in.lidar output maps to create custom raster-based filters, for example, use r.mapcalc to create -a mean-(2*stddev) map. (In this example the user may want to +a mean-(2*stddev) map. (In this example the user may want to include a lower bound filter in r.mapcalc to remove highly variable points (small n) or run r.neighbors to smooth the stddev map before further use.) @@ -332,10 +332,10 @@

    Memory consumption

    (> 10000x10000 pixels). If the module refuses to start complaining that there isn't enough memory, use the percent parameter to run the module in several passes. -In addition using a less precise map format (CELL [integer] or -FCELL [floating point]) will use less memory than a DCELL +In addition using a less precise map format (CELL [integer] or +FCELL [floating point]) will use less memory than a DCELL [double precision floating point] output map. -For method=n, the CELL format is used +For method=n, the CELL format is used automatically.

    @@ -356,7 +356,7 @@

    Memory consumption

    the number of data points.

    -The default map type=FCELL is intended as compromise between +The default map type=FCELL is intended as compromise between preserving data precision and limiting system resource consumption.

    Trim option

    @@ -454,28 +454,28 @@

    Serpent Mound dataset

    This example is analogous to the example used in the GRASS wiki page for importing LAS as raster DEM. -

    The sample LAS data are in the file "Serpent Mound Model LAS Data.las", +

    The sample LAS data are in the file "Serpent Mound Model LAS Data.laz", available at -appliedimagery.com: +Serpent Mound Model LAS Data.laz:

     # print LAS file info
    -r.in.lidar -p input="Serpent Mound Model LAS Data.las"
    +r.in.lidar -p input="Serpent Mound Model LAS Data.laz"
     
     # using v.in.lidar to create a new project
     # create project with CRS information of the LAS data
    -v.in.lidar -i input="Serpent Mound Model LAS Data.las" project=Serpent_Mound
    +v.in.lidar -i input="Serpent Mound Model LAS Data.laz" project=Serpent_Mound
     
     # quit and restart GRASS in the newly created project "Serpent_Mound"
     
     # scan the extents of the LAS data
    -r.in.lidar -sg input="Serpent Mound Model LAS Data.las"
    +r.in.lidar -sg input="Serpent Mound Model LAS Data.laz"
     
     # set the region to the extents of the LAS data, align to resolution
     g.region n=4323641.57 s=4320942.61 w=289020.90 e=290106.02 res=1 -ap
     
     # import as raster DEM
    -r.in.lidar input="Serpent Mound Model LAS Data.las" \
    +r.in.lidar input="Serpent Mound Model LAS Data.laz" \
                output=Serpent_Mound_Model_LAS_Data method=mean
     
    @@ -488,7 +488,7 @@

    Height above ground

    The mean height above ground of the points can be computed for each raster cell (the ground elevation is given by the raster map -elevation): +elevation):
     g.region raster=elevation -p
    @@ -551,14 +551,14 @@ 

    Multiple file input

    KNOWN ISSUES

      -
    • The "nan" value (as defined in C language) can leak into +
    • The "nan" value (as defined in C language) can leak into coeff_var raster maps. Cause is unknown. Possible - work-around is: r.null setnull=nan or - r.mapcalc 'no_nan = if(map == map, map, null())'.
    • + work-around is: r.null setnull=nan or + r.mapcalc 'no_nan = if(map == map, map, null())'.
    • Only one method can be applied for a single run and multiple map output from a single run - (e.g. method=string[,string,...] output=name[,name,...] - or n=string mean=string) is no supported.
    • + (e.g. method=string[,string,...] output=name[,name,...] + or n=string mean=string) is no supported. @@ -597,14 +597,14 @@

      REFERENCES

    • V. Petras, A. Petrasova, J. Jeziorska, H. Mitasova (2016): Processing UAV and lidar point clouds in GRASS GIS. -XXIII ISPRS Congress 2016 [ISPRS Archives, ResearchGate]
    • +XXIII ISPRS Congress 2016 [ISPRS Archives, ResearchGate]
    • ASPRS LAS format
    • -LAS library
    • +LAS library
    • -LAS library C API documentation
    • +LAS library C API documentation

    AUTHORS

    diff --git a/raster/r.in.mat/r.in.mat.html b/raster/r.in.mat/r.in.mat.html index be0f521dca2..d5613c1ed61 100644 --- a/raster/r.in.mat/r.in.mat.html +++ b/raster/r.in.mat/r.in.mat.html @@ -21,7 +21,7 @@

    DESCRIPTION

    The 'map_name' variable is optional, if it exists, and is valid, the new map will be thus named. If it doesn't exist or a name is specified with the output= option, the raster map's name will be set to -"MatFile" or the name specified respectively. +"MatFile" or the name specified respectively. (maximum 64 characters; normal GRASS naming rules apply)

    @@ -43,9 +43,9 @@

    NOTES

    copied between different system architectures without binary translation (caveat: see "TODO" below).

    -As there is no IEEE value for NaN in integer arrays, GRASS's null +As there is no IEEE value for NaN in integer arrays, GRASS's null value may be used to represent it within these maps. Usually Matlab will save -any integer based matrix with NaN values as a double-precision +any integer based matrix with NaN values as a double-precision floating point array, so this usually isn't an issue. To save space, once the map is loaded into GRASS you can convert it back to an integer map with the following command: @@ -53,7 +53,7 @@

    NOTES

    r.mapcalc "int_map = int(MATFile_map)"
    -NaN values in either floating point or double-precision floating point +NaN values in either floating point or double-precision floating point matrices should translate into null values as expected.

    @@ -73,8 +73,8 @@

    NOTES


    -Remember Matlab arrays are referenced as (row,column), -i.e. (y,x). +Remember Matlab arrays are referenced as (row,column), +i.e. (y,x).

    In addition, r.in.mat and r.out.mat make for a nice binary container format for transferring georeferenced maps around, diff --git a/raster/r.in.pdal/r.in.pdal.html b/raster/r.in.pdal/r.in.pdal.html index cefc1984bef..61afe778c6a 100644 --- a/raster/r.in.pdal/r.in.pdal.html +++ b/raster/r.in.pdal/r.in.pdal.html @@ -123,7 +123,7 @@

    Statistics

    coeff_var
    This computes the coefficient of variance of point values for each cell. Coefficient of variance is given in percentage and defined as -100 * sqrt(variance) / mean.
    +100 * sqrt(variance) / mean.
    median
    This computes the median of point values for each cell
    mode
    @@ -238,7 +238,7 @@

    Filtering and selection

    The user can use a combination of r.in.pdal output maps to create custom raster-based filters, for example, use r.mapcalc to create -a mean-(2*stddev) map. (In this example the user may want to +a mean-(2*stddev) map. (In this example the user may want to include a lower bound filter in r.mapcalc to remove highly variable points (small n) or run r.neighbors to smooth the stddev map before further use.) @@ -394,10 +394,10 @@

    Memory consumption

    (> 10000x10000 pixels). If the module refuses to start complaining that there isn't enough memory, use the percent parameter to run the module in several passes. -In addition using a less precise map format (CELL [integer] or -FCELL [floating point]) will use less memory than a DCELL +In addition using a less precise map format (CELL [integer] or +FCELL [floating point]) will use less memory than a DCELL [double precision floating point] output map. -For methods=n, mode, sidnmin, sidnmax, the CELL +For methods=n, mode, sidnmin, sidnmax, the CELL format is used automatically.

    @@ -418,7 +418,7 @@

    Memory consumption

    the number of data points.

    -The default map type=FCELL is intended as compromise between +The default map type=FCELL is intended as compromise between preserving data precision and limiting system resource consumption.

    Trim option

    @@ -512,28 +512,28 @@

    Serpent Mound dataset

    This example is analogous to the example used in the GRASS wiki page for importing LAS as raster DEM. -

    The sample LAS data are in the file "Serpent Mound Model LAS Data.las", +

    The sample LAS data are in the file "Serpent Mound Model LAS Data.laz", available at -appliedimagery.com: +Serpent Mound Model LAS Data.laz:

     # print LAS file info
    -r.in.pdal -p input="Serpent Mound Model LAS Data.las"
    +r.in.pdal -p input="Serpent Mound Model LAS Data.laz"
     
     # using v.in.lidar to create a new project
     # create a project with CRS information of the LAS data
    -v.in.lidar -i input="Serpent Mound Model LAS Data.las" project=Serpent_Mound
    +v.in.lidar -i input="Serpent Mound Model LAS Data.laz" project=Serpent_Mound
     
     # quit and restart GRASS in the newly created project "Serpent_Mound"
     
     # scan the extents of the LAS data
    -r.in.pdal -g input="Serpent Mound Model LAS Data.las"
    +r.in.pdal -g input="Serpent Mound Model LAS Data.laz"
     
     # set the region to the extents of the LAS data, align to resolution
     g.region n=4323641.57 s=4320942.61 w=289020.90 e=290106.02 res=1 -ap
     
     # import as raster DEM
    -r.in.pdal input="Serpent Mound Model LAS Data.las" \
    +r.in.pdal input="Serpent Mound Model LAS Data.laz" \
                output=Serpent_Mound_Model_LAS_Data method=mean
     
    @@ -546,7 +546,7 @@

    Height above ground

    The mean height above ground of the points can be computed for each raster cell (the ground elevation is given by the raster map -elevation): +elevation):
     g.region raster=elevation -p
    @@ -611,8 +611,8 @@ 

    KNOWN ISSUES

    • Only one method can be applied for a single run and multiple map output from a single run - (e.g. method=string[,string,...] output=name[,name,...] - or n=string mean=string) is no supported.
    • + (e.g. method=string[,string,...] output=name[,name,...] + or n=string mean=string) is no supported.
    If you encounter any problems (or solutions!) please contact the GRASS @@ -647,7 +647,7 @@

    REFERENCES

  • V. Petras, A. Petrasova, J. Jeziorska, H. Mitasova (2016): Processing UAV and lidar point clouds in GRASS GIS. -XXIII ISPRS Congress 2016 [ISPRS Archives, ResearchGate]
  • +XXIII ISPRS Congress 2016 [ISPRS Archives, ResearchGate]
  • ASPRS LAS format
  • @@ -659,7 +659,7 @@

    AUTHORS

    Markus Metz
    Vaclav Petras, -NCSU GeoForAll Lab +NCSU GeoForAll Lab (base_raster option, documentation),
    Maris Nartiss, LU GZZF (refactoring, additional filters, custom dimension support) diff --git a/raster/r.in.poly/r.in.poly.html b/raster/r.in.poly/r.in.poly.html index d47212a162d..3860f414f7c 100644 --- a/raster/r.in.poly/r.in.poly.html +++ b/raster/r.in.poly/r.in.poly.html @@ -56,20 +56,20 @@

    Input Format

    -The A signals the beginning of a filled polygon. +The A signals the beginning of a filled polygon. It must appear in the first column. -The L signals the beginning of a linear feature. +The L signals the beginning of a linear feature. It also must appear in the first column. -The P signals the beginning of a single cell point feature. +The P signals the beginning of a single cell point feature. Again, it must appear in the first column. The coordinates of the vertices of the polygon, or the coordinates defining the linear or point feature follow and must have a space in the first column and at least one space between the easting and the northing. To give meaning to the features, the -"=" indicates that the feature currently being +"=" indicates that the feature currently being processed has category value cat# (which must be an integer) and a label (which may be more than one word, or which may be omitted). diff --git a/raster/r.in.xyz/r.in.xyz.html b/raster/r.in.xyz/r.in.xyz.html index 70103d8e778..55c04e74a2d 100644 --- a/raster/r.in.xyz/r.in.xyz.html +++ b/raster/r.in.xyz/r.in.xyz.html @@ -43,7 +43,7 @@

    DESCRIPTION

    • Variance and derivatives use the biased estimator (n). [subject to change]
    • Coefficient of variance is given in percentage and defined as -(stddev/mean)*100.
    • +(stddev/mean)*100.

    @@ -78,8 +78,8 @@

    Memory use

    will use a large amount of system memory for large raster regions (10000x10000). If the module refuses to start complaining that there isn't enough memory, use the percent parameter to run the module in several passes. -In addition using a less precise map format (CELL [integer] or -FCELL [floating point]) will use less memory than a DCELL +In addition using a less precise map format (CELL [integer] or +FCELL [floating point]) will use less memory than a DCELL [double precision floating point] output map. Methods such as n, min, max, sum will also use less memory, while stddev, variance, and coeff_var will use more. @@ -91,9 +91,9 @@

    Memory use

    but for the aggregate fns it will also depend on the number of data points. (?) -->

    -The default map type=FCELL is intended as compromise between +The default map type=FCELL is intended as compromise between preserving data precision and limiting system resource consumption. -If reading data from a stdin stream, the program can only run using +If reading data from a stdin stream, the program can only run using a single pass.

    Setting region bounds and resolution

    @@ -122,15 +122,15 @@

    Setting region bounds and resolution

    If you only intend to interpolate the data with r.to.vect and v.surf.rst, then there is little point to setting the region resolution so fine that you only catch one data point per cell -- you might -as well use "v.in.ascii -zbt" directly. +as well use "v.in.ascii -zbt" directly.

    Filtering

    Points falling outside the current region will be skipped. This includes points falling exactly on the southern region bound. -(to capture those adjust the region with "g.region s=s-0.000001"; +(to capture those adjust the region with "g.region s=s-0.000001"; see g.region) -

    Blank lines and comment lines starting with the hash symbol (#) +

    Blank lines and comment lines starting with the hash symbol (#) will be skipped.

    @@ -147,7 +147,7 @@

    Filtering

    The user can use a combination of r.in.xyz output maps to create -custom filters. e.g. use r.mapcalc to create a mean-(2*stddev) +custom filters. e.g. use r.mapcalc to create a mean-(2*stddev) map. [In this example the user may want to include a lower bound filter in r.mapcalc to remove highly variable points (small n) or run r.neighbors to smooth the stddev map before further use.] @@ -193,7 +193,7 @@

    Import of x,y,string data

    r.in.xyz is expecting numeric values as z column. In order to perform a occurrence count operation even on x,y data with non-numeric attribute(s), the data can be imported using either the x or y -coordinate as a fake z column for method=n (count +coordinate as a fake z column for method=n (count number of points per grid cell), the z values are ignored anyway.

    EXAMPLES

    @@ -287,7 +287,7 @@

    TODO

    • Support for multiple map output from a single run.
      - method=string[,string,...] output=name[,name,...]
      + method=string[,string,...] output=name[,name,...]
      This can be easily handled by a wrapper script, with the added benefit of it being very simple to parallelize that way.
    @@ -295,8 +295,8 @@

    TODO

    KNOWN ISSUES

      -
    • "nan" can leak into coeff_var maps. -
      Cause unknown. Possible work-around: "r.null setnull=nan"
    • +
    • "nan" can leak into coeff_var maps. +
      Cause unknown. Possible work-around: "r.null setnull=nan"
    diff --git a/raster/r.info/main.c b/raster/r.info/main.c index ed06f72eba7..ad40ea6b3e9 100644 --- a/raster/r.info/main.c +++ b/raster/r.info/main.c @@ -40,6 +40,7 @@ enum OutputFormat { PLAIN, JSON }; /* local prototypes */ static void format_double(const double, char *); static void compose_line(FILE *, const char *, ...); +static char *history_as_string(struct History *hist); int main(int argc, char **argv) { @@ -773,16 +774,14 @@ int main(int argc, char **argv) json_object_set_string( root_object, "description", Rast_get_history(&hist, HIST_KEYWRD)); - JSON_Value *comments_value = json_value_init_array(); - JSON_Array *comments = json_array(comments_value); - if (Rast_history_length(&hist)) { - for (i = 0; i < Rast_history_length(&hist); i++) { - json_array_append_string( - comments, Rast_history_line(&hist, i)); - } + char *buffer = history_as_string(&hist); + if (buffer) { + json_object_set_string(root_object, "comments", buffer); + G_free(buffer); + } + else { + json_object_set_null(root_object, "comments"); } - json_object_set_value(root_object, "comments", - comments_value); } else { json_object_set_null(root_object, "source1"); @@ -794,7 +793,7 @@ int main(int argc, char **argv) } } - if (hflag->answer || format == JSON) { + if (hflag->answer) { if (hist_ok) { switch (format) { case PLAIN: @@ -823,16 +822,15 @@ int main(int argc, char **argv) json_object_set_string( root_object, "description", Rast_get_history(&hist, HIST_KEYWRD)); - JSON_Value *comments_value = json_value_init_array(); - JSON_Array *comments = json_array(comments_value); - if (Rast_history_length(&hist)) { - for (i = 0; i < Rast_history_length(&hist); i++) { - json_array_append_string( - comments, Rast_history_line(&hist, i)); - } + char *buffer = history_as_string(&hist); + if (buffer) { + json_object_set_string(root_object, "comments", buffer); + G_free(buffer); + } + else { + json_object_set_null(root_object, "comments"); } - json_object_set_value(root_object, "comments", - comments_value); + break; } } @@ -874,3 +872,46 @@ static void compose_line(FILE *out, const char *fmt, ...) printline(line); G_free(line); } + +static char *history_as_string(struct History *hist) +{ + int history_length = Rast_history_length(hist); + char *buffer = NULL; + if (history_length) { + size_t buffer_size = 0; + size_t total_length = 0; + for (int i = 0; i < history_length; i++) { + const char *line = Rast_history_line(hist, i); + size_t line_length = strlen(line); + + // +1 for the null character + size_t required_size = total_length + line_length + 1; + if (required_size > buffer_size) { + // This is heuristic for reallocation based on remaining + // iterations and current size which is a good estimate for the + // first iteration and possible overshoot later on reducing the + // number of reallocations. + buffer_size = required_size * (history_length - i); + buffer = (char *)G_realloc(buffer, buffer_size); + if (total_length == 0) + buffer[0] = '\0'; + } + if (line_length >= 1 && line[line_length - 1] == '\\') { + // Ending backslash is line continuation. + strncat(buffer, line, line_length - 1); + total_length += line_length - 1; + } + else { + strncat(buffer, line, line_length); + total_length += line_length; + if (i < history_length - 1) { + // Add newline to separate lines, but don't and newline at + // the end of last (or only) line. + strcat(buffer, "\n"); + ++total_length; + } + } + } + } + return buffer; +} diff --git a/raster/r.info/r.info.html b/raster/r.info/r.info.html index 8cc7b1a429f..b32f11ce6b1 100644 --- a/raster/r.info/r.info.html +++ b/raster/r.info/r.info.html @@ -69,7 +69,7 @@

    EXAMPLES

    | Comments: | | slope map elev = elev_ned10m | | zfactor = 1.00 format = degrees | - | min_slope = 0.000000 | + | min_slope = 0.000000 | | | +----------------------------------------------------------------------------+ @@ -78,7 +78,7 @@

    EXAMPLES

    subset of the available information by passing various flags to the module:

    -Output in shell script style, useful for eval (eval `r.info -g slope`): +Output in shell script style, useful for eval (eval `r.info -g slope`):

     r.info -g slope
    diff --git a/raster/r.info/testsuite/test_r_info.py b/raster/r.info/testsuite/test_r_info.py
    index 49f91a5723f..a793c5ab2ae 100644
    --- a/raster/r.info/testsuite/test_r_info.py
    +++ b/raster/r.info/testsuite/test_r_info.py
    @@ -47,7 +47,7 @@ def setUpClass(cls):
                 "source1": "",
                 "source2": "",
                 "description": "generated by r.mapcalc",
    -            "comments": ["1 * lakes_large"],
    +            "comments": "1 * lakes_large",
             }
     
         @classmethod
    @@ -136,6 +136,51 @@ def test_sflag_format_json(self):
             self._test_format_json_helper(module, expected_json_with_stats)
     
     
    +class TestComments(TestCase):
    +    """Check printing of comments"""
    +
    +    def test_comments_one_line(self):
    +        """Check that one line is text without any newlines"""
    +        module = SimpleModule("r.info", map="lakes", format="json")
    +        self.runModule(module)
    +        result = json.loads(module.outputs.stdout)
    +        self.assertFalse(result["comments"].endswith("\n"))
    +        self.assertEqual(result["comments"], "1 * lakes_large")
    +
    +    def test_comments_continued_line(self):
    +        """Check that continued lines are merged"""
    +        module = SimpleModule("r.info", map="elevation", format="json")
    +        self.runModule(module)
    +        result = json.loads(module.outputs.stdout)
    +        self.assertFalse(result["comments"].endswith("\n"))
    +        self.assertEqual(
    +            result["comments"],
    +            'r.proj input="ned03arcsec" location="northcarolina_latlong" '
    +            'mapset="helena" output="elev_ned10m" method="cubic" resolution=10',
    +        )
    +
    +    def test_comments_multiple_lines(self):
    +        """Check multiple lines are preserved"""
    +        module = SimpleModule("r.info", map="lsat7_2002_30", format="json")
    +        self.runModule(module)
    +        result = json.loads(module.outputs.stdout)
    +        self.assertFalse(result["comments"].endswith("\n"))
    +
    +        lines = result["comments"].splitlines()
    +        self.assertEqual(
    +            len(lines),
    +            31,
    +        )
    +        self.assertEqual(
    +            lines[0],
    +            'r.in.gdal input="p016r035_7t20020524_z17_nn30_nc_spm_wake.tif" output="lsat7_2002_30"',
    +        )
    +        self.assertEqual(
    +            lines[-1],
    +            'i.landsat.rgb "b=lsat7_2002_10" "g=lsat7_2002_20" "r=lsat7_2002_30"',
    +        )
    +
    +
     if __name__ == "__main__":
         from grass.gunittest.main import test
     
    diff --git a/raster/r.lake/r.lake.html b/raster/r.lake/r.lake.html
    index 4d12be899c6..d70d1297e6f 100644
    --- a/raster/r.lake/r.lake.html
    +++ b/raster/r.lake/r.lake.html
    @@ -1,6 +1,6 @@
     

    DESCRIPTION

    -

    The module fills a lake to a target water level from a given start point. The user +r.lake fills a lake to a target water level from a given start point. The user can think of it as r.grow with additional checks for elevation. The resulting raster map contains cells with values representing lake depth and NULL for all other @@ -66,8 +66,8 @@

    r.mapcalc equivalent - for GRASS hackers

    -The ${seedmap} variable is replaced by seed map names, ${dem} -with DEM map name, and ${wlevel} with target water level. To get +The ${seedmap} variable is replaced by seed map names, ${dem} +with DEM map name, and ${wlevel} with target water level. To get single water level, this code block is called with same level numerous times (in a loop) as the lake grows by single cells during single run. @@ -77,7 +77,7 @@

    KNOWN ISSUES

    • The entire map is loaded into RAM.
    • A completely negative seed map will not work! At least one cell must have - a value > 0. Output from r.lake -n cannot be used + a value > 0. Output from r.lake -n cannot be used as input in the next run.
    diff --git a/raster/r.li/r.li.cwed/r.li.cwed.html b/raster/r.li/r.li.cwed/r.li.cwed.html index 461eec731b6..88ac06f2ad7 100644 --- a/raster/r.li/r.li.cwed/r.li.cwed.html +++ b/raster/r.li/r.li.cwed/r.li.cwed.html @@ -26,8 +26,8 @@

    NOTES

    If the "moving window" method was selected in g.gui.rlisetup, then the output will be a raster map, otherwise an ASCII file will be generated in -the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ -(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux). +the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ +(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux).

    If the sample area contains only NULL values it is considered to @@ -68,10 +68,10 @@

    REFERENCES

    McGarigal, K., and B. J. Marks. 1995. FRAGSTATS: spatial pattern analysis program for quantifying landscape structure. USDA For. Serv. -Gen. Tech. Rep. PNW-351. (PDF) +Gen. Tech. Rep. PNW-351. (PDF)

    AUTHORS

    -Serena Pallecchi student of Computer Science University of Pisa (Italy).
    +Serena Pallecchi, student of Computer Science University of Pisa (Italy).
    Commission from Faunalia Pontedera (PI), Italy (www.faunalia.it)
    Markus Metz diff --git a/raster/r.li/r.li.daemon/r.li.daemon.html b/raster/r.li/r.li.daemon/r.li.daemon.html index 2d3cc5c4901..13f190df219 100644 --- a/raster/r.li/r.li.daemon/r.li.daemon.html +++ b/raster/r.li/r.li.daemon/r.li.daemon.html @@ -82,7 +82,7 @@

    REFERENCES

    McGarigal, K., and B. J. Marks. 1995. FRAGSTATS: spatial pattern analysis program for quantifying landscape structure. USDA For. Serv. -Gen. Tech. Rep. PNW-351. (PDF) +Gen. Tech. Rep. PNW-351. (PDF)

    AUTHORS

    diff --git a/raster/r.li/r.li.dominance/r.li.dominance.html b/raster/r.li/r.li.dominance/r.li.dominance.html index bccf6f01e04..15679874171 100644 --- a/raster/r.li/r.li.dominance/r.li.dominance.html +++ b/raster/r.li/r.li.dominance/r.li.dominance.html @@ -17,8 +17,8 @@

    NOTES

    If the "moving window" method was selected in g.gui.rlisetup, then the output will be a raster map, otherwise an ASCII file will be generated in -the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ -(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux). +the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ +(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux).

    If the input raster map contains only NULL values then @@ -68,10 +68,10 @@

    REFERENCES

    McGarigal, K., and B. J. Marks. 1995. FRAGSTATS: spatial pattern analysis program for quantifying landscape structure. USDA For. Serv. -Gen. Tech. Rep. PNW-351. (PDF) +Gen. Tech. Rep. PNW-351. (PDF)

    AUTHORS

    -Serena Pallecchi student of Computer Science University of Pisa (Italy).
    +Serena Pallecchi, student of Computer Science University of Pisa (Italy).
    Commission from Faunalia Pontedera (PI), Italy (www.faunalia.it)
    Markus Metz diff --git a/raster/r.li/r.li.edgedensity/r.li.edgedensity.html b/raster/r.li/r.li.edgedensity/r.li.edgedensity.html index e96f26e02b7..840a0cf9650 100644 --- a/raster/r.li/r.li.edgedensity/r.li.edgedensity.html +++ b/raster/r.li/r.li.edgedensity/r.li.edgedensity.html @@ -1,7 +1,7 @@

    DESCRIPTION

    -r.li.edgedensity calculates:
    -
    +r.li.edgedensity calculates: +
    • the density of all edges of patch type k r.li.edgedensity formula 1 @@ -29,8 +29,8 @@

      NOTES

      If the "moving window" method was selected in g.gui.rlisetup, then the output will be a raster map, otherwise an ASCII file will be generated in -the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ -(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux). +the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ +(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux).

      If the input raster map contains only NULL values then r.li.edgedensity @@ -89,10 +89,10 @@

      REFERENCES

      McGarigal, K., and B. J. Marks. 1995. FRAGSTATS: spatial pattern analysis program for quantifying landscape structure. USDA For. Serv. -Gen. Tech. Rep. PNW-351. (PDF) +Gen. Tech. Rep. PNW-351. (PDF)

      AUTHORS

      -Serena Pallecchi student of Computer Science University of Pisa (Italy).
      +Serena Pallecchi, student of Computer Science University of Pisa (Italy).
      Commission from Faunalia Pontedera (PI), Italy (www.faunalia.it)
      Markus Metz diff --git a/raster/r.li/r.li.html b/raster/r.li/r.li.html index a7685ca55ec..37e40d6cfc7 100644 --- a/raster/r.li/r.li.html +++ b/raster/r.li/r.li.html @@ -38,8 +38,8 @@

      NOTES

      1. run g.gui.rlisetup: create a configuration file selecting the parts of raster map to be analyzed. This file allows re-running - an analysis easily. It is stored on Windows in the directory C:\Users\userxy\AppData\Roaming\GRASS8\r.li\, on GNU/Linux in - $HOME/.grass8/r.li/.
      2. + an analysis easily. It is stored on Windows in the directory C:\Users\userxy\AppData\Roaming\GRASS8\r.li\, on GNU/Linux in + $HOME/.grass8/r.li/.
      3. run one or more of the r.li.[index] modules (e.g., r.li.patchdensity) to calculate the selected index @@ -85,7 +85,7 @@

        EXAMPLES

      4. CALCULATE PATCHDENSITY INDEX
          -
        1. set the region settings to the "geology" raster map: +
        2. set the region settings to the "geology" raster map:
           g.region raster=geology -p
           
          @@ -98,7 +98,7 @@

          EXAMPLES

      -The resulting patch density is stored in "patchdens" raster map. +The resulting patch density is stored in "patchdens" raster map. You can verify the result for example with contour lines:
      @@ -120,7 +120,7 @@ 

      SEE ALSO

      GUI tools:
        -
      • g.gui.rlisetup: Configuration editor for the r.li.* module where * is name of the index
      • +
      • g.gui.rlisetup: Configuration editor for the r.li.* module where * is name of the index
      Patch indices: @@ -190,7 +190,7 @@

      REFERENCES

      McGarigal, K., and B. J. Marks. 1995. FRAGSTATS: spatial pattern analysis program for quantifying landscape structure. USDA For. Serv. Gen. Tech. Rep. PNW-351 - (PDF).
    • + (PDF).
    • Baker, W.L. and Y. Cai. 1992. The r.le programs for multiscale analysis of diff --git a/raster/r.li/r.li.mpa/r.li.mpa.html b/raster/r.li/r.li.mpa/r.li.mpa.html index d54eabcb925..1acc2908608 100644 --- a/raster/r.li/r.li.mpa/r.li.mpa.html +++ b/raster/r.li/r.li.mpa/r.li.mpa.html @@ -18,8 +18,8 @@

      NOTES

      If the "moving window" method was selected in g.gui.rlisetup, then the output will be a raster map, otherwise an ASCII file will be generated in -the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ -(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux). +the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ +(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux).

      If the input raster map contains only NULL values then r.li.mpa considers to @@ -76,10 +76,10 @@

      REFERENCES

      McGarigal, K., and B. J. Marks. 1995. FRAGSTATS: spatial pattern analysis program for quantifying landscape structure. USDA For. Serv. -Gen. Tech. Rep. PNW-351. (PDF) +Gen. Tech. Rep. PNW-351. (PDF)

      AUTHORS

      -Serena Pallecchi student of Computer Science University of Pisa (Italy).
      +Serena Pallecchi, student of Computer Science University of Pisa (Italy).
      Commission from Faunalia Pontedera (PI), Italy (www.faunalia.it)
      Markus Metz diff --git a/raster/r.li/r.li.mps/r.li.mps.html b/raster/r.li/r.li.mps/r.li.mps.html index e52f98ede78..e7548a9db76 100644 --- a/raster/r.li/r.li.mps/r.li.mps.html +++ b/raster/r.li/r.li.mps/r.li.mps.html @@ -28,8 +28,8 @@

      NOTES

      If the "moving window" method was selected in g.gui.rlisetup, then the output will be a raster map, otherwise an ASCII file will be generated in -the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ -(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux). +the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ +(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux).

      If the sample area contains only NULL value cells, r.li.mps returns 0 (zero).
      @@ -78,7 +78,7 @@

      REFERENCES

      McGarigal, K., and B. J. Marks. 1995. FRAGSTATS: spatial pattern analysis program for quantifying landscape structure. USDA For. Serv. -Gen. Tech. Rep. PNW-351. (PDF) +Gen. Tech. Rep. PNW-351. (PDF)

      AUTHORS

      diff --git a/raster/r.li/r.li.padcv/r.li.padcv.html b/raster/r.li/r.li.padcv/r.li.padcv.html index bbc688975f2..23aed65eedd 100644 --- a/raster/r.li/r.li.padcv/r.li.padcv.html +++ b/raster/r.li/r.li.padcv/r.li.padcv.html @@ -19,8 +19,8 @@

      NOTES

      If the "moving window" method was selected in g.gui.rlisetup, then the output will be a raster map, otherwise an ASCII file will be generated in -the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ -(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux). +the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ +(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux).

      The result is NULL if the sample area contains only NULL values. @@ -68,7 +68,7 @@

      REFERENCES

      McGarigal, K., and B. J. Marks. 1995. FRAGSTATS: spatial pattern analysis program for quantifying landscape structure. USDA For. Serv. -Gen. Tech. Rep. PNW-351. (PDF) +Gen. Tech. Rep. PNW-351. (PDF)

      AUTHORS

      diff --git a/raster/r.li/r.li.padrange/r.li.padrange.html b/raster/r.li/r.li.padrange/r.li.padrange.html index 36e0f78f61c..6502c137f1e 100644 --- a/raster/r.li/r.li.padrange/r.li.padrange.html +++ b/raster/r.li/r.li.padrange/r.li.padrange.html @@ -20,8 +20,8 @@

      NOTES

      If the "moving window" method was selected in g.gui.rlisetup, then the output will be a raster map, otherwise an ASCII file will be generated in -the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ -(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux). +the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ +(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux).

      If the sample area contains only NULL values then r.li.padrange returns NULL.
      @@ -70,7 +70,7 @@

      REFERENCES

      McGarigal, K., and B. J. Marks. 1995. FRAGSTATS: spatial pattern analysis program for quantifying landscape structure. USDA For. Serv. -Gen. Tech. Rep. PNW-351. (PDF) +Gen. Tech. Rep. PNW-351. (PDF)

      AUTHORS

      diff --git a/raster/r.li/r.li.padsd/r.li.padsd.html b/raster/r.li/r.li.padsd/r.li.padsd.html index 344ce04d372..379049c9506 100644 --- a/raster/r.li/r.li.padsd/r.li.padsd.html +++ b/raster/r.li/r.li.padsd/r.li.padsd.html @@ -21,8 +21,8 @@

      NOTES

      If the "moving window" method was selected in g.gui.rlisetup, then the output will be a raster map, otherwise an ASCII file will be generated in -the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ -(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux). +the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ +(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux).

      The result is NULL if the sample area contains only NULL values. @@ -70,7 +70,7 @@

      REFERENCES

      McGarigal, K., and B. J. Marks. 1995. FRAGSTATS: spatial pattern analysis program for quantifying landscape structure. USDA For. Serv. -Gen. Tech. Rep. PNW-351. (PDF) +Gen. Tech. Rep. PNW-351. (PDF)

      AUTHORS

      diff --git a/raster/r.li/r.li.patchdensity/r.li.patchdensity.html b/raster/r.li/r.li.patchdensity/r.li.patchdensity.html index f22a27caae7..168a45d9772 100644 --- a/raster/r.li/r.li.patchdensity/r.li.patchdensity.html +++ b/raster/r.li/r.li.patchdensity/r.li.patchdensity.html @@ -23,8 +23,8 @@

      NOTES

      If the "moving window" method was selected in g.gui.rlisetup, then the output will be a raster map, otherwise an ASCII file will be generated in -the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ -(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux). +the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ +(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux).

      A sample area of only NULL values is considered to have zero patches, that is, the result is always ≥ 0. @@ -81,7 +81,7 @@

      REFERENCES

      McGarigal, K., and B. J. Marks. 1995. FRAGSTATS: spatial pattern analysis program for quantifying landscape structure. USDA For. Serv. -Gen. Tech. Rep. PNW-351. (PDF) +Gen. Tech. Rep. PNW-351. (PDF)

      AUTHORS

      diff --git a/raster/r.li/r.li.patchnum/r.li.patchnum.html b/raster/r.li/r.li.patchnum/r.li.patchnum.html index b0d282a4dee..743f965b5b8 100644 --- a/raster/r.li/r.li.patchnum/r.li.patchnum.html +++ b/raster/r.li/r.li.patchnum/r.li.patchnum.html @@ -14,8 +14,8 @@

      NOTES

      If the "moving window" method was selected in g.gui.rlisetup, then the output will be a raster map, otherwise an ASCII file will be generated in -the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ -(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux). +the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ +(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux).

      If the sample area contains only NULL values then it is considered to have zero patches.
      @@ -64,7 +64,7 @@

      REFERENCES

      McGarigal, K., and B. J. Marks. 1995. FRAGSTATS: spatial pattern analysis program for quantifying landscape structure. USDA For. Serv. -Gen. Tech. Rep. PNW-351. (PDF) +Gen. Tech. Rep. PNW-351. (PDF)

      AUTHORS

      diff --git a/raster/r.li/r.li.pielou/r.li.pielou.html b/raster/r.li/r.li.pielou/r.li.pielou.html index 3af1aee78ea..f8f9e2730fb 100644 --- a/raster/r.li/r.li.pielou/r.li.pielou.html +++ b/raster/r.li/r.li.pielou/r.li.pielou.html @@ -16,8 +16,8 @@

      NOTES

      If the "moving window" method was selected in g.gui.rlisetup, then the output will be a raster map, otherwise an ASCII file will be generated in -the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ -(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux). +the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ +(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux).

      If the input raster contains NULL value cells, r.li.pielou @@ -67,9 +67,9 @@

      REFERENCES

      McGarigal, K., and B. J. Marks. 1995. FRAGSTATS: spatial pattern analysis program for quantifying landscape structure. USDA For. Serv. -Gen. Tech. Rep. PNW-351. (PDF) +Gen. Tech. Rep. PNW-351. (PDF)

      AUTHORS

      Luca Delucchi and Duccio Rocchini, Fondazione E. Mach (Italy), based on the r.li.shannon code -developed by Serena Pallecchi student of Computer Science University of Pisa (Italy) +developed by Serena Pallecchi, student of Computer Science University of Pisa (Italy) diff --git a/raster/r.li/r.li.renyi/r.li.renyi.html b/raster/r.li/r.li.renyi/r.li.renyi.html index 62f3d0d5fae..3b866356765 100644 --- a/raster/r.li/r.li.renyi/r.li.renyi.html +++ b/raster/r.li/r.li.renyi/r.li.renyi.html @@ -19,8 +19,8 @@

      NOTES

      If the "moving window" method was selected in g.gui.rlisetup, then the output will be a raster map, otherwise an ASCII file will be generated in -the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ -(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux). +the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ +(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux).

      If the input raster map contains only NULL values then r.li.renyi @@ -70,10 +70,10 @@

      REFERENCES

      McGarigal, K., and B. J. Marks. 1995. FRAGSTATS: spatial pattern analysis program for quantifying landscape structure. USDA For. Serv. -Gen. Tech. Rep. PNW-351. (PDF) +Gen. Tech. Rep. PNW-351. (PDF)

      AUTHORS

      Luca Delucchi and Duccio Rocchini, Fondazione E. Mach (Italy), based on -the r.li.shannon code developed by Serena Pallecchi student of +the r.li.shannon code developed by Serena Pallecchi, student of Computer Science University of Pisa (Italy). diff --git a/raster/r.li/r.li.richness/r.li.richness.html b/raster/r.li/r.li.richness/r.li.richness.html index 33c10575607..32bc7f99b0c 100644 --- a/raster/r.li/r.li.richness/r.li.richness.html +++ b/raster/r.li/r.li.richness/r.li.richness.html @@ -16,8 +16,8 @@

      NOTES

      If the "moving window" method was selected in g.gui.rlisetup, then the output will be a raster map, otherwise an ASCII file will be generated in -the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ -(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux). +the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ +(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux).

      If the input raster map contains only NULL values then r.li.richness @@ -67,9 +67,9 @@

      REFERENCES

      McGarigal, K., and B. J. Marks. 1995. FRAGSTATS: spatial pattern analysis program for quantifying landscape structure. USDA For. Serv. -Gen. Tech. Rep. PNW-351. (PDF) +Gen. Tech. Rep. PNW-351. (PDF)

      AUTHORS

      -Serena Pallecchi student of Computer Science University of Pisa (Italy).
      +Serena Pallecchi, student of Computer Science University of Pisa (Italy).
      Commission from Faunalia Pontedera (PI), Italy (www.faunalia.it) diff --git a/raster/r.li/r.li.shannon/r.li.shannon.html b/raster/r.li/r.li.shannon/r.li.shannon.html index 684c97b4540..76ef70ebc25 100644 --- a/raster/r.li/r.li.shannon/r.li.shannon.html +++ b/raster/r.li/r.li.shannon/r.li.shannon.html @@ -17,8 +17,8 @@

      NOTES

      If the "moving window" method was selected in g.gui.rlisetup, then the output will be a raster map, otherwise an ASCII file will be generated in -the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ -(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux). +the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ +(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux).

      If the input raster map contains only NULL values then r.li.shannon @@ -68,9 +68,9 @@

      REFERENCES

      McGarigal, K., and B. J. Marks. 1995. FRAGSTATS: spatial pattern analysis program for quantifying landscape structure. USDA For. Serv. -Gen. Tech. Rep. PNW-351. (PDF) +Gen. Tech. Rep. PNW-351. (PDF)

      AUTHORS

      -Serena Pallecchi student of Computer Science University of Pisa (Italy).
      +Serena Pallecchi, student of Computer Science University of Pisa (Italy).
      Commission from Faunalia Pontedera (PI), Italy (www.faunalia.it) diff --git a/raster/r.li/r.li.shape/r.li.shape.html b/raster/r.li/r.li.shape/r.li.shape.html index e0d8f6e9ef7..5b7d7219588 100644 --- a/raster/r.li/r.li.shape/r.li.shape.html +++ b/raster/r.li/r.li.shape/r.li.shape.html @@ -18,8 +18,8 @@

      NOTES

      If the "moving window" method was selected in g.gui.rlisetup, then the output will be a raster map, otherwise an ASCII file will be generated in -the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ -(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux). +the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ +(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux).

      If the input raster map contains only NULL values then r.li.shape @@ -69,7 +69,7 @@

      REFERENCES

      McGarigal, K., and B. J. Marks. 1995. FRAGSTATS: spatial pattern analysis program for quantifying landscape structure. USDA For. Serv. -Gen. Tech. Rep. PNW-351. (PDF) +Gen. Tech. Rep. PNW-351. (PDF)

      AUTHORS

      diff --git a/raster/r.li/r.li.simpson/r.li.simpson.html b/raster/r.li/r.li.simpson/r.li.simpson.html index 82f37920d49..fb33b7cad98 100644 --- a/raster/r.li/r.li.simpson/r.li.simpson.html +++ b/raster/r.li/r.li.simpson/r.li.simpson.html @@ -17,8 +17,8 @@

      NOTES

      file/map parameters. If the "moving window" method was selected in g.gui.rlisetup, then the output will be a raster map, otherwise an ASCII file will be generated in -the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ -(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux). +the folder C:\Users\userxy\AppData\Roaming\GRASS8\r.li\output\ +(MS-Windows) or $HOME/.grass8/r.li/output/ (GNU/Linux).

      If the input raster map contains only NULL values then r.li.simpson @@ -68,9 +68,9 @@

      REFERENCES

      McGarigal, K., and B. J. Marks. 1995. FRAGSTATS: spatial pattern analysis program for quantifying landscape structure. USDA For. Serv. -Gen. Tech. Rep. PNW-351. (PDF) +Gen. Tech. Rep. PNW-351. (PDF)

      AUTHORS

      -Serena Pallecchi student of Computer Science University of Pisa (Italy).
      +Serena Pallecchi, student of Computer Science University of Pisa (Italy).
      Commission from Faunalia Pontedera (PI), Italy (www.faunalia.it) diff --git a/raster/r.mapcalc/expression.c b/raster/r.mapcalc/expression.c index 72581c921f7..24aebb60e63 100644 --- a/raster/r.mapcalc/expression.c +++ b/raster/r.mapcalc/expression.c @@ -403,6 +403,7 @@ static char *format_function(const expression *e, int prec) G_free(args[i]); } strcat(result, ")"); + G_free(args); return result; } diff --git a/raster/r.mapcalc/r.mapcalc.html b/raster/r.mapcalc/r.mapcalc.html index 6c88642d17f..0f41f42b5db 100644 --- a/raster/r.mapcalc/r.mapcalc.html +++ b/raster/r.mapcalc/r.mapcalc.html @@ -414,8 +414,8 @@

      Data types and their precision

      Note that the value counter wraps around when the value overflows its range. -E.g., if your expression is a = int(2147483648), you will get NULL -value. For expression a = int(2147483649), you will reach the lowest +E.g., if your expression is a = int(2147483648), you will get NULL +value. For expression a = int(2147483649), you will reach the lowest value possible instead, i.e. -2147483647.

      Floating point values in the expression

      @@ -557,8 +557,8 @@

      Usage from command line

       'result = elevation * 2'
       
      -Without the quotes, the *, which has special meaning to the UNIX shell, -would be altered and r.mapcalc would see something other than the *. +Without the quotes, the *, which has special meaning to the UNIX shell, +would be altered and r.mapcalc would see something other than the *.

      Multiple computations

      @@ -583,7 +583,7 @@

      Backwards compatibility

      For the backwards compatibility with GRASS 6, -if no options are given, it manufactures file=- (which reads from +if no options are given, it manufactures file=- (which reads from stdin), so you can continue to use e.g.:
       r.mapcalc < file
      @@ -594,7 +594,7 @@ 

      Backwards compatibility

      foo = 1 EOF
      -But unless you need compatibility with previous GRASS GIS versions, use file= +But unless you need compatibility with previous GRASS GIS versions, use file= explicitly, as stated above.

      When the map name contains uppercase letter(s) or a dot which are not @@ -619,15 +619,15 @@

      Interactive input in command line

      the map title will contain only some of it, but most (if not all) of the formula will be placed into the history file for the result map. -

      Raster MASK handling

      +

      Raster mask handling

      -r.mapcalc follows the common GRASS behavior of raster MASK handling, -so the MASK is only applied when reading an existing GRASS raster map. +r.mapcalc follows the common GRASS behavior of raster mask handling, +so the mask is only applied when reading an existing GRASS raster map. This implies that, for example, the command:

       r.mapcalc "elevation_exaggerated = elevation * 3"
       
      -create a map respecting the masked pixels if MASK is active. +create a map with NULL cells for the masked-out cells if raster mask is active.

      However, when creating a map which is not based on any map, @@ -636,24 +636,26 @@

      Raster MASK handling

      r.mapcalc "base_height = 200.0" the created raster map is limited only by a computation region -but it is not affected by an active MASK. -This is expected because, as mentioned above, MASK is only applied when reading, -not when writing a raster map. -

      -If also in this case the MASK should be applied, an if() statement including the -MASK should be used, e.g.: +but it is not affected by an active raster mask. +This is expected because, as mentioned above, the mask is only applied +when reading, not when writing a raster map. +If the raster mask should be applied in this case, an if() +function including the mask raster should be used, e.g. +(assuming the mask is called MASK):

       r.mapcalc "base_height = if(MASK, 200.0, null())"
       
      -When testing MASK related expressions keep in mind that when MASK is active +

      +When testing expressions related to mask handling keep in mind that +when the raster mask is active, you don't see data in masked areas even if they are not NULL. See r.mask for details.

      eval function

      If the output of the computation should be only one map but the expression is so complex that it is better to split it -to several expressions, the eval function can be used: +to several expressions, the eval function can be used:
       r.mapcalc << EOF
       eval(elev_200 = elevation - 200, \
      @@ -662,23 +664,23 @@ 

      eval function

      elevation_result = (0.5 * elev_200) + 0.8 * elev_p EOF
      -This example uses unix-like << EOF syntax to provide +This example uses unix-like << EOF syntax to provide input to r.mapcalc.

      Note that the temporary variables (maps) are not created and thus it does not matter whether they exists or not. -In the example above, if map elev_200 exists it will not be +In the example above, if map elev_200 exists it will not be overwritten and no error will be generated. -The reason is that the name elev_200 now denotes the temporary +The reason is that the name elev_200 now denotes the temporary variable (map) and not the existing map. -The following parts of the expression will use the temporary elev_200 -and the existing elev_200 will be left intact and will not be used. +The following parts of the expression will use the temporary elev_200 +and the existing elev_200 will be left intact and will not be used. If a user want to use the existing map, the name of the temporary variable (map) must be changed.

      Using the same map for input and output results

      A map cannot be used both as an input and as an output as in -this invalid expression oldmap = oldmap + 1, instead +this invalid expression oldmap = oldmap + 1, instead a subsequent rename using g.rename is needed when the same name is desired: @@ -797,7 +799,7 @@

      KNOWN ISSUES

      Any maps generated by a r.mapcalc command only exist after the entire command has completed. All maps are generated concurrently, row-by-row (i.e. there is an implicit "for row in rows {...}" around the entire expression). -Thus the #, @, and [ ] operators cannot be used on a map +Thus the #, @, and [ ] operators cannot be used on a map generated within same r.mapcalc command run. Consequently, the following (strikethrough code) does not work: @@ -806,11 +808,11 @@

      KNOWN ISSUES

      othermap = newmap[-1, 0] / newmap[1, 0] -

      Continuation lines must end with a \ and have no trailing +

      Continuation lines must end with a \ and have no trailing white space (blanks or tabs). If the user does leave white space at the end of continuation lines, the error messages produced by r.mapcalc will be meaningless and the equation will not work as the user intended. -This is particularly important for the eval() function. +This is particularly important for the eval() function.

      Currently, there is no comment mechanism in r.mapcalc. Perhaps adding a capability that would cause the entire line to be @@ -820,7 +822,7 @@

      KNOWN ISSUES

      of simply a blank line. This would make separation of multiple scripts separable by white space.

      r.mapcalc does not print a warning in case of operations on -NULL cells. It is left to the user to utilize the isnull() function. +NULL cells. It is left to the user to utilize the isnull() function.

      REFERENCES

      diff --git a/raster/r.mapcalc/r3.mapcalc.html b/raster/r.mapcalc/r3.mapcalc.html index 8990adefb68..46bcbcff3ae 100644 --- a/raster/r.mapcalc/r3.mapcalc.html +++ b/raster/r.mapcalc/r3.mapcalc.html @@ -397,8 +397,8 @@

      Usage from command line

       'result = volume * 2'
       
      -Without the quotes, the *, which has special meaning to the UNIX shell, -would be altered and r3.mapcalc would see something other than the *. +Without the quotes, the *, which has special meaning to the UNIX shell, +would be altered and r3.mapcalc would see something other than the *.

      Multiple computations

      @@ -409,7 +409,7 @@

      Backwards compatibility

      For the backwards compatibility with GRASS 6, -if no options are given, it manufactures file=- (which reads from +if no options are given, it manufactures file=- (which reads from stdin), so you can continue to use e.g.:
       r3.mapcalc < file
      @@ -420,7 +420,7 @@ 

      Backwards compatibility

      foo = 1 EOF
      -But unless you need compatibility with previous GRASS GIS versions, use file= +But unless you need compatibility with previous GRASS GIS versions, use file= explicitly, as stated above.

      When the map name contains uppercase letter(s) or a dot which are not @@ -449,15 +449,15 @@

      Interactive input in command line

      existing map layers. Users should therefore take care to assign program outputs raster map names that do not yet exist in their current mapsets. -

      3D GRID MASK handling

      +

      3D GRID mask handling

      -r3.mapcalc follows the common GRASS behavior of raster MASK handling, -so the MASK is only applied when reading an existing GRASS raster map. +r3.mapcalc follows the common GRASS behavior of raster mask handling, +so the mask is only applied when reading an existing GRASS raster map. This implies that, for example, the command:

       r3.mapcalc "volume_amplified = volume * 3"
       
      -create a map respecting the masked pixels if MASK is active. +create a map with NULL cells for the masked-out cells if raster mask is active.

      However, when creating a map which is not based on any map, @@ -466,19 +466,21 @@

      3D GRID MASK handling

      r3.mapcalc "volume_const = 200.0" the created raster map is limited only by a computation region -but it is not affected by an active MASK. -This is expected because, as mentioned above, MASK is only applied when reading, -not when writing a raster map. -

      -If also in this case the MASK should be applied, an if() statement including the -MASK should be used, e.g.: +but it is not affected by an active raster mask. +This is expected because, as mentioned above, the mask is only applied +when reading, not when writing a raster map. +If the raster mask should be applied in this case, an if() +function including the mask raster should be used, e.g. +(assuming the mask is called MASK):

       r3.mapcalc "volume_const = if(MASK, 200.0, null())"
       
      -When testing MASK related expressions keep in mind that when MASK is active +

      +When testing expressions related to mask handling keep in mind that +when the raster mask is active, you don't see data in masked areas even if they are not NULL. -See r.mask for details. +See r3.mask for details.

      Random number generator initialization

      The pseudo-random number generator used by the rand() function can @@ -570,7 +572,7 @@

      KNOWN ISSUES

      Any maps generated by a r3.mapcalc command only exist after the entire command has completed. All maps are generated concurrently, row-by-row (i.e. there is an implicit "for row in rows {...}" around the entire expression). -Thus the #, @, and [ ] operators cannot be used on a map +Thus the #, @, and [ ] operators cannot be used on a map generated within same r3.mapcalc command run.

      @@ -578,11 +580,11 @@ 

      KNOWN ISSUES

      othermap = newmap[-1, 0] / newmap[1, 0]
      -

      Continuation lines must end with a \ and have no trailing +

      Continuation lines must end with a \ and have no trailing white space (blanks or tabs). If the user does leave white space at the end of continuation lines, the error messages produced by r3.mapcalc will be meaningless and the equation will not work as the user intended. -This is particularly important for the eval() function. +This is particularly important for the eval() function.

      Currently, there is no comment mechanism in r3.mapcalc. Perhaps adding a capability that would cause the entire line to be @@ -592,7 +594,7 @@

      KNOWN ISSUES

      of simply a blank line. This would make separation of multiple scripts separable by white space.

      r3.mapcalc does not print a warning in case of operations on -NULL cells. It is left to the user to utilize the isnull() function. +NULL cells. It is left to the user to utilize the isnull() function.

      REFERENCES

      diff --git a/raster/r.null/r.null.html b/raster/r.null/r.null.html index 4d92b027d65..58293e58822 100644 --- a/raster/r.null/r.null.html +++ b/raster/r.null/r.null.html @@ -41,7 +41,7 @@

      NULL data compression

      By default no data files (i.e., NULL files) are not compressed unless a specific environment variable is set. The NULL file compression must be -explicitly turned on with export GRASS_COMPRESS_NULLS=1.
      +explicitly turned on with export GRASS_COMPRESS_NULLS=1.
      Warning: such raster maps can then only be opened with GRASS GIS 7.2.0 or later. NULL file compression can be managed with r.null -z. diff --git a/raster/r.object.geometry/r.object.geometry.html b/raster/r.object.geometry/r.object.geometry.html index 527db3cfffd..ca3524ebffc 100644 --- a/raster/r.object.geometry/r.object.geometry.html +++ b/raster/r.object.geometry/r.object.geometry.html @@ -23,10 +23,10 @@

      DESCRIPTION

    • area
    • perimeter
    • compact_square (compactness compared to a square: - compact_square = 4 * sqrt(area) / perimeter)
    • + compact_square = 4 * sqrt(area) / perimeter)
    • compact_circle (compactness compared to a circle: - compact_circle = perimeter / ( 2 * sqrt(PI * area) ))
    • -
    • fractal dimension ( fd = 2 * ( log(perimeter) / log(area + 0.001) ) )
    • + compact_circle = perimeter / ( 2 * sqrt(PI * area) )) +
    • fractal dimension ( fd = 2 * ( log(perimeter) / log(area + 0.001) ) )
    • mean x coordinate of object (in map units)
    • mean y coordinate of object (in map units)
    diff --git a/raster/r.out.bin/r.out.bin.html b/raster/r.out.bin/r.out.bin.html index 0041286cf25..48dcc20e334 100644 --- a/raster/r.out.bin/r.out.bin.html +++ b/raster/r.out.bin/r.out.bin.html @@ -9,7 +9,7 @@

    DESCRIPTION

    NOTES

    With the -h flag, data can be directly used by -GMT as Grid Format 1 (float) or +GMT as Grid Format 1 (float) or 2 (short). For example:
    diff --git a/raster/r.out.gdal/r.out.gdal.html b/raster/r.out.gdal/r.out.gdal.html
    index 28ddeb9c7d2..a7a0c04332e 100644
    --- a/raster/r.out.gdal/r.out.gdal.html
    +++ b/raster/r.out.gdal/r.out.gdal.html
    @@ -9,7 +9,7 @@ 

    DESCRIPTION

    (createopt="TFW=YES,COMPRESS=DEFLATE").

    For possible createopt and metaopt parameters please consult the individual -supported formats +supported formats pages on the GDAL website. The createopt parameter may be used to create TFW or World files ("TFW=YES","WORLDFILE=ON"). @@ -18,14 +18,14 @@

    DESCRIPTION

    (created imagery groups with the i.group module)

    As with most GRASS raster modules, the current region extents and region -resolution are used, and a MASK is respected if present. +resolution are used, and a raster mask is respected if present. Use g.region's "align=", or "raster=" options if you need to realign the region settings to match the original map's before export.

    SUPPORTED RASTER FORMATS

    -The set of supported +The set of supported raster formats written by r.out.gdal depends on the local GDAL installation, printed with the -l flag. Available may be (incomplete list):

    @@ -110,7 +110,7 @@

    Ranges of GDAL data types

    Adding overviews to speed up map display in other software

    -Adding overviews with gdaladdo +Adding overviews with gdaladdo after exporting can speed up display. The overviews are created internally within the exported file. The amount of levels (power-of-two factors) are controlled with the overviews parameter. The higher the overview level @@ -151,13 +151,13 @@

    Improving GeoTIFF compatibility

    Here are some things to try:
      -
    • Create a World file with createopt="TFW=YES".
    • +
    • Create a World file with createopt="TFW=YES".
    • Do not use GeoTIFF internal compression. Other GIS software often supports only a subset of the available compression methods with the supported methods differing between GIS software packages. Unfortunately this means the output image can be rather huge, but the file can be -compressed with software like zip, gnuzip, or bzip2.
    • +compressed with software like zip, gnuzip, or bzip2.
    • Skip exporting the color table. Color tables are not always properly rendered, particularly for type UInt16, and the GeoTIFF file can appear @@ -165,8 +165,8 @@

      Improving GeoTIFF compatibility

      a method to reset the color table and assign a new color table (sometimes called symbology).
    • -
    • Keep metadata simple with createopt="PROFILE=GeoTIFF" or -createopt="PROFILE=BASELINE". With BASELINE no GDAL or GeoTIFF +
    • Keep metadata simple with createopt="PROFILE=GeoTIFF" or +createopt="PROFILE=BASELINE". With BASELINE no GDAL or GeoTIFF tags will be written and a World file is required (createopt="TFW=YES").
    @@ -185,7 +185,7 @@

    EXAMPLES

    Export the integer raster basin_50K map to GeoTIFF format

    -See also GeoTIFF format description (GDAL): +See also GeoTIFF format description (GDAL):
     g.region raster=basin_50K -p
    @@ -194,7 +194,7 @@ 

    Export the integer raster basin_50K map to GeoTIFF format

    Export the integer raster landclass96 map to Cloud Optimized GeoTIFF format

    -See also Cloud Optimized GeoTIFF (COG) format description (GDAL): +See also Cloud Optimized GeoTIFF (COG) format description (GDAL):
     g.region -p raster=landclass96
    @@ -267,7 +267,8 @@ 

    Export RGB with alpha channel that encodes NULL cells

    equal to this nodata value will be interpreted as nodata. Using an additional alpha channel means that all pixels with an alpha value of 0 are transparent. The alpha channel thus represents per-pixel encoding of -nodata, just like the GRASS MASK (null file). That means when using an alpha +nodata, just like the GRASS raster mask or per-raster null file. +That means when using an alpha channel, you do not need to "free up" any particular value, but you can use any value you like to replace NULL cells, as long as the value can be represented by the Byte data type. It does not matter if that value is @@ -298,7 +299,7 @@

    Export RGB with alpha channel that encodes NULL cells

    # create group for export i.group group=out_rgba input=out_r,out_g,out_b,out_a -# remove any MASK because this works only if there are +# remove any mask because this works only if there are # no NULL cells in the bands to be exported r.mask -r @@ -313,7 +314,7 @@

    Export RGB with alpha channel that encodes NULL cells

    Export the floating point raster elevation map to ERDAS/IMG format

    -See also Erdas Imagine .img format description (GDAL): +See also Erdas Imagine .img format description (GDAL):
     g.region raster=elevation -p
    @@ -372,8 +373,8 @@ 

    REFERENCES

    SEE ALSO

    -GDAL supported raster formats and -GDAL supported vector formats +GDAL supported raster formats and +GDAL supported vector formats

    diff --git a/raster/r.out.mat/r.out.mat.html b/raster/r.out.mat/r.out.mat.html index 82bee6f3a87..32865313ca2 100644 --- a/raster/r.out.mat/r.out.mat.html +++ b/raster/r.out.mat/r.out.mat.html @@ -31,13 +31,13 @@

    NOTES

    copied between different system architectures without binary translation.

    -As there is no IEEE value for NaN for integer maps, GRASS's null +As there is no IEEE value for NaN for integer maps, GRASS's null value is used to represent it within these maps. You'll have to do something like this to clean them once the map is loaded into Matlab:
        map_data(find(map_data < -1e9)) = NaN;
    Null values in maps containing either floating point or double-precision -floating point data should translate into NaN values as expected. +floating point data should translate into NaN values as expected.

    diff --git a/raster/r.out.mpeg/r.out.mpeg.html b/raster/r.out.mpeg/r.out.mpeg.html index eb282acbbd9..aa8db1b3a4c 100644 --- a/raster/r.out.mpeg/r.out.mpeg.html +++ b/raster/r.out.mpeg/r.out.mpeg.html @@ -74,9 +74,9 @@

    NOTES

    Lawrence A. Rowe, Kevin Gong, Ketan Patel, and Dan Wallach Computer Science Division-EECS, Univ. of Calif. at Berkeley

    Available from Berkeley: -http://biowiki.org/BerkeleyMpegEncoder +http://biowiki.org/BerkeleyMpegEncoder (Wayback machine)
    or as part of the netpbm package (ppmtompeg): -http://netpbm.sourceforge.net +https://netpbm.sourceforge.net -Any MASK in place will be respected when writing the new raster map layer, -including any edits to the MASKed areas. + +A raster mask in place will be respected both when reading the original raster +map layer and when writing the new raster map layer, +so any edits to the masked areas will be ignored.

    Geographic Region Concerns

    diff --git a/scripts/d.rast.leg/d.rast.leg.html b/scripts/d.rast.leg/d.rast.leg.html index 49714803c2a..f92bdd75f2d 100644 --- a/scripts/d.rast.leg/d.rast.leg.html +++ b/scripts/d.rast.leg/d.rast.leg.html @@ -20,7 +20,7 @@

    NOTES

    which the legend is generated. This is useful to visualize (time) series of raster maps with a common static legend instead of the default dynamic legend.

    To remove all frames when clearing the display, use -d.erase -f. +d.erase -f.

    EXAMPLE

    diff --git a/scripts/d.shade/d.shade.html b/scripts/d.shade/d.shade.html index 34286919468..ee081872a5f 100644 --- a/scripts/d.shade/d.shade.html +++ b/scripts/d.shade/d.shade.html @@ -24,8 +24,8 @@

    NOTES

    EXAMPLES

    -In this example, the aspect map in the North Carolina sample -dataset is used to hillshade the elevation map: +In this example, the aspect map in the North Carolina sample +dataset is used to hillshade the elevation map:
     g.region raster=aspect -p
    @@ -64,7 +64,7 @@ 

    EXAMPLES

    Interesting visualizations can be created using different color tables for -elevation raster map, for example using haxby color table. +elevation raster map, for example using haxby color table.

    SEE ALSO

    diff --git a/scripts/d.to.rast/d.to.rast.html b/scripts/d.to.rast/d.to.rast.html index c0330e066b5..4efa1bb4f83 100644 --- a/scripts/d.to.rast/d.to.rast.html +++ b/scripts/d.to.rast/d.to.rast.html @@ -46,4 +46,4 @@

    SEE ALSO

    AUTHOR

    -Anna Petrasova, NCSU OSGeoREL +Anna Petrasova, NCSU GeoForAll Lab diff --git a/scripts/d.what.rast/d.what.rast.html b/scripts/d.what.rast/d.what.rast.html index ac5d314d11f..949835f8372 100644 --- a/scripts/d.what.rast/d.what.rast.html +++ b/scripts/d.what.rast/d.what.rast.html @@ -1,6 +1,6 @@

    DESCRIPTION

    -d.what.rast d.what.rast outputs the category value(s) +d.what.rast outputs the category value(s) associated with user-specified location(s) in user-specified raster map layer(s). The active monitor can be selected with d.mon. @@ -18,4 +18,4 @@

    SEE ALSO

    AUTHOR

    -Anna Petrasova, NCSU OSGeoREL +Anna Petrasova, NCSU GeoForAll Lab diff --git a/scripts/d.what.vect/d.what.vect.html b/scripts/d.what.vect/d.what.vect.html index 4b6303e1ca0..d891b350927 100644 --- a/scripts/d.what.vect/d.what.vect.html +++ b/scripts/d.what.vect/d.what.vect.html @@ -1,6 +1,6 @@

    DESCRIPTION

    -d.what.vect d.what.vect outputs the category value(s) +d.what.vect outputs the category value(s) associated with user-specified location(s) in user-specified vector map layer(s). The active monitor can be selected with d.mon. @@ -18,4 +18,4 @@

    SEE ALSO

    AUTHOR

    -Anna Petrasova, NCSU OSGeoREL +Anna Petrasova, NCSU GeoForAll Lab diff --git a/scripts/db.droptable/db.droptable.html b/scripts/db.droptable/db.droptable.html index 68cd12be937..bf5a7209e0a 100644 --- a/scripts/db.droptable/db.droptable.html +++ b/scripts/db.droptable/db.droptable.html @@ -33,7 +33,7 @@

    Removing an attribute table from given database

    db.droptable allows defining optionally driver and database options different from default connection settings -(db.connect -p). +(db.connect -p).
     # drop the table from SQLite database
    diff --git a/scripts/db.in.ogr/db.in.ogr.html b/scripts/db.in.ogr/db.in.ogr.html
    index 70e7dddc1b1..14c46cd6c06 100644
    --- a/scripts/db.in.ogr/db.in.ogr.html
    +++ b/scripts/db.in.ogr/db.in.ogr.html
    @@ -12,11 +12,12 @@ 

    Import CSV file

    Limited type recognition can be done for Integer, Real, String, Date, Time and DateTime columns through a descriptive file with same name as the CSV file, but .csvt extension -(see details here). +(see details here).
     # NOTE: create koeppen_gridcode.csvt first for automated type recognition
    -db.in.ogr input=koeppen_gridcode.csv output=koeppen_gridcode
    +db.in.ogr input=koeppen_gridcode.csv output=koeppen_gridcode gdal_doo="AUTODETECT_TYPE=YES"
    +db.describe koeppen_gridcode -c
     db.select table=koeppen_gridcode
     
    @@ -50,7 +51,7 @@

    Import of a PostgreSQL table

    Import XLS file

    To force reading headers, define environmental -variable OGR_XLS_HEADERS='FORCE'. Parameter db_table +variable OGR_XLS_HEADERS='FORCE'. Parameter db_table refers to the list within XLS file.
    diff --git a/scripts/db.test/db.test.html b/scripts/db.test/db.test.html
    index fb7d6271ab2..5484d91fd5b 100644
    --- a/scripts/db.test/db.test.html
    +++ b/scripts/db.test/db.test.html
    @@ -2,11 +2,12 @@ 

    DESCRIPTION

    db.test tests database driver and database server running set of SQL queries. Database must exist and connection must be set -by db.connect. +by db.connect.

    EXAMPLE

    Test current SQL backend driver: +

     db.connect -p
     db.test test=test1
    diff --git a/scripts/db.univar/db.univar.py b/scripts/db.univar/db.univar.py
    index 4654e3011a0..3969d4f7caf 100755
    --- a/scripts/db.univar/db.univar.py
    +++ b/scripts/db.univar/db.univar.py
    @@ -89,8 +89,7 @@ def sortfile(infile, outfile):
             for i in range(len(lines)):
                 lines[i] = float(lines[i].rstrip("\r\n"))
             lines.sort()
    -        for line in lines:
    -            outf.write(str(line) + "\n")
    +        outf.writelines(str(line) + "\n" for line in lines)
     
         inf.close()
         outf.close()
    @@ -277,7 +276,8 @@ def main():
                 sys.stdout.write("coeff_var=0\n")
             sys.stdout.write("sum=%.15g\n" % sum)
         else:
    -        raise ValueError(f"Unknown output format {output_format}")
    +        msg = f"Unknown output format {output_format}"
    +        raise ValueError(msg)
     
         if not extend:
             return
    diff --git a/scripts/g.extension.all/g.extension.all.html b/scripts/g.extension.all/g.extension.all.html
    index 5b0cb992e3e..e06872d2f16 100644
    --- a/scripts/g.extension.all/g.extension.all.html
    +++ b/scripts/g.extension.all/g.extension.all.html
    @@ -4,13 +4,13 @@ 

    DESCRIPTION

    installed GRASS Addons extensions in local GRASS installation. The extensions can be installed by g.extension. List of -locally installed extensions can be printed by g.extension --a. +locally installed extensions can be printed by g.extension +-a.

    EXAMPLES

    Rebuild locally installed extensions which were built against -different GIS Library (see g.version -r) +different GIS Library (see g.version -r)
     g.extension.rebuild.all
    diff --git a/scripts/g.extension.all/g.extension.all.py b/scripts/g.extension.all/g.extension.all.py
    index 1b0a2a88108..03bd7471a9d 100644
    --- a/scripts/g.extension.all/g.extension.all.py
    +++ b/scripts/g.extension.all/g.extension.all.py
    @@ -109,9 +109,7 @@ def download_modules_xml_file(url, response_format, *args, **kwargs):
                 desc = HTTP_STATUS_CODES[index].description
                 gs.fatal(
                     _(
    -                    "Download file from <{url}>, "
    -                    "return status code {code}, "
    -                    "{desc}"
    +                    "Download file from <{url}>, return status code {code}, {desc}"
                     ).format(
                         url=url,
                         code=response.code,
    diff --git a/scripts/g.extension/g.extension.html b/scripts/g.extension/g.extension.html
    index 06e24cb4e43..1fc8a5a96b2 100644
    --- a/scripts/g.extension/g.extension.html
    +++ b/scripts/g.extension/g.extension.html
    @@ -36,19 +36,19 @@ 

    Where the extensions are installed

    directory. The default is a directory for application data and settings inside the user's home directory. -On GNU/Linux it is $HOME/.grass8/addons, -on MS-Windows it is %APPDATA%\Roaming\GRASS8\addons. -The name of the directory is stored in the GRASS_ADDON_BASE +On GNU/Linux it is $HOME/.grass8/addons, +on MS-Windows it is %APPDATA%\Roaming\GRASS8\addons. +The name of the directory is stored in the GRASS_ADDON_BASE environmental variable.

    The flag -s changes this install target directory to the GRASS GIS installation directory -(determined by GISBASE environmental variable, e.g. /usr/) -rather than the default directory defined as per GRASS_ADDON_BASE +(determined by GISBASE environmental variable, e.g. /usr/) +rather than the default directory defined as per GRASS_ADDON_BASE (see also documentation for variables). g.extension checks if the user has permission to write to -GISBASE or GRASS_ADDON_BASE. +GISBASE or GRASS_ADDON_BASE.

    The place where the extensions are installed can be customized by @@ -77,7 +77,7 @@

    Local source code directory

    Source code in a ZIP or TAR archive

    In addition, new extension can be also installed from a ZIP file -or an archive file from the TAR family (e.g., .tar.gz or .bz2). +or an archive file from the TAR family (e.g., .tar.gz or .bz2). The file can be on disk (specified with a path), or on the web (specified by an URL). @@ -85,7 +85,7 @@

    Online repositories: GitHub, GitLab and Bitbucket

    For well known general hosting services, namely GitHub, GitLab and Bitbucket, g.extension supports the download of a repository. Here the user only needs to provide a base URL to the repository web page -(with or without the https:// part). +(with or without the https:// part). For GitHub, GitLab and Bitbucket, the latest source code in the default branch is downloaded, unless a specific branch is requested in the branch option. @@ -276,4 +276,4 @@

    AUTHORS

    Markus Neteler (original shell script)
    Martin Landa, Czech Technical University in Prague, Czech Republic (Python rewrite)
    -Vaclav Petras, NCSU OSGeoREL (support for general sources, partial refactoring) +Vaclav Petras, NCSU GeoForAll Lab (support for general sources, partial refactoring) diff --git a/scripts/g.extension/g.extension.py b/scripts/g.extension/g.extension.py index f14a742ca06..f694e73c23a 100644 --- a/scripts/g.extension/g.extension.py +++ b/scripts/g.extension/g.extension.py @@ -517,8 +517,7 @@ def get_version_branch(major_version): if stderr: gs.fatal( _( - "Failed to get branch from the Git repository <{repo_path}>.\n" - "{error}" + "Failed to get branch from the Git repository <{repo_path}>.\n{error}" ).format( repo_path=GIT_URL, error=gs.decode(stderr), @@ -1041,11 +1040,11 @@ def write_xml_modules(name, tree=None): if bnode is not None: file_.write("%s\n" % (" " * indent)) indent += 4 - for fnode in bnode.findall("file"): - file_.write( - "%s%s\n" - % (" " * indent, os.path.join(options["prefix"], fnode.text)) - ) + file_.writelines( + "%s%s\n" + % (" " * indent, os.path.join(options["prefix"], fnode.text)) + for fnode in bnode.findall("file") + ) indent -= 4 file_.write("%s\n" % (" " * indent)) file_.write('%s\n' % (" " * indent, libgis_revison)) @@ -1076,22 +1075,26 @@ def write_xml_extensions(name, tree=None): # extension name file_.write('%s\n' % (" " * indent, tnode.get("name"))) indent += 4 - """ - file_.write('%s%s\n' % - (' ' * indent, tnode.find('description').text)) - file_.write('%s%s\n' % - (' ' * indent, tnode.find('keywords').text)) - """ + + # file_.write( + # "%s%s\n" + # % (" " * indent, tnode.find("description").text) + # ) + # file_.write( + # "%s%s\n" + # % (" " * indent, tnode.find("keywords").text) + # ) + # extension files bnode = tnode.find("binary") if bnode is not None: file_.write("%s\n" % (" " * indent)) indent += 4 - for fnode in bnode.findall("file"): - file_.write( - "%s%s\n" - % (" " * indent, os.path.join(options["prefix"], fnode.text)) - ) + file_.writelines( + "%s%s\n" + % (" " * indent, os.path.join(options["prefix"], fnode.text)) + for fnode in bnode.findall("file") + ) indent -= 4 file_.write("%s\n" % (" " * indent)) # extension modules @@ -1099,8 +1102,10 @@ def write_xml_extensions(name, tree=None): if mnode is not None: file_.write("%s\n" % (" " * indent)) indent += 4 - for fnode in mnode.findall("module"): - file_.write("%s%s\n" % (" " * indent, fnode.text)) + file_.writelines( + "%s%s\n" % (" " * indent, fnode.text) + for fnode in mnode.findall("module") + ) indent -= 4 file_.write("%s\n" % (" " * indent)) @@ -1132,14 +1137,14 @@ def write_xml_toolboxes(name, tree=None): % (" " * indent, tnode.get("name"), tnode.get("code")) ) indent += 4 - for cnode in tnode.findall("correlate"): - file_.write( - '%s\n' % (" " * indent, tnode.get("code")) - ) - for mnode in tnode.findall("task"): - file_.write( - '%s\n' % (" " * indent, mnode.get("name")) - ) + file_.writelines( + '%s\n' % (" " * indent, tnode.get("code")) + for cnode in tnode.findall("correlate") + ) + file_.writelines( + '%s\n' % (" " * indent, mnode.get("name")) + for mnode in tnode.findall("task") + ) indent -= 4 file_.write("%s\n" % (" " * indent)) @@ -1392,16 +1397,16 @@ def install_extension_xml(edict): for name in edict: # so far extensions do not have description or keywords # only modules have - """ - try: - desc = gtask.parse_interface(name).description - # mname = gtask.parse_interface(name).name - keywords = gtask.parse_interface(name).keywords - except Exception as e: - gs.warning(_("No addons metadata available." - " Addons metadata file not updated.")) - return [] - """ + + # try: + # desc = gtask.parse_interface(name).description + # # mname = gtask.parse_interface(name).name + # keywords = gtask.parse_interface(name).keywords + # except Exception as e: + # gs.warning( + # _("No addons metadata available. Addons metadata file not updated.") + # ) + # return [] tnode = None for node in tree.findall("task"): @@ -1412,14 +1417,13 @@ def install_extension_xml(edict): if tnode is None: # create new node for task tnode = ET.Element("task", attrib={"name": name}) - """ - dnode = etree.Element('description') - dnode.text = desc - tnode.append(dnode) - knode = etree.Element('keywords') - knode.text = (',').join(keywords) - tnode.append(knode) - """ + + # dnode = ET.Element("description") + # dnode.text = desc + # tnode.append(dnode) + # knode = ET.Element("keywords") + # knode.text = (",").join(keywords) + # tnode.append(knode) # create binary bnode = ET.Element("binary") @@ -1549,37 +1553,36 @@ def install_module_xml(mlist): # binary files installed with an extension are now # listed in extensions.xml - """ - # create binary - bnode = etree.Element('binary') - list_of_binary_files = [] - for file_name in os.listdir(url): - file_type = os.path.splitext(file_name)[-1] - file_n = os.path.splitext(file_name)[0] - html_path = os.path.join(options['prefix'], 'docs', 'html') - c_path = os.path.join(options['prefix'], 'bin') - py_path = os.path.join(options['prefix'], 'scripts') - # html or image file - if file_type in ['.html', '.jpg', '.png'] \ - and file_n in os.listdir(html_path): - list_of_binary_files.append(os.path.join(html_path, file_name)) - # c file - elif file_type in ['.c'] and file_name in os.listdir(c_path): - list_of_binary_files.append(os.path.join(c_path, file_n)) - # python file - elif file_type in ['.py'] and file_name in os.listdir(py_path): - list_of_binary_files.append(os.path.join(py_path, file_n)) - # man file - man_path = os.path.join(options['prefix'], 'docs', 'man', 'man1') - if name + '.1' in os.listdir(man_path): - list_of_binary_files.append(os.path.join(man_path, name + '.1')) - # add binaries to xml file - for binary_file_name in list_of_binary_files: - fnode = etree.Element('file') - fnode.text = binary_file_name - bnode.append(fnode) - tnode.append(bnode) - """ + # # create binary + # bnode = etree.Element("binary") + # list_of_binary_files = [] + # for file_name in os.listdir(url): + # file_type = os.path.splitext(file_name)[-1] + # file_n = os.path.splitext(file_name)[0] + # html_path = os.path.join(options["prefix"], "docs", "html") + # c_path = os.path.join(options["prefix"], "bin") + # py_path = os.path.join(options["prefix"], "scripts") + # # html or image file + # if file_type in [".html", ".jpg", ".png"] and file_n in os.listdir( + # html_path + # ): + # list_of_binary_files.append(os.path.join(html_path, file_name)) + # # c file + # elif file_type in [".c"] and file_name in os.listdir(c_path): + # list_of_binary_files.append(os.path.join(c_path, file_n)) + # # python file + # elif file_type in [".py"] and file_name in os.listdir(py_path): + # list_of_binary_files.append(os.path.join(py_path, file_n)) + # # man file + # man_path = os.path.join(options["prefix"], "docs", "man", "man1") + # if name + ".1" in os.listdir(man_path): + # list_of_binary_files.append(os.path.join(man_path, name + ".1")) + # # add binaries to xml file + # for binary_file_name in list_of_binary_files: + # fnode = etree.Element("file") + # fnode.text = binary_file_name + # bnode.append(fnode) + # tnode.append(bnode) tree.append(tnode) else: gs.verbose( @@ -1799,8 +1802,9 @@ def is_binary_string(bytes): def extract_zip(name, directory, tmpdir): """Extract a ZIP file into a directory""" gs.debug( - "extract_zip(name={name}, directory={directory}," - " tmpdir={tmpdir})".format(name=name, directory=directory, tmpdir=tmpdir), + "extract_zip(name={name}, directory={directory}, tmpdir={tmpdir})".format( + name=name, directory=directory, tmpdir=tmpdir + ), 3, ) try: @@ -1824,8 +1828,9 @@ def extract_zip(name, directory, tmpdir): def extract_tar(name, directory, tmpdir): """Extract a TAR or a similar file into a directory""" gs.debug( - "extract_tar(name={name}, directory={directory}," - " tmpdir={tmpdir})".format(name=name, directory=directory, tmpdir=tmpdir), + "extract_tar(name={name}, directory={directory}, tmpdir={tmpdir})".format( + name=name, directory=directory, tmpdir=tmpdir + ), 3, ) import tarfile @@ -2182,18 +2187,12 @@ def remove_extension(force=False): gs.message(_("Extension <%s> successfully uninstalled.") % ename) elif flags["t"]: gs.warning( - _( - "Toolbox <%s> not removed. " - "Re-run '%s' with '-f' flag to force removal" - ) + _("Toolbox <%s> not removed. Re-run '%s' with '-f' flag to force removal") % (options["extension"], "g.extension") ) else: gs.warning( - _( - "Extension <%s> not removed. " - "Re-run '%s' with '-f' flag to force removal" - ) + _("Extension <%s> not removed. Re-run '%s' with '-f' flag to force removal") % (options["extension"], "g.extension") ) @@ -2589,7 +2588,6 @@ def resolve_known_host_service(url, name, branch): def validate_url(url): - """""" if not os.path.exists(url): url_validated = False message = None @@ -2821,8 +2819,7 @@ def main(): if options["operation"] != "add": gs.warning( _( - "Flag '{}' is relevant only to" - " 'operation=add'. Ignoring this flag." + "Flag '{}' is relevant only to 'operation=add'. Ignoring this flag." ).format(flag) ) else: @@ -2835,10 +2832,8 @@ def main(): install_extension() else: if original_url == "" or flags["o"]: - """ - Query GitHub API only if extension will be downloaded - from official GRASS GIS addon repository - """ + # Query GitHub API only if extension will be downloaded + # from official GRASS GIS addons repository get_addons_paths(gg_addons_base_dir=options["prefix"]) source, url = resolve_source_code( name=options["extension"], diff --git a/scripts/g.extension/testsuite/data/sample_modules/r.plus.example/r.plus.example.html b/scripts/g.extension/testsuite/data/sample_modules/r.plus.example/r.plus.example.html index 394878481e4..d746306326e 100644 --- a/scripts/g.extension/testsuite/data/sample_modules/r.plus.example/r.plus.example.html +++ b/scripts/g.extension/testsuite/data/sample_modules/r.plus.example/r.plus.example.html @@ -18,4 +18,4 @@

    SEE ALSO

    AUTHOR

    -Vaclav Petras, NCSU OSGeoREL
    +Vaclav Petras, NCSU GeoForAll Lab diff --git a/scripts/g.extension/testsuite/test_addons_download.py b/scripts/g.extension/testsuite/test_addons_download.py index 07be93cf878..111e591f90d 100644 --- a/scripts/g.extension/testsuite/test_addons_download.py +++ b/scripts/g.extension/testsuite/test_addons_download.py @@ -51,10 +51,9 @@ def setUp(self): if self.install_prefix.exists(): files = [path.name for path in self.install_prefix.iterdir()] if files: - raise RuntimeError( - f"Install prefix path '{self.install_prefix}' \ + msg = f"Install prefix path '{self.install_prefix}' \ contains files {','.join(files)}" - ) + raise RuntimeError(msg) def tearDown(self): """Remove created files""" diff --git a/scripts/g.extension/testsuite/test_addons_modules.py b/scripts/g.extension/testsuite/test_addons_modules.py index 9b99e7ac2ae..cc2eff2aa5b 100644 --- a/scripts/g.extension/testsuite/test_addons_modules.py +++ b/scripts/g.extension/testsuite/test_addons_modules.py @@ -44,9 +44,7 @@ v.neighborhoodmatrix v.transects wx.metadata -""".replace( - "\n", os.linesep -) +""".replace("\n", os.linesep) class TestModulesMetadata(TestCase): @@ -81,11 +79,10 @@ def setUp(self): if os.path.exists(self.install_prefix): files = os.listdir(self.install_prefix) if files: - raise RuntimeError( - "Install prefix path '{}' contains files {}".format( - self.install_prefix, files - ) + msg = "Install prefix path '{}' contains files {}".format( + self.install_prefix, files ) + raise RuntimeError(msg) def tearDown(self): """Remove created files""" diff --git a/scripts/g.manual/g.manual.html b/scripts/g.manual/g.manual.html index 877497d58f3..2c46f7765a2 100644 --- a/scripts/g.manual/g.manual.html +++ b/scripts/g.manual/g.manual.html @@ -6,7 +6,7 @@

    DESCRIPTION

    NOTES

    The name of the browser is defined in the environment variable -GRASS_HTML_BROWSER. For most platforms this should be an +GRASS_HTML_BROWSER. For most platforms this should be an executable in your PATH, or the full path to an executable. See variables for details. diff --git a/scripts/i.band.library/i.band.library.html b/scripts/i.band.library/i.band.library.html index 196f1319e3a..8b1975b53f2 100644 --- a/scripts/i.band.library/i.band.library.html +++ b/scripts/i.band.library/i.band.library.html @@ -159,15 +159,15 @@

    Band reference registry files

    Each series starts with an unique identifier ("Sentinel2" in example above). Required attributes are only two: -a shortcut and bands. Note that a shortcut +a shortcut and bands. Note that a shortcut must be unique in all band reference registry files. Number of other attributes is not defined or even limited (in example above -only description and instruments attributes are -defined). List of bands is defined by a bands attribute. Each +only description and instruments attributes are +defined). List of bands is defined by a bands attribute. Each band is defined by an identifier ("1", "2" in example above). List of attributes describing each band is not pre-defined or limited. In -example above each band is described by a central wavelength -(nm), bandwidth (nm), and a tag. +example above each band is described by a central wavelength +(nm), bandwidth (nm), and a tag.

    Band reference identifier defined by pattern option is given by @@ -177,7 +177,7 @@

    Band reference registry files

    System-defined registry files are located in GRASS GIS installation -directory ($GISBASE/etc/i.band.library). Note that +directory ($GISBASE/etc/i.band.library). Note that currently i.band.library allows managing only system-defined registry files. Support for user-defined registry files is planned to be implemented, see KNOWN ISSUES section for diff --git a/scripts/i.in.spotvgt/i.in.spotvgt.html b/scripts/i.in.spotvgt/i.in.spotvgt.html index 38719f79fc2..ad4a4466294 100644 --- a/scripts/i.in.spotvgt/i.in.spotvgt.html +++ b/scripts/i.in.spotvgt/i.in.spotvgt.html @@ -13,6 +13,8 @@

    NOTES

    The SPOT VGT files are delivered in HDF4 (Hierarchical Data Format Release 4) format. It is required to have the GDAL libraries installed with HDF4 support. +

    EXAMPLE

    +

    Export of entire world SPOT VGT maps

    When working with SPOT VGT with entire world extent, it is recommended diff --git a/scripts/i.in.spotvgt/i.in.spotvgt.py b/scripts/i.in.spotvgt/i.in.spotvgt.py index 9885caea5ec..8e56f5f529b 100755 --- a/scripts/i.in.spotvgt/i.in.spotvgt.py +++ b/scripts/i.in.spotvgt/i.in.spotvgt.py @@ -260,10 +260,7 @@ def main(): gs.message(_("Imported SPOT VEGETATION SM quality map <%s>.") % smfile) gs.message( - _( - "Note: A snow map can be extracted by category " - "252 (d.rast %s cat=252)" - ) + _("Note: A snow map can be extracted by category 252 (d.rast %s cat=252)") % smfile ) gs.message("") diff --git a/scripts/i.oif/i.oif.html b/scripts/i.oif/i.oif.html index e763154f201..6420c79daba 100644 --- a/scripts/i.oif/i.oif.html +++ b/scripts/i.oif/i.oif.html @@ -44,7 +44,7 @@

    NOTES

    By default the module will calculate standard deviations for all bands in -parallel. To run serially use the -s flag. If the WORKERS +parallel. To run serially use the -s flag. If the WORKERS environment variable is set, the number of concurrent processes will be limited to that number of jobs. diff --git a/scripts/i.oif/i.oif.py b/scripts/i.oif/i.oif.py index cb156675927..81ef0b25f7b 100755 --- a/scripts/i.oif/i.oif.py +++ b/scripts/i.oif/i.oif.py @@ -146,8 +146,7 @@ def main(): sys.stdout.write(fmt % (p + (v,))) else: outf = open(output, "w") - for v, p in oif: - outf.write(fmt % (p + (v,))) + outf.writelines(fmt % (p + (v,)) for v, p in oif) outf.close() diff --git a/scripts/i.pansharpen/i.pansharpen.html b/scripts/i.pansharpen/i.pansharpen.html index 6fd17725e86..2ccbb6ad66a 100644 --- a/scripts/i.pansharpen/i.pansharpen.html +++ b/scripts/i.pansharpen/i.pansharpen.html @@ -1,6 +1,6 @@

    DESCRIPTION

    -i.pansharpen uses a high resolution panchromatic band from a +i.pansharpen uses a high resolution panchromatic band from a multispectral image to sharpen 3 lower resolution bands. The 3 lower resolution bands can then be combined into an RGB color image at a higher (more detailed) resolution than is possible using the original 3 @@ -9,10 +9,12 @@

    DESCRIPTION

    and a high resolution panchromatic band 8 at 15m resolution. Pan sharpening allows bands 3-2-1 (or other combinations of 30m resolution bands like 4-3-2 or 5-4-2) to be combined into a 15m resolution color image. -

    -i.pansharpen offers a choice of three different 'pan sharpening' + +

    +i.pansharpen offers a choice of three different 'pan sharpening' algorithms: IHS, Brovey, and PCA. -

    + +

    For IHS pan sharpening, the original 3 lower resolution bands, selected as red, green and blue channels for creating an RGB composite image, are transformed into IHS (intensity, hue, and saturation) color space. The @@ -20,44 +22,49 @@

    DESCRIPTION

    with the original hue (H) and saturation (S) channels, and transformed back to RGB color space at the higher resolution of the panchromatic band. The algorithm for this can be represented as: RGB -> IHS -> [pan]HS -> RGB. -

    + +

    With a Brovey pan sharpening, each of the 3 lower resolution bands and panchromatic band are combined using the following algorithm to calculate 3 new bands at the higher resolution (example for band 1): +

                              band1
         new band1 = ----------------------- * panband
                      band1 + band2 + band3
     
    + In PCA pan sharpening, a principal component analysis is performed on the original 3 lower resolution bands to create 3 principal component images (PC1, PC2, and PC3) and their associated eigenvectors (EV), such that: -
     
    +
          band1  band2  band3
     PC1: EV1-1  EV1-2  EV1-3
     PC2: EV2-1  EV2-2  EV2-3
     PC3: EV3-1  EV3-2  EV3-3
    +
    and +
     PC1 = EV1-1 * band1 + EV1-2 * band2 + EV1-3 * band3 - mean(bands 1,2,3)
    -
     
    + An inverse PCA is then performed, substituting the panchromatic band for PC1. To do this, the eigenvectors matrix is inverted (in this case transposed), the PC images are multiplied by the eigenvectors with the panchromatic band substituted for PC1, and mean of each band is added to each transformed image band using the following algorithm (example for band 1): -
     
    +
     band1 = pan * EV1-1 + PC2 * EV1-2 + PC3 * EV1-3 + mean(band1)
    -
     
    + The assignment of the channels depends on the satellite. Examples of satellite imagery with high resolution panchromatic bands, and lower resolution spectral bands include Landsat 7 ETM, QuickBird, and SPOT. -
    +

    NOTES

    The module works for 2-bit to 30-bit images. All images are rescaled to 8-bit @@ -69,7 +76,8 @@

    NOTES

    be rescaled to 0-255. This can give better visual distinction to features, especially when the range of actual values in an image only occupies a relatively limited portion of the possible range. -

    + +

    i.pansharpen temporarily changes the computational region to the high resolution of the panchromatic band during sharpening calculations, then restores the previous region settings. The current region coordinates (and @@ -77,11 +85,13 @@

    NOTES

    histogram matched to the band it is replaces prior to substitution (i.e., the intensity channel for IHS sharpening, the low res band selected for each color channel with Brovey sharpening, and the PC1 image for PCA sharpening). -

    + +

    By default, the command will attempt to employ parallel processing, using up to 3 cores simultaneously. The -s flag will disable parallel processing, but does use an optimized r.mapcalc expression to reduce disk I/O. -

    + +

    The three pan-sharpened output channels may be combined with d.rgb or r.composite. Colors may be optionally optimized with i.colors.enhance. While the resulting color image will be at the higher resolution in all cases, diff --git a/scripts/i.spectral/i.spectral.py b/scripts/i.spectral/i.spectral.py index 07efd8af31f..e275b004f72 100755 --- a/scripts/i.spectral/i.spectral.py +++ b/scripts/i.spectral/i.spectral.py @@ -104,8 +104,7 @@ def draw_gnuplot(what, xlabels, output, img_format, coord_legend): outfile = os.path.join(tmp_dir, "data_%d" % i) outf = open(outfile, "w") xrange = max(xrange, len(row) - 2) - for j, val in enumerate(row[3:]): - outf.write("%d %s\n" % (j + 1, val)) + outf.writelines("%d %s\n" % (j + 1, val) for j, val in enumerate(row[3:])) outf.close() # build gnuplot script @@ -147,8 +146,7 @@ def draw_gnuplot(what, xlabels, output, img_format, coord_legend): plotfile = os.path.join(tmp_dir, "spectrum.gnuplot") plotf = open(plotfile, "w") - for line in lines: - plotf.write(line + "\n") + plotf.writelines(line + "\n" for line in lines) plotf.close() if output: @@ -163,15 +161,13 @@ def draw_linegraph(what): xfile = os.path.join(tmp_dir, "data_x") xf = open(xfile, "w") - for j, val in enumerate(what[0][3:]): - xf.write("%d\n" % (j + 1)) + xf.writelines("%d\n" % (j + 1) for j, val in enumerate(what[0][3:])) xf.close() for i, row in enumerate(what): yfile = os.path.join(tmp_dir, "data_y_%d" % i) yf = open(yfile, "w") - for j, val in enumerate(row[3:]): - yf.write("%s\n" % val) + yf.writelines("%s\n" % val for j, val in enumerate(row[3:])) yf.close() yfiles.append(yfile) @@ -193,8 +189,7 @@ def draw_linegraph(what): if not found: gcore.fatal( _( - "Supported monitor isn't running. Please launch one of the" - " monitors {}." + "Supported monitor isn't running. Please launch one of the monitors {}." ).format(", ".join(supported_monitors)) ) selected_monitor = gcore.read_command("d.mon", flags="p", quiet=True).replace( diff --git a/scripts/i.tasscap/i.tasscap.html b/scripts/i.tasscap/i.tasscap.html index 2bb244e32d8..9281ef10243 100644 --- a/scripts/i.tasscap/i.tasscap.html +++ b/scripts/i.tasscap/i.tasscap.html @@ -86,7 +86,8 @@

    REFERENCES

  • LANDSAT-7: TASSCAP factors cited from: DERIVATION OF A TASSELED CAP TRANSFORMATION BASED ON LANDSAT 7 AT-SATELLITE REFLECTANCE. Chengquan Huang, Bruce Wylie, Limin Yang, Collin Homer and Gregory Zylstra Raytheon ITSS, - USGS EROS Data Center Sioux Falls, SD 57198, USA (http://landcover.usgs.gov/pdf/tasseled.pdf). + USGS EROS Data Center Sioux Falls, SD 57198, USA + (PDF). This is published as well in INT. J. OF RS, 2002, VOL 23, NO. 8, 1741-1748.
  • MODIS Tasseled Cap coefficients - Ref: Lobser & Cohen (2007). MODIS tasseled cap: land cover characteristics expressed through transformed MODIS data. diff --git a/scripts/i.tasscap/i.tasscap.py b/scripts/i.tasscap/i.tasscap.py index dc767fd3f19..fb22c6048ed 100755 --- a/scripts/i.tasscap/i.tasscap.py +++ b/scripts/i.tasscap/i.tasscap.py @@ -27,7 +27,7 @@ # DERIVATION OF A TASSELED CAP TRANSFORMATION BASED ON LANDSAT 7 AT-SATELLITE REFLECTANCE # Chengquan Huang, Bruce Wylie, Limin Yang, Collin Homer and Gregory Zylstra Raytheon ITSS, # USGS EROS Data Center Sioux Falls, SD 57198, USA -# http://landcover.usgs.gov/pdf/tasseled.pdf +# https://digitalcommons.unl.edu/usgsstaffpub/621/ # # This is published as well in INT. J. OF RS, 2002, VOL 23, NO. 8, 1741-1748. # Compare discussion: diff --git a/scripts/m.proj/m.proj.html b/scripts/m.proj/m.proj.html index b67f6c1fa6f..b8015206b28 100644 --- a/scripts/m.proj/m.proj.html +++ b/scripts/m.proj/m.proj.html @@ -23,19 +23,19 @@

    DESCRIPTION

    NOTES

    -cs2cs expects input data to formatted as x y, so if -working with latitude-longitude data be sure to send the x -value first, i.e., longitude latitude. Output data will +cs2cs expects input data to formatted as x y, so if +working with latitude-longitude data be sure to send the x +value first, i.e., longitude latitude. Output data will be exported using the same convention.

    -cs2cs will treat a third data column as a z value +cs2cs will treat a third data column as a z value (elevation) and will modify the value accordingly. This usually translates into small but real differences in that data column.

    cs2cs does not expect the input stream to contain column headings, only numbers. If your data file has lines you wish to have passed through without being processed, they must start with the -'#' character. +'#' character.

    If sending m.proj data from standard input, be aware that the data is first stored to a temporary file before being processed @@ -58,7 +58,7 @@

    NOTES

    If output is to lat/long, it will be formatted using PROJ's Degree:Minute:Second (DMS) convention -of DDDdMM'SSS.SS"H. This can be handy if you wish to quickly +of DDDdMM'SSS.SS"H. This can be handy if you wish to quickly convert lat/long decimal degree data into its DMS equivalent.
    Alternatively, to have m.proj output data in decimal degrees, use the -d flag. This flag can also be used with non-lat/long @@ -66,7 +66,7 @@

    NOTES

    default is 2).

    Note that Lat/long output can be converted to GRASS's DMS convention -(DDD:MM:SSS.SSSH) by piping the results of m.proj +(DDD:MM:SSS.SSSH) by piping the results of m.proj through the sed stream editor as follows.

    @@ -105,7 +105,7 @@ 

    Reproject Long/Lat WGS84 coordinate pair to current map projection

    -The same, but load points from a file named waypoints.txt and +The same, but load points from a file named waypoints.txt and continue on to import the results into a GRASS vector points map in the current map projection:

    @@ -141,25 +141,25 @@ 

    Custom projection parameter usage

    -Projection parameters provided in the above case: +proj -(projection type), +name (projection name), +a -(ellipsoid: equatorial radius), +es (ellipsoid: -eccentricity squared), +zone (zone for the area), -+unfact (conversion factor from meters to other units, -e.g. feet), +lat_0 (standard parallel), +lon_0 -(central meridian), +k (scale factor) and +x_0 +Projection parameters provided in the above case: +proj +(projection type), +name (projection name), +a +(ellipsoid: equatorial radius), +es (ellipsoid: +eccentricity squared), +zone (zone for the area), ++unfact (conversion factor from meters to other units, +e.g. feet), +lat_0 (standard parallel), +lon_0 +(central meridian), +k (scale factor) and +x_0 (false easting). Sometimes false northing is needed which is coded as -+y_0. Internally, the underlying ++y_0. Internally, the underlying PROJ projection library performs an inverse projection to latitude-longitude and then projects the coordinate list to the target projection.

    Datum conversions are automatically handled by the PROJ library if -+datum settings are specified on both the input and output -projections on the command line. The +towgs84 parameter can be used to ++datum settings are specified on both the input and output +projections on the command line. The +towgs84 parameter can be used to define either 3 or 7 term datum transform coefficients, satisfying this requirement. -

    If a datum is specified there is no need for the +ellps= or underlying -parameters, +a=, +es=, etc. +

    If a datum is specified there is no need for the +ellps= or underlying +parameters, +a=, +es=, etc.

    Another custom parameter usage example: diff --git a/scripts/r.fillnulls/r.fillnulls.html b/scripts/r.fillnulls/r.fillnulls.html index 29c5e42480e..04d060f6339 100644 --- a/scripts/r.fillnulls/r.fillnulls.html +++ b/scripts/r.fillnulls/r.fillnulls.html @@ -16,10 +16,10 @@

    NOTES

    is generated in a hole. The width of edge area can be adjusted by changing the edge parameter.

    During the interpolation following warning may occur when using the RST method:

    - + Warning: strip exists with insufficient data
    Warning: taking too long to find points for interpolation--please change -the region to area where your points are
    +the region to area where your points are

    This warning is generated if large data holes exist within the surface. As the idea of r.fillnulls is to fill such holes, the user may @@ -34,7 +34,7 @@

    NOTES

    on v.surf.rst regularized splines with tension interpolation module which interpolates the raster cell values for NULL data areas from the boundary values of the -NULL data area. An eventual raster MASK is respected during the NULL +NULL data area. An eventual raster mask is respected during the NULL data area(s) filling. The interpolated values are patched into the NULL data area(s) of the input map and saved into a new raster map. diff --git a/scripts/r.fillnulls/r.fillnulls.py b/scripts/r.fillnulls/r.fillnulls.py index b22550db497..23519b88122 100755 --- a/scripts/r.fillnulls/r.fillnulls.py +++ b/scripts/r.fillnulls/r.fillnulls.py @@ -150,9 +150,7 @@ def main(): mapset = gs.gisenv()["MAPSET"] unique = str(os.getpid()) # Shouldn't we use temp name? prefix = "r_fillnulls_%s_" % unique - failed_list = ( - [] - ) # a list of failed holes. Caused by issues with v.surf.rst. Connected with #1813 + failed_list = [] # a list of failed holes. Caused by issues with v.surf.rst. Connected with #1813 # check if input file exists if not gs.find_file(input)["file"]: @@ -229,10 +227,7 @@ def main(): ) except CalledModuleError: gs.fatal( - _( - "abandoned. Removing temporary map, restoring " - "user mask if needed:" - ) + _("abandoned. Removing temporary map, restoring user mask if needed:") ) # assign unique IDs to each hole or hole system (holes closer than edge @@ -248,10 +243,7 @@ def main(): ) except CalledModuleError: gs.fatal( - _( - "abandoned. Removing temporary map, restoring " - "user mask if needed:" - ) + _("abandoned. Removing temporary map, restoring user mask if needed:") ) # get a list of unique hole cat's @@ -275,10 +267,7 @@ def main(): ) except CalledModuleError: gs.fatal( - _( - "abandoned. Removing temporary maps, restoring " - "user mask if needed:" - ) + _("abandoned. Removing temporary maps, restoring user mask if needed:") ) tmp_vmaps.append(prefix + "holes") diff --git a/scripts/r.grow/testsuite/test_grow.py b/scripts/r.grow/testsuite/test_grow.py index 80421da2fec..a857d51569a 100644 --- a/scripts/r.grow/testsuite/test_grow.py +++ b/scripts/r.grow/testsuite/test_grow.py @@ -4,7 +4,6 @@ class TestRGrow(TestCase): - @classmethod def setUpClass(cls): """Set up a small region and test map.""" diff --git a/scripts/r.import/r.import.html b/scripts/r.import/r.import.html index 6858bdd85d3..6cd0aea6875 100644 --- a/scripts/r.import/r.import.html +++ b/scripts/r.import/r.import.html @@ -84,7 +84,7 @@

    EXAMPLES

    Import of SRTM V3 global data at 1 arc-seconds resolution

    The SRTM V3 1 arc-second global data (~30 meters resolution) are available -from EarthExplorer (http://earthexplorer.usgs.gov/). +from EarthExplorer (https://earthexplorer.usgs.gov/). The SRTM collections are located under the "Digital Elevation" category.

    Example for North Carolina sample dataset (the tile name is "n35_w079_1arc_v3.tif"): @@ -106,7 +106,7 @@

    Import of SRTM V3 global data at 1 arc-seconds resolution

  • Import of WorldClim data

    -Import of a subset from WorldClim Bioclim data set, +Import of a subset from WorldClim Bioclim data set, to be reprojected to current project CRS (North Carolina sample dataset). Different resolutions are available, in this example we use the 2.5 arc-minutes resolution data. During import, we spatially subset the world data to the @@ -115,7 +115,7 @@

    Import of WorldClim data

     # download selected Bioclim data (2.5 arc-minutes resolution)
     # optionally tiles are available for the 30 arc-sec resolution
    -wget http://biogeo.ucdavis.edu/data/climate/worldclim/1_4/grid/cur/bio_2-5m_bil.zip
    +wget https://geodata.ucdavis.edu/climate/worldclim/1_4/grid/cur/bio_2-5m_bil.zip
     
     # extract BIO1 from package (BIO1 = Annual Mean Temperature):
     unzip bio_2-5m_bil.zip bio1.bil bio1.hdr
    diff --git a/scripts/r.in.srtm/r.in.srtm.html b/scripts/r.in.srtm/r.in.srtm.html
    index 6532b93523d..bd3832fc8ee 100644
    --- a/scripts/r.in.srtm/r.in.srtm.html
    +++ b/scripts/r.in.srtm/r.in.srtm.html
    @@ -4,7 +4,8 @@ 

    DESCRIPTION

    SRTM Version 1 and improved Version 2 data sets can be downloaded from NASA at this site:
    -http://dds.cr.usgs.gov/srtm/ +http://dds.cr.usgs.gov/srtm/ (archived)
    +https://earthexplorer.usgs.gov/

    Gap-filled SRTM Version 3 data can be downloaded from USGS at this site:
    @@ -35,10 +36,10 @@

    SEE ALSO

    r.in.nasadem (Addon) -

    The Shuttle Radar Topography Mission -homepage at NASA's JPL. +

    The Shuttle Radar Topography Mission +homepage at NASA's JPL. (archived)
    -The SRTM Web Forum +The SRTM Web Forum (archived)

    AUTHORS

    diff --git a/scripts/r.in.wms/r.in.wms.html b/scripts/r.in.wms/r.in.wms.html index 0031627991c..5fbc8d032e5 100644 --- a/scripts/r.in.wms/r.in.wms.html +++ b/scripts/r.in.wms/r.in.wms.html @@ -1,8 +1,8 @@

    DESCRIPTION

    r.in.wms handles all of downloading and importing raster data -from OGC -WMS and OGC +from OGC +WMS and OGC WMTS web mapping servers. It only needs be told the desired data to collect (bounds and resolution) via a region, the server to get the data from, and the layer or layers to get. It @@ -22,7 +22,7 @@

    NOTES

    When using GDAL WMS driver (driver=WMS_GDAL), the GDAL library needs to be built with WMS support, -see GDAL WMS manual page +see GDAL WMS manual page for details.

    Tiled WMS

    @@ -30,7 +30,7 @@

    Tiled WMS

    Into the parameter layers the name of the TiledGroup need to be inserted from Tile Service file. Time variable can be specified in urlparams parameter, -e.g: urlparams='time=2012-1-1'. +e.g: urlparams='time=2012-1-1'.

    EXAMPLES

    @@ -38,11 +38,11 @@

    General Get Capabilities Request

     # Topographic WMS with OpenStreetMap by mundialis
    -r.in.wms -c url="http://ows.mundialis.de/services/service?"
    -r.in.wms -c url="http://ows.mundialis.de/services/service?" | grep Name
    +r.in.wms -c url="https://ows.mundialis.de/services/service?"
    +r.in.wms -c url="https://ows.mundialis.de/services/service?" | grep Name
     
     # Czech WMS
    -r.in.wms -c url="http://wms.cuzk.cz/wms.asp"
    +r.in.wms -c url="https://wms.cuzk.cz/wms.asp"
     

    Download raster data from WMS server (GetMap request)

    @@ -70,8 +70,8 @@

    Open Street Map

    More OSM WMS servers can be found online, e.g. on the OSM wiki in a -OSM WMS Servers list -and on the OSM-WMS-EUROPE page. +OSM WMS Servers list +and on the WORLD_OSM_WMS page.

    Countries and coastlines

    @@ -154,14 +154,14 @@

    Satellite data covering Europe

    REQUIREMENTS

    -r.in.wms requires the gdalwarp +r.in.wms requires the gdalwarp utility from the GDAL/OGR library.

    REFERENCES

    SEE ALSO

    diff --git a/scripts/r.mapcalc.simple/r.mapcalc.simple.html b/scripts/r.mapcalc.simple/r.mapcalc.simple.html index 47481b58836..1277602f1a7 100644 --- a/scripts/r.mapcalc.simple/r.mapcalc.simple.html +++ b/scripts/r.mapcalc.simple/r.mapcalc.simple.html @@ -6,7 +6,7 @@

    DESCRIPTION

    The general syntax for the expression follows r.mapcalc expression format, -for example, A + B or exp(A + B) are valid. +for example, A + B or exp(A + B) are valid. The variables A, B, ..., F represent raster maps which are provided as options a, b, ..., f. @@ -30,7 +30,7 @@

    NOTES

    variable names (A, B, C, ...).
  • The output raster name is not included in the expression.
  • The expression is expected to be a single short one liner - without the function eval().
  • + without the function eval(). Differences to r.mapcalc.simple module in GRASS GIS 5 and 6: @@ -44,7 +44,7 @@

    NOTES

  • The variable names are case-insensitive to allow the original uppercase as well as lowercase as in option names (unless the -c flag is used).
  • -
  • Option names for each map are just one letter (not amap, etc.). +
  • Option names for each map are just one letter (not amap, etc.).
  • Output option name is output as for other modules (not outfile).
  • Raster map names can be optionally quoted (the -q flag).
  • diff --git a/scripts/r.mask/r.mask.html b/scripts/r.mask/r.mask.html index c520bbea353..312de990c47 100644 --- a/scripts/r.mask/r.mask.html +++ b/scripts/r.mask/r.mask.html @@ -1,6 +1,6 @@

    DESCRIPTION

    -r.mask - Facilitates creation of a raster "MASK" map to +r.mask facilitates the creation of a raster "MASK" map to control raster operations.

    @@ -23,7 +23,7 @@

    DESCRIPTION

    The user should be aware that a MASK remains in place until a user renames it to something other than "MASK", or removes it. To remove a mask and restore raster operations to normal (i.e., all cells of the current region), remove the -MASK by setting the -r remove MASK flag (r.mask -r). +MASK by setting the -r remove MASK flag (r.mask -r). Alternatively, a mask can be removed using g.remove or by renaming it to any other name with g.rename. @@ -34,7 +34,7 @@

    NOTES

    the MASK file is removed.

    r.mask uses r.reclass to create a reclassification of an -existing raster map and name it MASK. A reclass map takes up less +existing raster map and name it MASK. A reclass map takes up less space, but is affected by any changes to the underlying map from which it was created. The user can select category values from the input raster to use in the MASK with the maskcats parameter; if r.mask is run from the @@ -48,7 +48,7 @@

    Different ways to create a MASK

    that a MASK can also be created using other functions that have a raster as output, by naming the output raster 'MASK'. Such layers could have other values than 1 and NULL. The user should therefore be aware that grid cells -in the MASK map containing NULL or 0 will replace data with +in the MASK map containing NULL or 0 will replace data with NULL, while cells containing other values will allow data to pass through unaltered. This means that:

    @@ -136,7 +136,7 @@

    SEE ALSO

    r.mapcalc, r.reclass, g.remove, -g.rename +g.rename, r.quant
    diff --git a/scripts/r.out.xyz/r.out.xyz.html b/scripts/r.out.xyz/r.out.xyz.html index e603c592e32..8e40d2c9399 100644 --- a/scripts/r.out.xyz/r.out.xyz.html +++ b/scripts/r.out.xyz/r.out.xyz.html @@ -19,7 +19,7 @@

    NOTES

    r.out.xyz can combine several input raster maps, which can be convenient when it comes to e.g. produce ASCII point cloud files.

    -r.out.xyz is simply a front-end to "r.stats -1g[n]". +r.out.xyz is simply a front-end to "r.stats -1g[n]".

    EXAMPLES

    diff --git a/scripts/r.pack/r.pack.py b/scripts/r.pack/r.pack.py index 1f517a427af..5d1a63d07cd 100644 --- a/scripts/r.pack/r.pack.py +++ b/scripts/r.pack/r.pack.py @@ -101,9 +101,7 @@ def main(): map_basedir = os.path.sep.join( os.path.normpath( map_file["file"], - ).split( - os.path.sep - )[:-2], + ).split(os.path.sep)[:-2], ) vrt_files[map] = map_basedir diff --git a/scripts/r.plane/r.plane.html b/scripts/r.plane/r.plane.html index 714272038e9..385c6a1fb11 100644 --- a/scripts/r.plane/r.plane.html +++ b/scripts/r.plane/r.plane.html @@ -12,7 +12,7 @@

    DESCRIPTION

    NOTES

    -g.region -c provides the easting and northing coordinates for +g.region -c provides the easting and northing coordinates for the center of the current region.

    CELL (integer) maps take less disk space than FCELLs (floating point), diff --git a/scripts/r.shade/r.shade.html b/scripts/r.shade/r.shade.html index 9004b13f26a..1583ecb3e32 100644 --- a/scripts/r.shade/r.shade.html +++ b/scripts/r.shade/r.shade.html @@ -39,8 +39,8 @@

    NOTES

    EXAMPLES

    -In this example, the aspect map in the North Carolina sample -dataset is used to hillshade the elevation map: +In this example, the aspect map in the North Carolina sample +dataset is used to hillshade the elevation map:
     g.region raster=aspect -p
    @@ -66,7 +66,7 @@ 

    EXAMPLES

    Interesting visualizations can be created using different color tables for -elevation raster map, for example using haxby color table. +elevation raster map, for example using haxby color table. diff --git a/scripts/v.in.geonames/v.in.geonames.html b/scripts/v.in.geonames/v.in.geonames.html index a38aa770cc5..c8383c4bc3f 100644 --- a/scripts/v.in.geonames/v.in.geonames.html +++ b/scripts/v.in.geonames/v.in.geonames.html @@ -3,7 +3,7 @@

    DESCRIPTION

    v.in.geonames imports Geonames.org country files (Gazetteer data) into a GRASS vector points map. The country files can be downloaded from the -GeoNames Data Dump +GeoNames Data Dump Server. Only original files can be processed (unzip compressed file first). These Geonames files are encoded in UTF-8 which is maintained in the GRASS database. @@ -35,8 +35,8 @@

    NOTES

    alternatenames : alternatenames, comma separated varchar(4000) latitude : latitude in decimal degrees (wgs84) longitude : longitude in decimal degrees (wgs84) -feature class : see http://www.geonames.org/export/codes.html, char(1) -feature code : see http://www.geonames.org/export/codes.html, varchar(10) +feature class : see https://www.geonames.org/export/codes.html, char(1) +feature code : see https://www.geonames.org/export/codes.html, varchar(10) country code : ISO-3166 2-letter country code, 2 characters cc2 : alternate country codes, comma separated, ISO-3166 2-letter country code, 60 characters admin1 code : fipscode (subject to change to iso code), isocode for the us and ch, see file admin1Codes.txt for display names of this code; varchar(20) @@ -46,7 +46,7 @@

    NOTES

    population : integer elevation : in meters, integer gtopo30 : average elevation of 30'x30' (ca 900mx900m) area in meters, integer -timezone : the timezone id (see file http://download.geonames.org/export/dump/timeZones.txt) +timezone : the timezone id (see file https://download.geonames.org/export/dump/timeZones.txt) modification date : date of last modification in yyyy-MM-dd format
    @@ -55,7 +55,7 @@

    EXAMPLE

    Download and import geonames for Czech Republic.
    -wget http://download.geonames.org/export/dump/CZ.zip
    +wget https://download.geonames.org/export/dump/CZ.zip
     unzip CZ.zip
     
     v.in.geonames input=CZ.txt output=geonames_cz
    @@ -64,8 +64,8 @@ 

    EXAMPLE

    REFERENCES

    SEE ALSO

    diff --git a/scripts/v.in.geonames/v.in.geonames.py b/scripts/v.in.geonames/v.in.geonames.py index b6f5f47ace0..cf2e8c99705 100755 --- a/scripts/v.in.geonames/v.in.geonames.py +++ b/scripts/v.in.geonames/v.in.geonames.py @@ -8,9 +8,9 @@ # Converted to Python by Glynn Clements # # PURPOSE: Import geonames.org dumps -# http://download.geonames.org/export/dump/ +# https://download.geonames.org/export/dump/ # -# Feature Codes: http://www.geonames.org/export/codes.html +# Feature Codes: https://www.geonames.org/export/codes.html # # COPYRIGHT: (c) 2008-2014 Markus Neteler, GRASS Development Team # @@ -69,7 +69,7 @@ def main(): gs.message(_("Converting %d place names...") % num_places) # pump data into GRASS: - # http://download.geonames.org/export/dump/readme.txt + # https://download.geonames.org/export/dump/readme.txt # The main 'geoname' table has the following fields : # --------------------------------------------------- # geonameid : integer id of record in geonames database @@ -78,8 +78,8 @@ def main(): # alternatenames : alternatenames, comma separated varchar(4000) # latitude : latitude in decimal degrees (wgs84) # longitude : longitude in decimal degrees (wgs84) - # feature class : see http://www.geonames.org/export/codes.html, char(1) - # feature code : see http://www.geonames.org/export/codes.html, varchar(10) + # feature class : see https://www.geonames.org/export/codes.html, char(1) + # feature code : see https://www.geonames.org/export/codes.html, varchar(10) # country code : ISO-3166 2-letter country code, 2 characters # cc2 : alternate country codes, comma separated, ISO-3166 2-letter country code, 60 characters # admin1 code : fipscode (subject to change to iso code), isocode for the us and ch, see file admin1Codes.txt for display names of this code; varchar(20) @@ -89,7 +89,7 @@ def main(): # population : integer # elevation : in meters, integer # gtopo30 : average elevation of 30'x30' (ca 900mx900m) area in meters, integer - # timezone : the timezone id (see file http://download.geonames.org/export/dump/timeZones.txt) + # timezone : the timezone id (see file https://download.geonames.org/export/dump/timeZones.txt) # modification date : date of last modification in yyyy-MM-dd format # geonameid|name|asciiname|alternatenames|latitude|longitude|featureclass|featurecode|countrycode|cc2|admin1code|admin2code|admin3code|admin4code|population|elevation|gtopo30|timezone|modificationdate diff --git a/scripts/v.in.lines/v.in.lines.html b/scripts/v.in.lines/v.in.lines.html index 2dff0a8263f..86a87c6dbcb 100644 --- a/scripts/v.in.lines/v.in.lines.html +++ b/scripts/v.in.lines/v.in.lines.html @@ -5,7 +5,7 @@

    DESCRIPTION

    NOTES

    Input ASCII coordinates are simply a series of "x y" data points. -Lines are separated by a row containing "NaN NaN". +Lines are separated by a row containing "NaN NaN".

    The user can import 3D lines by providing 3 columns of data in the input stream and using the -z flag. diff --git a/scripts/v.in.mapgen/v.in.mapgen.html b/scripts/v.in.mapgen/v.in.mapgen.html index af0b4a51c06..52f874cfe77 100644 --- a/scripts/v.in.mapgen/v.in.mapgen.html +++ b/scripts/v.in.mapgen/v.in.mapgen.html @@ -7,9 +7,9 @@

    NOTES

    This module only imports data into vector lines.

    The user can get coastline data in Mapgen or Matlab format from NOAA's Coastline -Extractor at http://www.ngdc.noaa.gov/mgg/shorelines/shorelines.html. +Extractor at https://www.ngdc.noaa.gov/mgg/shorelines/shorelines.html.

    Matlab vector line maps are simply a series of "x y" data points. Lines -are separated by a row containing NaN NaN. +are separated by a row containing NaN NaN. Output from Matlab with this command:
    diff --git a/scripts/v.in.mapgen/v.in.mapgen.py b/scripts/v.in.mapgen/v.in.mapgen.py index efe7af1075d..e76486b75b4 100755 --- a/scripts/v.in.mapgen/v.in.mapgen.py +++ b/scripts/v.in.mapgen/v.in.mapgen.py @@ -106,8 +106,10 @@ def main(): if f[0].lower() == "nan": if points != []: outf.write("L %d 1\n" % len(points)) - for point in points: - outf.write(" %.15g %.15g %.15g\n" % tuple(map(float, point))) + outf.writelines( + " %.15g %.15g %.15g\n" % tuple(map(float, point)) + for point in points + ) outf.write(" 1 %d\n" % cat) cat += 1 points = [] @@ -134,8 +136,9 @@ def main(): if line[0] == "#": if points != []: outf.write("L %d 1\n" % len(points)) - for point in points: - outf.write(" %.15g %.15g\n" % tuple(map(float, point))) + outf.writelines( + " %.15g %.15g\n" % tuple(map(float, point)) for point in points + ) outf.write(" 1 %d\n" % cat) cat += 1 points = [] @@ -144,8 +147,9 @@ def main(): if points != []: outf.write("L %d 1\n" % len(points)) - for point in points: - outf.write(" %.15g %.15g\n" % tuple(map(float, point))) + outf.writelines( + " %.15g %.15g\n" % tuple(map(float, point)) for point in points + ) outf.write(" 1 %d\n" % cat) cat += 1 outf.close() diff --git a/scripts/v.in.wfs/v.in.wfs.html b/scripts/v.in.wfs/v.in.wfs.html index eaa96c13780..fbee44ea6eb 100644 --- a/scripts/v.in.wfs/v.in.wfs.html +++ b/scripts/v.in.wfs/v.in.wfs.html @@ -35,7 +35,7 @@

    WFS import with API key

    v.in.wfs -l url="$URL"
    -From that file we learn that the shipwreck layer is called "v:x633" +From that file we learn that the shipwreck layer is called "v:x633" and that EPSG code 4326 (LatLong WGS84) is a supported SRS for this data layer.
    diff --git a/scripts/v.in.wfs/v.in.wfs.py b/scripts/v.in.wfs/v.in.wfs.py
    index 2949949ad50..ead40698fbd 100755
    --- a/scripts/v.in.wfs/v.in.wfs.py
    +++ b/scripts/v.in.wfs/v.in.wfs.py
    @@ -227,7 +227,14 @@ def main():
                 grass.run_command("v.in.ogr", flags="o", input=tmpxml, output=out)
             grass.message(_("Vector map <%s> imported from WFS.") % out)
         except Exception:
    +        import xml.etree.ElementTree as ET
    +
             grass.message(_("WFS import failed"))
    +
    +        root = ET.parse(tmpxml).getroot()
    +        if "ServiceExceptionReport" in root.tag:
    +            se = root.find(root.tag[:-6])  # strip "Report" from the tag
    +            grass.message(se.text.strip())
         finally:
             try_remove(tmpxml)
     
    diff --git a/scripts/v.rast.stats/v.rast.stats.html b/scripts/v.rast.stats/v.rast.stats.html
    index 966f7ae1a00..e25f117454b 100644
    --- a/scripts/v.rast.stats/v.rast.stats.html
    +++ b/scripts/v.rast.stats/v.rast.stats.html
    @@ -24,14 +24,14 @@ 

    NOTES

    The script stops if a (prefixed) upload column is already present in the vector map attribute table, unless otherwise instructed with the -c continue flag. The column prefix will be separated from the statistic name -with an underscore. For example with a prefix of "elev" the sum -column will be named elev_sum. +with an underscore. For example with a prefix of "elev" the sum +column will be named elev_sum.

    If a DBF database is being used, note that column names are restricted by the DBF specification to 10 characters. Therefore it is advised to be economical in the use of the column prefix when using DBF as any additional characters will be chopped off. -

    If a MASK is present, it will be restored after the script finished. -The script changes temporarily to the resolution of the given raster map. +

    The script internally aligns the raster grid cells to the grid of the first +input raster map.

    If an area has several categories in the selected layer (equivalent to overlapping polygons in Simple Features), only one category will be kept during the rasterization process. Statistics for the skipped @@ -81,4 +81,4 @@

    SEE ALSO

    AUTHOR

    -Markus Neteler, CEA (for the EDEN EU/FP6 Project) +Markus Neteler, CEA (for the EDEN EU/FP6 Project) diff --git a/scripts/v.rast.stats/v.rast.stats.py b/scripts/v.rast.stats/v.rast.stats.py index 4096aa49caf..3f8b5c80ddd 100644 --- a/scripts/v.rast.stats/v.rast.stats.py +++ b/scripts/v.rast.stats/v.rast.stats.py @@ -156,8 +156,8 @@ def main(): # save current settings: gs.use_temp_region() - # Temporarily aligning region resolution to $RASTER resolution - # keep boundary settings + # Align region cells with the first input raster, + # keeping the (approximate) extent settings. gs.run_command("g.region", align=rasters[0]) # check if DBF driver used, in this case cut to 10 chars col names: diff --git a/scripts/v.what.vect/v.what.vect.html b/scripts/v.what.vect/v.what.vect.html index 0a4a88cca49..353431e3ce6 100644 --- a/scripts/v.what.vect/v.what.vect.html +++ b/scripts/v.what.vect/v.what.vect.html @@ -41,7 +41,7 @@

    EXAMPLES

    In this example, city names, population data and others from -Geonames.org country files are +Geonames.org country files are transferred to selected EU CORINE landuse/landcover classes ("Continuous urban fabric", 111, and "Discontinuous urban fabric", 112). Note: The example is in UTM projection to which the input maps have been diff --git a/scripts/wxpyimgview/wxpyimgview_gui.py b/scripts/wxpyimgview/wxpyimgview_gui.py index b0f89deee80..b5338dc0db8 100644 --- a/scripts/wxpyimgview/wxpyimgview_gui.py +++ b/scripts/wxpyimgview/wxpyimgview_gui.py @@ -117,12 +117,14 @@ def read_bmp_header(self, header): magic, bmfh, bmih = struct.unpack("2s12s40s10x", header) if gs.decode(magic) != "BM": - raise SyntaxError("Invalid magic number") + msg = "Invalid magic number" + raise SyntaxError(msg) size, res1, res2, hsize = struct.unpack("NOTES Setting the connection with t.connect will not test the connection for validity. Hence a database connection will not be established.

    -The connection values are stored in the mapset's VAR file. +The connection values are stored in the mapset's VAR file. The -d flag will set the default TGIS connection parameters. A SQLite database "tgis/sqlite.db" will be created in the current mapset directory. diff --git a/temporal/t.copy/t.copy.html b/temporal/t.copy/t.copy.html index 9be8115a1e2..290f7661e3a 100644 --- a/temporal/t.copy/t.copy.html +++ b/temporal/t.copy/t.copy.html @@ -37,9 +37,4 @@

    SEE ALSO

    AUTHOR

    -Markus Metz, mundialis - - +Markus Metz, mundialis diff --git a/temporal/t.merge/t.merge.html b/temporal/t.merge/t.merge.html index b694d1a3203..ff24d1603f9 100644 --- a/temporal/t.merge/t.merge.html +++ b/temporal/t.merge/t.merge.html @@ -1,7 +1,7 @@

    DESCRIPTION

    -This module is designed to register the maps of several input space -time datasets in a single output dataset. The datasets to merge can be +The t.merge module is designed to register the maps of several input +space time datasets in a single output dataset. The datasets to merge can be either space time raster, 3D raster or vector datasets and must have the same temporal type (absolute or relative).

    diff --git a/temporal/t.rast.accdetect/t.rast.accdetect.py b/temporal/t.rast.accdetect/t.rast.accdetect.py index 17433e06f3f..8d70bf255f2 100644 --- a/temporal/t.rast.accdetect/t.rast.accdetect.py +++ b/temporal/t.rast.accdetect/t.rast.accdetect.py @@ -402,7 +402,7 @@ def main(): prev_map = curr_map subexpr1 = "null()" subexpr3 = "%i" % (indicator_start) - elif i > 0 and i < num_maps - 1: + elif 0 < i < num_maps - 1: prev_map = occurrence_maps[map.next().get_id()].get_name() next_map = occurrence_maps[map.prev().get_id()].get_name() # In case the previous map is null() set null() or the start @@ -444,7 +444,7 @@ def main(): prev_map = curr_map subexpr1 = "null()" subexpr3 = "%i" % (indicator_start) - elif i > 0 and i < num_maps - 1: + elif 0 < i < num_maps - 1: prev_map = occurrence_maps[map.prev().get_id()].get_name() next_map = occurrence_maps[map.next().get_id()].get_name() # In case the previous map is null() set null() or the start diff --git a/temporal/t.rast.aggregate.ds/t.rast.aggregate.ds.py b/temporal/t.rast.aggregate.ds/t.rast.aggregate.ds.py index 99aa795d7cc..1a5cbfeea6d 100755 --- a/temporal/t.rast.aggregate.ds/t.rast.aggregate.ds.py +++ b/temporal/t.rast.aggregate.ds/t.rast.aggregate.ds.py @@ -144,10 +144,7 @@ def main(): if sampler_sp.temporal_extent.get_map_time() != "interval": dbif.close() gs.fatal( - _( - "All registered maps of the aggregation dataset " - "must have time intervals" - ) + _("All registered maps of the aggregation dataset must have time intervals") ) # We will create the strds later, but need to check here diff --git a/temporal/t.rast.algebra/t.rast.algebra.html b/temporal/t.rast.algebra/t.rast.algebra.html index 1944d63baa3..e5786d9578c 100644 --- a/temporal/t.rast.algebra/t.rast.algebra.html +++ b/temporal/t.rast.algebra/t.rast.algebra.html @@ -590,7 +590,7 @@

    SEE ALSO

    REFERENCES

    The use of this module requires the following software to be installed: -PLY(Python-Lex-Yacc) +PLY(Python-Lex-Yacc)

    diff --git a/temporal/t.rast.extract/t.rast.extract.html b/temporal/t.rast.extract/t.rast.extract.html
    index 1a3b6d84e5e..e7dcd7897c8 100644
    --- a/temporal/t.rast.extract/t.rast.extract.html
    +++ b/temporal/t.rast.extract/t.rast.extract.html
    @@ -22,7 +22,7 @@ 

    DESCRIPTION

    NOTES

    The r.mapcalc sub-expression should not contain the left side -"map =" of a full r.mapcalc expression, only the right +"map =" of a full r.mapcalc expression, only the right side, eg.: diff --git a/temporal/t.rast.gapfill/t.rast.gapfill.py b/temporal/t.rast.gapfill/t.rast.gapfill.py index a4d3b3a8c95..986ccd4373d 100755 --- a/temporal/t.rast.gapfill/t.rast.gapfill.py +++ b/temporal/t.rast.gapfill/t.rast.gapfill.py @@ -151,10 +151,7 @@ def main(): if len(map_.get_follows()) > 1: gs.warning( - _( - "More than one predecessor of the gap found. " - "Using the first found." - ) + _("More than one predecessor of the gap found. Using the first found.") ) # Interpolate the maps using parallel processing diff --git a/temporal/t.rast.import/testsuite/test_temporal_rast_import.py b/temporal/t.rast.import/testsuite/test_temporal_rast_import.py index 15e66058a56..4a0decbf82d 100644 --- a/temporal/t.rast.import/testsuite/test_temporal_rast_import.py +++ b/temporal/t.rast.import/testsuite/test_temporal_rast_import.py @@ -1,9 +1,3 @@ -""" -Created on Fri Feb 26 14:46:06 2016 - -@author: lucadelu -""" - """ Test t.rast.import diff --git a/temporal/t.rast.list/tests/t_rast_list_test.py b/temporal/t.rast.list/tests/t_rast_list_test.py index 86e539467cd..e2919652b23 100644 --- a/temporal/t.rast.list/tests/t_rast_list_test.py +++ b/temporal/t.rast.list/tests/t_rast_list_test.py @@ -289,6 +289,6 @@ def test_gran_json(space_time_raster_dataset): item["name"] in space_time_raster_dataset.raster_names or item["name"] is None ) - assert len(result["data"]) > len( - space_time_raster_dataset.raster_names - ), "There should be more entries because of finer granularity" + assert len(result["data"]) > len(space_time_raster_dataset.raster_names), ( + "There should be more entries because of finer granularity" + ) diff --git a/temporal/t.rast.mapcalc/t.rast.mapcalc.html b/temporal/t.rast.mapcalc/t.rast.mapcalc.html index afbe1d04422..dbef6373aff 100644 --- a/temporal/t.rast.mapcalc/t.rast.mapcalc.html +++ b/temporal/t.rast.mapcalc/t.rast.mapcalc.html @@ -128,11 +128,11 @@

    NOTES

    EXAMPLES

    The following command creates a new space time raster dataset -january_under_0 that will set to null all cells with +january_under_0 that will set to null all cells with temperature above zero in the January maps while keeping all the rest as in the original time series. This will change the maximum values of all January maps in the new STRDS as compared to the original one, -tempmean_monthly. +tempmean_monthly.
     t.rast.mapcalc input=tempmean_monthly output=january_under_0 basename=january_under_0 \
    diff --git a/temporal/t.rast.out.vtk/t.rast.out.vtk.html b/temporal/t.rast.out.vtk/t.rast.out.vtk.html
    index c0a000cf3e5..9746406f093 100644
    --- a/temporal/t.rast.out.vtk/t.rast.out.vtk.html
    +++ b/temporal/t.rast.out.vtk/t.rast.out.vtk.html
    @@ -1,6 +1,6 @@
     

    DESCRIPTION

    -This module exports all maps registered in a space time raster datasets +t.rast.out.vtk exports all maps registered in a space time raster datasets as VTK legacy files using a numerical numbering scheme. The VTK files can be visualized with any VTK based visualize. Our preferred tool is ParaView. The VTK legacy files are created using r.out.vtk. diff --git a/temporal/t.rast.univar/t.rast.univar.html b/temporal/t.rast.univar/t.rast.univar.html index 7ecc0ed3316..ee1f6940764 100644 --- a/temporal/t.rast.univar/t.rast.univar.html +++ b/temporal/t.rast.univar/t.rast.univar.html @@ -56,5 +56,5 @@

    SEE ALSO

    AUTHOR

    -Sören Gebbert, Thünen Institute of Climate-Smart Agriculture -Stefan Blumentrath, (Support for zones, parallel processing, and spatial relations) +Sören Gebbert, Thünen Institute of Climate-Smart Agriculture
    +Stefan Blumentrath (support for zones, parallel processing, and spatial relations) diff --git a/temporal/t.rast.what/t.rast.what.py b/temporal/t.rast.what/t.rast.what.py index 24190a6a716..a4d5af7939d 100755 --- a/temporal/t.rast.what/t.rast.what.py +++ b/temporal/t.rast.what/t.rast.what.py @@ -105,6 +105,7 @@ import copy import sys +from contextlib import nullcontext import grass.script as gs @@ -357,75 +358,71 @@ def one_point_per_row_output( output is of type: x,y,start,end,value """ # open the output file for writing - out_file = open(output, "w") if output != "-" else sys.stdout - - if write_header is True: - out_str = "" - if vcat: - out_str += "cat{sep}" - if site_input: - out_str += "x{sep}y{sep}site{sep}start{sep}end{sep}value\n" - else: - out_str += "x{sep}y{sep}start{sep}end{sep}value\n" - out_file.write(out_str.format(sep=separator)) - - for count in range(len(output_files)): - file_name = output_files[count] - gs.verbose(_("Transforming r.what output file %s") % (file_name)) - map_list = output_time_list[count] - in_file = open(file_name) - for line in in_file: - line = line.split(separator) + with open(output, "w") if output != "-" else nullcontext(sys.stdout) as out_file: + if write_header is True: + out_str = "" if vcat: - cat = line[0] - x = line[1] - y = line[2] - values = line[4:] - if site_input: - site = line[3] - values = line[5:] - + out_str += "cat{sep}" + if site_input: + out_str += "x{sep}y{sep}site{sep}start{sep}end{sep}value\n" else: - x = line[0] - y = line[1] - if site_input: - site = line[2] - values = line[3:] + out_str += "x{sep}y{sep}start{sep}end{sep}value\n" + out_file.write(out_str.format(sep=separator)) + + for count in range(len(output_files)): + file_name = output_files[count] + gs.verbose(_("Transforming r.what output file %s") % (file_name)) + map_list = output_time_list[count] + with open(file_name) as in_file: + for line in in_file: + line = line.split(separator) + if vcat: + cat = line[0] + x = line[1] + y = line[2] + values = line[4:] + if site_input: + site = line[3] + values = line[5:] - for i in range(len(values)): - start, end = map_list[i].get_temporal_extent_as_tuple() - 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" - % ( + else: + x = line[0] + y = line[1] + if site_input: + site = line[2] + values = line[3:] + + for i in range(len(values)): + start, end = map_list[i].get_temporal_extent_as_tuple() + 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" + % ( + { + "x": float(x), + "y": float(y), + "site_name": str(site), + "sep": separator, + } + ) + ) + else: + coor_string = "%(x)10.10f%(sep)s%(y)10.10f%(sep)s" % ( + {"x": float(x), "y": float(y), "sep": separator} + ) + time_string = "%(start)s%(sep)s%(end)s%(sep)s%(val)s\n" % ( { - "x": float(x), - "y": float(y), - "site_name": str(site), + "start": str(start), + "end": str(end), + "val": (values[i].strip()), "sep": separator, } ) - ) - else: - coor_string = "%(x)10.10f%(sep)s%(y)10.10f%(sep)s" % ( - {"x": float(x), "y": float(y), "sep": separator} - ) - time_string = "%(start)s%(sep)s%(end)s%(sep)s%(val)s\n" % ( - { - "start": str(start), - "end": str(end), - "val": (values[i].strip()), - "sep": separator, - } - ) - - out_file.write(cat_str + coor_string + time_string) - - in_file.close() - if out_file is not sys.stdout: - out_file.close() + out_file.write(cat_str + coor_string + time_string) ############################################################################ @@ -441,77 +438,73 @@ def one_point_per_col_output( Each row represents a single raster map, hence a single time stamp """ # open the output file for writing - out_file = open(output, "w") if output != "-" else sys.stdout first = True - for count in range(len(output_files)): - file_name = output_files[count] - gs.verbose(_("Transforming r.what output file %s") % (file_name)) - in_file = open(file_name) - lines = in_file.readlines() - - matrix = [] - for line in lines: - matrix.append(line.split(separator)) + with open(output, "w") if output != "-" else nullcontext(sys.stdout) as out_file: + for count in range(len(output_files)): + file_name = output_files[count] + gs.verbose(_("Transforming r.what output file %s") % (file_name)) + with open(file_name) as in_file: + lines = in_file.readlines() - num_cols = len(matrix[0]) + matrix = [] + for line in lines: + matrix.append(line.split(separator)) - if first is True: - if write_header is True: - out_str = "start%(sep)send" % ({"sep": separator}) - - # Define different separator for coordinates and sites - coor_sep = ";" if separator == "," else "," + num_cols = len(matrix[0]) - for row in matrix: - if vcat: - cat = row[0] - x = row[1] - y = row[2] - out_str += ( - "{sep}{cat}{csep}{x:10.10f}{csep}" - "{y:10.10f}".format( - cat=cat, - x=float(x), - y=float(y), - sep=separator, - csep=coor_sep, + if first is True: + if write_header is True: + out_str = "start%(sep)send" % ({"sep": separator}) + + # Define different separator for coordinates and sites + coor_sep = ";" if separator == "," else "," + + for row in matrix: + if vcat: + cat = row[0] + x = row[1] + y = row[2] + out_str += ( + "{sep}{cat}{csep}{x:10.10f}{csep}{y:10.10f}".format( + cat=cat, + x=float(x), + y=float(y), + sep=separator, + csep=coor_sep, + ) ) - ) - if site_input: - site = row[3] - out_str += "{sep}{site}".format(sep=coor_sep, site=site) - else: - x = row[0] - y = row[1] - out_str += "{sep}{x:10.10f}{csep}{y:10.10f}".format( - x=float(x), y=float(y), sep=separator, csep=coor_sep - ) - if site_input: - site = row[2] - out_str += "{sep}{site}".format(sep=coor_sep, site=site) + if site_input: + site = row[3] + out_str += "{sep}{site}".format(sep=coor_sep, site=site) + else: + x = row[0] + y = row[1] + out_str += "{sep}{x:10.10f}{csep}{y:10.10f}".format( + x=float(x), y=float(y), sep=separator, csep=coor_sep + ) + if site_input: + site = row[2] + out_str += "{sep}{site}".format(sep=coor_sep, site=site) - out_file.write(out_str + "\n") + out_file.write(out_str + "\n") - first = False + first = False - 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" % ( - {"start": str(start), "end": str(end), "sep": separator} - ) - out_file.write(time_string) - for row in range(len(matrix)): - value = matrix[row][col + ncol] - out_file.write( - "%(sep)s%(value)s" % ({"sep": separator, "value": value.strip()}) + 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" % ( + {"start": str(start), "end": str(end), "sep": separator} ) - out_file.write("\n") - - in_file.close() - if out_file is not sys.stdout: - out_file.close() + out_file.write(time_string) + for row in range(len(matrix)): + value = matrix[row][col + ncol] + out_file.write( + "%(sep)s%(value)s" + % ({"sep": separator, "value": value.strip()}) + ) + out_file.write("\n") ############################################################################ @@ -528,7 +521,6 @@ def one_point_per_timerow_output( 3730731.49590371|5642483.51236521|6|8|7|7 3581249.04638104|5634411.97526282|5|8|7|7 """ # noqa: E501 - out_file = open(output, "w") if output != "-" else sys.stdout matrix = [] header = "" @@ -538,7 +530,6 @@ def one_point_per_timerow_output( file_name = output_files[count] gs.verbose("Transforming r.what output file %s" % (file_name)) map_list = output_time_list[count] - in_file = open(file_name) if write_header: if first is True: @@ -554,7 +545,8 @@ def one_point_per_timerow_output( ) header += time_string - lines = in_file.readlines() + with open(file_name) as in_file: + lines = in_file.readlines() for i in range(len(lines)): cols = lines[i].split(separator) @@ -574,26 +566,23 @@ def one_point_per_timerow_output( first = False - in_file.close() - - if write_header: - out_file.write(header + "\n") + with open(output, "w") if output != "-" else nullcontext(sys.stdout) as out_file: + if write_header: + out_file.write(header + "\n") - gs.verbose(_("Writing the output file <%s>") % (output)) - for row in matrix: - first = True - for col in row: - value = col.strip() + gs.verbose(_("Writing the output file <%s>") % (output)) + for row in matrix: + first = True + for col in row: + value = col.strip() - if first is False: - out_file.write("%s" % (separator)) - out_file.write(value) + if first is False: + out_file.write("%s" % (separator)) + out_file.write(value) - first = False + first = False - out_file.write("\n") - if out_file is not sys.stdout: - out_file.close() + out_file.write("\n") ############################################################################ diff --git a/temporal/t.rast3d.algebra/t.rast3d.algebra.html b/temporal/t.rast3d.algebra/t.rast3d.algebra.html index 35680946f8d..29ec831cf68 100644 --- a/temporal/t.rast3d.algebra/t.rast3d.algebra.html +++ b/temporal/t.rast3d.algebra/t.rast3d.algebra.html @@ -8,7 +8,7 @@

    NOTES

    The module expects an expression as input parameter in the following form:

    -"result = expression" +"result = expression"

    The statement structure is exact the same as of t.rast.algebra, @@ -17,7 +17,7 @@

    NOTES

    REFERENCES

    -PLY(Python-Lex-Yacc) +PLY(Python-Lex-Yacc)

    SEE ALSO

    diff --git a/temporal/t.rast3d.univar/t.rast3d.univar.html b/temporal/t.rast3d.univar/t.rast3d.univar.html index d9ccd43e154..a46f90507fb 100644 --- a/temporal/t.rast3d.univar/t.rast3d.univar.html +++ b/temporal/t.rast3d.univar/t.rast3d.univar.html @@ -15,5 +15,5 @@

    SEE ALSO

    AUTHOR

    -Sören Gebbert, Thünen Institute of Climate-Smart Agriculture -Stefan Blumentrath, (Support for zones) +Sören Gebbert, Thünen Institute of Climate-Smart Agriculture
    +Stefan Blumentrath (support for zones) diff --git a/temporal/t.register/t.register.html b/temporal/t.register/t.register.html index ca6c3339db3..31e11fb5962 100644 --- a/temporal/t.register/t.register.html +++ b/temporal/t.register/t.register.html @@ -275,9 +275,7 @@

    Importing and registering ECA&D climatic data

    Europe based on ECA&D information. Download and decompress mean temperature data from: -here -by accepting their -Terms of use. +here.
     # import E-OBS V12 into a lat-long project (alternatively, use r.external)
    diff --git a/temporal/t.remove/t.remove.html b/temporal/t.remove/t.remove.html
    index 3d9a9be56f7..09aeb53cefc 100644
    --- a/temporal/t.remove/t.remove.html
    +++ b/temporal/t.remove/t.remove.html
    @@ -3,7 +3,7 @@ 

    DESCRIPTION

    The module t.remove removes space time datasets (STRDS, STR3DS, STVDS) from the temporal database. In other words, by default it deletes the relevant database entries. It can also unregister maps from temporal -database using the recursive mode -r (recursive) +database using the recursive mode -r (recursive).

    Optionally, also the raster, 3D raster and vector maps of the space time datasets can be removed from the current mapset using the -d (delete) @@ -22,23 +22,23 @@

    EXAMPLE

    raster maps) are removed:
    -#Create new and empty STRDS
    +# Create new and empty STRDS
     t.create output=precip_months_sum semantictype=mean \
       title="Monthly sum of precipitation" \
       description="Monthly sum of precipitation for the \
       North Carolina sample data"
     
    -#Register maps from sample dataset (selecting a subset with g.list)
    +# Register maps from sample dataset (selecting a subset with g.list)
     t.register -i type=raster input=precip_months_sum \
       maps=$(g.list type=raster pattern="201*_precip" separator=comma) \
       start="2010-01-01" increment="1 months"
     
    -#Create some new data by aggregating with 1 years granularity
    +# Create some new data by aggregating with 1 years granularity
     t.rast.aggregate input=precip_months_sum \
    -  output=precip_years_sum basename=precip_years_sum granularity="1 \
    -  years" method=sum
    +  output=precip_years_sum basename=precip_years_sum \
    +  granularity="1 years" method=sum
     
    -#Remove all newly produced data:
    +# Remove all newly produced data:
     
     # a) the aggregated STRDS with 1 years granularity along with its raster maps
     t.remove -df type=strds input=precip_years_sum
    diff --git a/temporal/t.remove/t.remove.py b/temporal/t.remove/t.remove.py
    index 91fa58dde7a..a5bb3d85fd8 100755
    --- a/temporal/t.remove/t.remove.py
    +++ b/temporal/t.remove/t.remove.py
    @@ -100,19 +100,17 @@ def main():
             else:
                 dataset_list = tuple(datasets.split(","))
     
    -    # Read the dataset list from file
         if file:
    -        fd = open(file)
    -
             line = True
    -        while True:
    -            line = fd.readline()
    -            if not line:
    -                break
    -
    -            line_list = line.split("\n")
    -            dataset_name = line_list[0]
    -            dataset_list.append(dataset_name)
    +        with open(file) as fd:
    +            while True:
    +                line = fd.readline()
    +                if not line:
    +                    break
    +
    +                line_list = line.split("\n")
    +                dataset_name = line_list[0]
    +                dataset_list.append(dataset_name)
     
         statement = ""
     
    diff --git a/temporal/t.rename/t.rename.html b/temporal/t.rename/t.rename.html
    index f7270c0741c..98ec45270bd 100644
    --- a/temporal/t.rename/t.rename.html
    +++ b/temporal/t.rename/t.rename.html
    @@ -1,7 +1,8 @@
     

    DESCRIPTION

    -This module renames space time datasets of different types (STRDS, STVDS, STR3DS) -and updates the space time dataset register entries of the registered maps. +The t.rename module renames space time datasets of different +types (STRDS, STVDS, STR3DS) and updates the space time dataset register +entries of the registered maps.

    NOTES

    @@ -12,7 +13,7 @@

    EXAMPLE

    A new vector space time dataset will be created, renamed and in the end removed
    -#Create new and empty STVDS
    +# Create new and empty STVDS
     t.create type=stvds output=toberenamed semantictype=mean \
       title="Example to rename" \
       description="This is an example just to show how rename"
    diff --git a/temporal/t.sample/t.sample.html b/temporal/t.sample/t.sample.html
    index 14003800c15..0ae2adb1d84 100644
    --- a/temporal/t.sample/t.sample.html
    +++ b/temporal/t.sample/t.sample.html
    @@ -1,6 +1,6 @@
     

    DESCRIPTION

    -The purpose of this module is to compute and to show spatio-temporal +The purpose of t.sample is to compute and to show spatio-temporal relations between space time datasets of different type. Several input space time datasets are sampled by a sample space time dataset using temporal topological relations. The types of the input space time diff --git a/temporal/t.select/t.select.html b/temporal/t.select/t.select.html index 04b74ff2b72..bef3af167e3 100644 --- a/temporal/t.select/t.select.html +++ b/temporal/t.select/t.select.html @@ -6,7 +6,7 @@

    DESCRIPTION

    PROGRAM USE

    The module expects an expression as input parameter in the following form:

    -"result = expression" +"result = expression"

    The statement structure is similar to r.mapcalc, see r.mapcalc. @@ -359,7 +359,7 @@

    EXAMPLES

    REFERENCES

    -PLY(Python-Lex-Yacc) +PLY(Python-Lex-Yacc)

    Gebbert, S., Leppelt, T., Pebesma, E., 2019. A topology based spatio-temporal map algebra for big data analysis. Data 4, 86. https://doi.org/10.3390/data4020086 diff --git a/temporal/t.support/t.support.html b/temporal/t.support/t.support.html index be9e45b9d42..e7958797cb5 100644 --- a/temporal/t.support/t.support.html +++ b/temporal/t.support/t.support.html @@ -1,6 +1,7 @@

    DESCRIPTION

    -This module is dedicated to modify and update the metadata of a space time dataset. +The t.support module is dedicated to modify and update the metadata of +a space time dataset.

    The title, description and the semantic type can be modified.

    diff --git a/temporal/t.unregister/t.unregister.html b/temporal/t.unregister/t.unregister.html index c6f59a9f1fc..b54782e592d 100644 --- a/temporal/t.unregister/t.unregister.html +++ b/temporal/t.unregister/t.unregister.html @@ -1,7 +1,7 @@

    DESCRIPTION

    -This module is designed to unregister raster, 3D raster and vector map -layers from space time datasets and the temporal database. +The t.unregister module is designed to unregister raster, 3D raster +and vector map layers from space time datasets and the temporal database.

    Map layer that should be unregistered from the temporal database can be diff --git a/temporal/t.unregister/t.unregister.py b/temporal/t.unregister/t.unregister.py index cacfe579a7b..a67ef91adb5 100755 --- a/temporal/t.unregister/t.unregister.py +++ b/temporal/t.unregister/t.unregister.py @@ -103,17 +103,16 @@ def main(): # Read the map list from file if file: - fd = open(file) - - line = True - while True: - line = fd.readline() - if not line: - break - - mapname = line.strip() - mapid = dummy.build_id(mapname, mapset) - maplist.append(mapid) + with open(file) as fd: + line = True + while True: + line = fd.readline() + if not line: + break + + mapname = line.strip() + mapid = dummy.build_id(mapname, mapset) + maplist.append(mapid) num_maps = len(maplist) update_dict = {} diff --git a/temporal/t.upgrade/t.upgrade.html b/temporal/t.upgrade/t.upgrade.html index 5b0517a001a..1fd6e4986ca 100644 --- a/temporal/t.upgrade/t.upgrade.html +++ b/temporal/t.upgrade/t.upgrade.html @@ -1,7 +1,7 @@

    DESCRIPTION

    -This module upgrades the temporal database in the current mapset -from version 2 (default in GRASS 7) to 3 (default in GRASS 8). +The t.upgrade module upgrades the temporal database in the current +mapset from version 2 (default in GRASS 7) to 3 (default in GRASS 8). The version 3 introduces a semantic label support, see i.band.library for details. diff --git a/temporal/t.vect.algebra/t.vect.algebra.html b/temporal/t.vect.algebra/t.vect.algebra.html index 89453068a61..df91dfe0d62 100644 --- a/temporal/t.vect.algebra/t.vect.algebra.html +++ b/temporal/t.vect.algebra/t.vect.algebra.html @@ -442,7 +442,7 @@

    Examples:

    REFERENCES

    -PLY(Python-Lex-Yacc) +PLY(Python-Lex-Yacc)

    SEE ALSO

    diff --git a/temporal/t.vect.extract/t.vect.extract.html b/temporal/t.vect.extract/t.vect.extract.html index c6b7716cf02..3904cd6800b 100644 --- a/temporal/t.vect.extract/t.vect.extract.html +++ b/temporal/t.vect.extract/t.vect.extract.html @@ -7,7 +7,7 @@

    DESCRIPTION

    EXAMPLE

    In the following example a new space time vector dataset will be create -with all the data later than 2000: +with all the data later than the year 2000:
     t.vect.extract input=shoreline where="start_time > 2000" \
    diff --git a/temporal/t.vect.list/t.vect.list.html b/temporal/t.vect.list/t.vect.list.html
    index 0e7211671a6..4b33bbc3626 100644
    --- a/temporal/t.vect.list/t.vect.list.html
    +++ b/temporal/t.vect.list/t.vect.list.html
    @@ -2,7 +2,7 @@ 

    DESCRIPTION

    -This module provides the same functionality as +The t.vect.list module provides the same functionality as t.rast.list, the only difference is the vector map layer metadata. diff --git a/temporal/t.vect.observe.strds/t.vect.observe.strds.html b/temporal/t.vect.observe.strds/t.vect.observe.strds.html index e1ca0a6c6c3..c5e923af7e2 100644 --- a/temporal/t.vect.observe.strds/t.vect.observe.strds.html +++ b/temporal/t.vect.observe.strds/t.vect.observe.strds.html @@ -94,7 +94,7 @@

    EXAMPLE

    +----------------------------------------------------------------------------+ | Name: precip_stations_monthly | | Mapset: climate_2009_2012 | - | Project: nc_spm_temporal_workshop | + | Project: nc_spm_temporal_workshop | | Database: /grassdata | | Title: North Carolina 30 year precipitation normals (3D) | | Map scale: 1:1 | diff --git a/utils/create_python_init_file.py b/utils/create_python_init_file.py index ff4b0f18255..8d045b9924b 100755 --- a/utils/create_python_init_file.py +++ b/utils/create_python_init_file.py @@ -14,7 +14,6 @@ @author Vaclav Petras """ - import os import sys import glob @@ -32,15 +31,10 @@ def main(path): continue modules.append(os.path.splitext(os.path.basename(f))[0]) - fd = open(os.path.join(path, "__init__.py"), "w") - try: + with open(os.path.join(path, "__init__.py"), "w") as fd: fd.write("all = [%s" % os.linesep) - for m in modules: - fd.write(" '%s',%s" % (m, os.linesep)) + fd.writelines(" '%s',%s" % (m, os.linesep) for m in modules) fd.write(" ]%s" % os.linesep) - finally: - fd.close() - return 0 diff --git a/utils/fix_typos.sh b/utils/fix_typos.sh index 68185b6b897..dab54f107a1 100755 --- a/utils/fix_typos.sh +++ b/utils/fix_typos.sh @@ -57,7 +57,7 @@ EXCLUDED_FILES="*/.svn*,configure,config.status,config.sub,*/autom4te.cache/*" EXCLUDED_FILES="$EXCLUDED_FILES,*/lib/cdhc/doc/goodness.ps,*/lib/cdhc/doc/goodness.tex,*/macosx/pkg/resources/ReadMe.rtf" EXCLUDED_FILES="$EXCLUDED_FILES,*/lib/gis/FIPS.code,*/lib/gis/projection,*/lib/proj/parms.table,*/lib/proj/units.table,*/lib/proj/desc.table" EXCLUDED_FILES="$EXCLUDED_FILES,*/locale/po/*.po" -EXCLUDED_FILES="$EXCLUDED_FILES,*/doc/notebooks/*.ipynb,*/*/*/*.ipynb" +EXCLUDED_FILES="$EXCLUDED_FILES,*/*/*/*.ipynb" EXCLUDED_FILES="$EXCLUDED_FILES,*/doc/*.svg,*/gui/icons/grass/*.svg,*/gui/images/*.svg,*/macosx/app/*.svg,*/man/*.svg,*/raster/*/*.svg" EXCLUDED_FILES="$EXCLUDED_FILES,*/fix_typos/*,fix_typos.sh,*.eps,geopackage_aspatial.html" EXCLUDED_FILES="$EXCLUDED_FILES,PROVENANCE.TXT,libtool,ltmain.sh,libtool.m4" diff --git a/utils/g.html2man/g.html2man.py b/utils/g.html2man/g.html2man.py index 46a749ae5d3..f8d5b0a89b4 100755 --- a/utils/g.html2man/g.html2man.py +++ b/utils/g.html2man/g.html2man.py @@ -26,18 +26,17 @@ def fix(content): def main(): # parse HTML infile = sys.argv[1] - inf = open(infile) - p = HTMLParser(entities) - for n, line in enumerate(inf): - try: - p.feed(line) - except Exception as err: - sys.stderr.write( - "%s:%d:0: Error (%s): %s\n" % (infile, n + 1, repr(err), line) - ) - sys.exit(1) - p.close() - inf.close() + with open(infile) as inf: + p = HTMLParser(entities) + for n, line in enumerate(inf): + try: + p.feed(line) + except Exception as err: + sys.stderr.write( + "%s:%d:0: Error (%s): %s\n" % (infile, n + 1, repr(err), line) + ) + sys.exit(1) + p.close() # generate groff sf = StringIO() diff --git a/utils/g.html2man/htmltags.txt b/utils/g.html2man/htmltags.txt deleted file mode 100644 index 1ecbb475c13..00000000000 --- a/utils/g.html2man/htmltags.txt +++ /dev/null @@ -1,28 +0,0 @@ -List of tags supported by g.html2man - - -<HEADER> -<HEAD> -<BODY> -<BLINK> -<A HREF="#toc"></A> -<PRE> -<IMG SRC=> -<A NAME="toc"> -<HR> -<H2> -<H3> -<BR> -<BR> -<DT> -<DD> -<LI> -<UL> -<OL> -<B> -<I> -<P> -  - -Hackish: -<table> diff --git a/utils/g.html2man/rest.py b/utils/g.html2man/rest.py index c320f18e397..9f828a78808 100644 --- a/utils/g.html2man/rest.py +++ b/utils/g.html2man/rest.py @@ -20,11 +20,13 @@ def find(node, tag, attr=None, val=None): if isinstance(node, tuple): node = node[2] if not isinstance(node, list): - raise ValueError("child not found") + msg = "child not found" + raise ValueError(msg) for child in node: if match(child, tag, attr, val): return child - raise ValueError("child not found") + msg = "child not found" + raise ValueError(msg) children = itemgetter(2) diff --git a/utils/generate_release_notes.py b/utils/generate_release_notes.py index c8a22b01d86..4ad19fdb060 100755 --- a/utils/generate_release_notes.py +++ b/utils/generate_release_notes.py @@ -125,20 +125,20 @@ def print_by_category(changes, categories, file=None): def binder_badge(tag): """Get mybinder Binder badge from a given tag, hash, or branch""" binder_image_url = "https://mybinder.org/badge_logo.svg" - binder_url = f"https://mybinder.org/v2/gh/OSGeo/grass/{tag}?urlpath=lab%2Ftree%2Fdoc%2Fnotebooks%2Fjupyter_example.ipynb" # noqa + binder_url = f"https://mybinder.org/v2/gh/OSGeo/grass/{tag}?urlpath=lab%2Ftree%2Fdoc%2Fexamples%2Fnotebooks%2Fjupyter_example.ipynb" # noqa return f"[![Binder]({binder_image_url})]({binder_url})" def print_support(file=None): url = "https://opencollective.com/grass/tiers/supporter/all.json" - response = requests.get(url=url) + response = requests.get(url=url, timeout=7) data = response.json() if data: print_section_heading_3("Monthly Financial Supporters", file=file) random.shuffle(data) supporters = [] for member in data: - supporters.append(f"""[{member['name']}]({member['profile']})""") + supporters.append(f"""[{member["name"]}]({member["profile"]})""") print(", ".join(supporters)) print() @@ -263,7 +263,8 @@ def notes_from_git_log(start_tag, end_tag, categories, exclude): ).stdout commits = yaml.safe_load(text) if not commits: - raise RuntimeError("No commits retrieved from git log (try different tags)") + msg = "No commits retrieved from git log (try different tags)" + raise RuntimeError(msg) svn_name_by_git_author = csv_to_dict( CONFIG_DIRECTORY / "svn_name_git_author.csv", diff --git a/utils/md_isvalid.py b/utils/md_isvalid.py index 8b963bb953b..0eb9c228376 100644 --- a/utils/md_isvalid.py +++ b/utils/md_isvalid.py @@ -14,8 +14,8 @@ def check_md(filename): - p = subprocess.Popen(["mdl", filename]) - p.wait() + with subprocess.Popen(["mdl", filename]) as p: + p.wait() def print_line(): @@ -27,20 +27,18 @@ def check_module(module): print(module) tmp_file = gs.tempfile() with open(tmp_file, "w") as fp: - p = subprocess.Popen([module, "--md-description"], stdout=fp) - p.wait() - - p = subprocess.Popen( + with subprocess.Popen([module, "--md-description"], stdout=fp) as p: + p.wait() + with subprocess.Popen( [ "mdl", "--style", os.path.join(os.path.dirname(__file__), "mdl_style.rb"), fp.name, ] - ) - p.wait() - - return p.returncode + ) as p: + p.wait() + return p.returncode if __name__ == "__main__": diff --git a/utils/mkhtml.py b/utils/mkhtml.py index c01ab0534cb..0864356889c 100644 --- a/utils/mkhtml.py +++ b/utils/mkhtml.py @@ -7,7 +7,7 @@ # Glynn Clements # Martin Landa <landa.martin gmail.com> # PURPOSE: Create HTML manual page snippets -# COPYRIGHT: (C) 2007-2024 by Glynn Clements +# COPYRIGHT: (C) 2007-2025 by Glynn Clements # and the GRASS Development Team # # This program is free software under the GNU General diff --git a/utils/mkrest.py b/utils/mkrest.py index 6270b78c6b2..1cc51c72b8a 100755 --- a/utils/mkrest.py +++ b/utils/mkrest.py @@ -19,6 +19,7 @@ import re import subprocess from datetime import datetime +from pathlib import Path pgm = sys.argv[1] year = sys.argv[2] if len(sys.argv) > 1 else str(datetime.now().year) @@ -47,10 +48,7 @@ def read_file(name): try: - f = open(name, "rb") - s = f.read() - f.close() - return s + return Path(name).read_bytes() except OSError: return "" @@ -79,8 +77,9 @@ def read_file(name): sys.stdout.write(tmp_data) arguments = ["pandoc", "-s", "-r", "html", src_file, "-w", "rst"] -process = subprocess.Popen(arguments, stdout=subprocess.PIPE) -html_text = process.communicate()[0] +with subprocess.Popen(arguments, stdout=subprocess.PIPE) as process: + html_text = process.communicate()[0] + if html_text: for k, v in replacement.iteritems(): html_text = html_text.replace(k, v) diff --git a/utils/ppmrotate.py b/utils/ppmrotate.py index cef32eba0f4..28bfee13141 100755 --- a/utils/ppmrotate.py +++ b/utils/ppmrotate.py @@ -17,6 +17,8 @@ import os import atexit import array +from pathlib import Path + import grass.script as gs tmp_img = None @@ -36,10 +38,8 @@ def cleanup(): def read_ppm(src): global width, height + text = Path(src).read_bytes() - fh = open(src, "rb") - text = fh.read() - fh.close() i = 0 j = text.find("\n", i) if text[i:j] != "P6": @@ -53,7 +53,8 @@ def read_ppm(src): j = text.find("\n", i) maxval = text[i:j] if int(maxval) != 255: - raise OSError("Max value in image != 255") + msg = "Max value in image != 255" + raise OSError(msg) i = j + 1 return array.array("B", text[i:]) @@ -61,10 +62,9 @@ def read_ppm(src): def write_ppm(dst, data): w = height h = width - fh = open(dst, "wb") - fh.write("P6\n%d %d\n%d\n" % (w, h, 255)) - data.tofile(fh) - fh.close() + with open(dst, "wb") as fh: + fh.write("P6\n%d %d\n%d\n" % (w, h, 255)) + data.tofile(fh) def rotate_ppm(srcd): @@ -91,9 +91,8 @@ def ppmtopng(dst, src): if gs.find_program("g.ppmtopng", "--help"): gs.run_command("g.ppmtopng", input=src, output=dst, quiet=True) elif gs.find_program("pnmtopng"): - fh = open(dst, "wb") - gs.call(["pnmtopng", src], stdout=fh) - fh.close() + with open(dst, "wb") as fh: + gs.call(["pnmtopng", src], stdout=fh) elif gs.find_program("convert"): gs.call(["convert", src, dst]) else: diff --git a/utils/pylintrc.txt b/utils/pylintrc.txt deleted file mode 100644 index 2b812c4ace2..00000000000 --- a/utils/pylintrc.txt +++ /dev/null @@ -1,287 +0,0 @@ -# This is a configuration file for pylint. - -# (C) 2011-2013 by 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. - -# author: Vaclav Petras <wenzeslaus gmail.com> - -# This configuration should work for all Python library, Python scripts and -# wxPython GUI. -# Example for gui/wxpython: -# pylint --rcfile=../../utils/pylintrc.txt -f parseable -r n -i y lmgr/ - -# A lot of errors need to be ignored now to avoid a large number of messages. -# Files need to be fixed one by one (partially without this configuration). - -# Save this file ~/.pylintrc on Linux - - -[MASTER] - -# Specify a configuration file. -#rcfile= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -#init-hook= - -# Profiled execution. -profile=no - -# Add files or directories to the blacklist. They should be base names, not -# paths. -ignore=CVS - -# Pickle collected data for later comparisons. -persistent=no - -# List of plugins (as comma separated values of python modules names) to load, -# usually to register additional checkers. -load-plugins= - - -[MESSAGES CONTROL] - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time. -#enable= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifier separated by comma (,) or put this option -# multiple time (only on the command line, not in the configuration file where -# it should appear only once). -disable=E1103,W0141,W0201,W0311,W0312,W0612,W0613,W0614,W0622,R0201,R0901,R0904,C0103,C0111,C0322,C0323,C0324,F0401 - -# E1103: caused probably by wxPython -# W0613: unused arg -> solve somewhere here using event -# C0103: names not defined -# W0201: define methods used for init -# W0622: Redefining built-in %r: needs complex solution - -# E1101: %s %r has no %r member: why? -# R0201: can be solved -# F0401: something is possible to import only in grass - - -[REPORTS] - -# Set the output format. Available formats are text, parseable, colorized, msvs -# (visual studio) and html -output-format=text - -# Include message's id in output -include-ids=no - -# Put messages in a separate file for each module / package specified on the -# command line instead of printing them on stdout. Reports (if any) will be -# written in a file name "pylint_global.[txt|html]". -files-output=no - -# Tells whether to display a full report or only the messages -reports=yes - -# Python expression which should return a note less than 10 (10 is the highest -# note). You have access to the variables errors warning, statement which -# respectively contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Add a comment according to your evaluation note. This is used by the global -# evaluation report (RP0004). -comment=no - - -[VARIABLES] - -# Tells whether we should check for unused import in __init__ files. -init-import=yes - -# A regular expression matching the beginning of the name of dummy variables -# (i.e. not used). -dummy-variables-rgx=dummy|unused - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -additional-builtins=_ - - -[BASIC] - -# Required attributes for module, separated by a comma -required-attributes= - -# List of builtins function names that should not be used, separated by a comma -bad-functions=map,filter,apply,input - -# Regular expression which should only match correct module names -module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - -# Regular expression which should only match correct module level names -const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - -# Regular expression which should only match correct class names -class-rgx=[A-Z_][a-zA-Z0-9]+$ - -# Regular expression which should only match correct function names -function-rgx=[a-z_][a-zA-Z0-9_]{2,30}$ - -# Regular expression which should only match correct method names -method-rgx=[a-zA-Z_][a-zA-Z0-9_]{2,30}$ - -# Regular expression which should only match correct instance attribute names -attr-rgx=[a-z_][a-zA-Z0-9_]{2,30}$ - -# Regular expression which should only match correct argument names -argument-rgx=[a-z_][a-zA-Z0-9_]{2,30}$ - -# Regular expression which should only match correct variable names -variable-rgx=[a-z_][a-zA-Z0-9_]{2,30}$|[a-z] - -# Regular expression which should only match correct list comprehension / -# generator expression variable names -inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ - -# Good variable names which should always be accepted, separated by a comma -good-names=i,j,_,x,y,z,N,E,S,W,id - -# Bad variable names which should always be refused, separated by a comma -bad-names=foo,bar,baz,toto,tutu,tata - -# Regular expression which should only match functions or classes name which do -# not require a docstring -no-docstring-rgx=__.*__ - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=FIXME,TODO,\\todo,@todo - -# general regexp for convention, warning etc. would be great - -[TYPECHECK] - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# List of classes names for which member attributes should not be checked -# (useful for classes with attributes dynamically set). -ignored-classes=SQLObject - -# When zope mode is activated, add a predefined set of Zope acquired attributes -# to generated-members. -zope=no - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E0201 when accessed. Python regular -# expressions are accepted. -generated-members=REQUEST,acl_users,aq_parent - - -[FORMAT] - -# Maximum number of characters on a single line. -# Increased form 80. -max-line-length=150 - -# Maximum number of lines in a module -# Increased from 1000. -max-module-lines=2000 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - - -[SIMILARITIES] - -# Minimum lines number of a similarity. -min-similarity-lines=4 - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - - -[DESIGN] - -# Maximum number of arguments for function / method -# Increased from 5. -max-args=10 - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore -ignored-argument-names=_.*|event - -# Maximum number of locals for function / method body -# Changed from 15. -max-locals=20 - -# Maximum number of return / yield for function / method body -max-returns=6 - -# Maximum number of branch for function / method body -# Changed from 12. -max-branchs=20 - -# Maximum number of statements in function / method body -max-statements=50 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of attributes for a class (see R0902). -# Changed from 7. -max-attributes=10 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=2 - -# Maximum number of public methods for a class (see R0904). -# Increased from 20 -max-public-methods=30 - - -[IMPORTS] - -# Deprecated modules which should not be used, separated by a comma -deprecated-modules=regsub,string,TERMIOS,Bastion,rexec - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled) -import-graph= - -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled) -ext-import-graph= - -# Create a graph of internal dependencies in the given file (report RP0402 must -# not be disabled) -int-import-graph= - - -[CLASSES] - -# List of interface methods to ignore, separated by a comma. This is used for -# instance to not check methods defines in Zope's Interface base class. -ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__,__new__,setUp - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "Exception" -overgeneral-exceptions=Exception diff --git a/utils/release.yml b/utils/release.yml index eb601419274..c0d9cc17400 100644 --- a/utils/release.yml +++ b/utils/release.yml @@ -6,7 +6,7 @@ notes: example: 'r.slope.aspect:' - title: Graphical User Interface - regexp: '(wxGUI.*|gui|GUI): ' + regexp: '(wxGUI.*|gui|GUI)(\(\w[\w.-]*\))?: ' example: 'wxGUI:' - title: Python @@ -14,7 +14,7 @@ notes: example: 'grass.script:' - title: Documentation and Messages - regexp: '(docs?|man|manual|manual pages|[Ss]phinx|mkhtml|messages?): ' + regexp: '(docs?|man|manual|manual pages|[Ss]phinx|mkhtml|MkDocs|mkdocs|messages?): ' example: 'doc:' - title: Libraries and General Functionality diff --git a/utils/thumbnails.py b/utils/thumbnails.py index 181a16aa666..2eab7319f52 100755 --- a/utils/thumbnails.py +++ b/utils/thumbnails.py @@ -15,6 +15,8 @@ import os import atexit +from pathlib import Path + import grass.script as gs @@ -35,10 +37,7 @@ def cleanup(): def make_gradient(path): - fh = open(path) - text = fh.read() - fh.close() - + text = Path(path).read_text() lines = text.splitlines() records = [] for line in lines: diff --git a/vector/v.buffer/v.buffer.html b/vector/v.buffer/v.buffer.html index 8870cd335c5..b2e11ac47cd 100644 --- a/vector/v.buffer/v.buffer.html +++ b/vector/v.buffer/v.buffer.html @@ -33,7 +33,7 @@ <h2>NOTES</h2> <i>For advanced users:</i> the built-in buffer algorithm is no longer used, as we use GEOS instead. If GRASS was not compiled with GEOS support or the <a href="variables.html">environmental -variable</a> <tt>GRASS_VECTOR_BUFFER</tt> is defined, then GRASS +variable</a> <code>GRASS_VECTOR_BUFFER</code> is defined, then GRASS generates buffers using the built-in buffering algorithm (which is still buggy for some input data). diff --git a/vector/v.build/v.build.html b/vector/v.build/v.build.html index c66da69110a..20fd726d44a 100644 --- a/vector/v.build/v.build.html +++ b/vector/v.build/v.build.html @@ -76,7 +76,8 @@ <h2>SEE ALSO</h2> <a href="v.build.all.html">v.build.all</a>, <a href="v.build.polylines.html">v.build.polylines</a>, <a href="v.edit.html">v.edit</a>, -<a href="v.split.html">v.split</a> +<a href="v.split.html">v.split</a>, +<a href="v.support.html">v.support</a> </em> <p>See also <em><a href="wxGUI.vdigit.html">wxGUI vector digitizer</a></em>. diff --git a/vector/v.cluster/v.cluster.html b/vector/v.cluster/v.cluster.html index 235e9491b1e..fd70f1ebaa9 100644 --- a/vector/v.cluster/v.cluster.html +++ b/vector/v.cluster/v.cluster.html @@ -20,7 +20,9 @@ <h2>DESCRIPTION</h2> <b>distance</b> or <b>method=density</b> if clusters should be created separately for each observed density (distance to the farthest neighbor). -<h4>dbscan</h4> +<h3>Clustering methods</h3> + +<h4>dbscan method</h4> The <a href="https://en.wikipedia.org/wiki/DBSCAN">Density-Based Spatial Clustering of Applications with Noise</a> is a commonly used clustering algorithm. A new cluster is started for a point with at least @@ -29,12 +31,12 @@ <h4>dbscan</h4> least <i>min</i> - 1 neighbors are within the maximum distance for each point already in the cluster. -<h4>dbscan2</h4> +<h4>dbscan2 method</h4> Similar to <i>dbscan</i>, but here it is sufficient if the resultant cluster consists of at least <b>min</b> points, even if no point in the cluster has at least <i>min - 1</i> neighbors within <b>distance</b>. -<h4>density</h4> +<h4>density method</h4> This method creates clusters according to their point density. The maximum distance is not used. Instead, the points are sorted ascending by the distance to their farthest neighbor (core distance), inspecting @@ -44,7 +46,7 @@ <h4>density</h4> own maximum distance. This method can identify clusters with different densities and can create nested clusters. -<h4>optics</h4> +<h4>optics method</h4> This method is <a href="https://en.wikipedia.org/wiki/OPTICS_algorithm">Ordering Points to Identify the Clustering Structure</a>. It is controlled by the number @@ -75,7 +77,7 @@ <h4>optics</h4> cluster. The order of the input points is arbitrary and can thus influence the resultant clusters. -<h4>optics2</h4> +<h4>optics2 method</h4> <b>EXPERIMENTAL</b> This method is similar to OPTICS, minimizing the reachability of each point. Points are reconnected if their reachability can be reduced. Contrary to OPTICS, a cluster's seed is @@ -176,6 +178,7 @@ <h2>EXAMPLE</h2> Generate random points for analysis (100 points per area), use different method for clustering and visualize using color stored the attribute table. + <div class="code"><pre> # pick a subregion of the vector urbanarea g.region -p n=272950 s=188330 w=574720 e=703090 res=10 diff --git a/vector/v.colors.out/main.c b/vector/v.colors.out/main.c index 8ea03b06ce5..5d5f84bebf0 100644 --- a/vector/v.colors.out/main.c +++ b/vector/v.colors.out/main.c @@ -28,7 +28,7 @@ int main(int argc, char **argv) { struct GModule *module; struct { - struct Option *map, *field, *file, *col; + struct Option *map, *field, *file, *col, *format, *color_format; } opt; struct { struct Flag *p; @@ -40,6 +40,8 @@ int main(int argc, char **argv) const char *file, *name, *layer, *column; FILE *fp; + ColorFormat clr_frmt; + G_gisinit(argv[0]); module = G_define_module(); @@ -65,6 +67,12 @@ int main(int argc, char **argv) opt.col->description = _("If not given, color rules refer to categories"); opt.col->guisection = _("Settings"); + opt.format = G_define_standard_option(G_OPT_F_FORMAT); + opt.format->guisection = _("Print"); + + opt.color_format = G_define_standard_option(G_OPT_C_FORMAT); + opt.color_format->guisection = _("Color"); + flag.p = G_define_flag(); flag.p->key = 'p'; flag.p->description = _("Output values as percentages"); @@ -106,8 +114,26 @@ int main(int argc, char **argv) else colors = &cat_colors; - Rast_print_colors(colors, (DCELL)min, (DCELL)max, fp, - flag.p->answer ? 1 : 0); + if (strcmp(opt.format->answer, "json") == 0) { + if (strcmp(opt.color_format->answer, "rgb") == 0) { + clr_frmt = RGB; + } + else if (strcmp(opt.color_format->answer, "triplet") == 0) { + clr_frmt = TRIPLET; + } + else if (strcmp(opt.color_format->answer, "hsv") == 0) { + clr_frmt = HSV; + } + else { + clr_frmt = HEX; + } + Rast_print_json_colors(colors, (DCELL)min, (DCELL)max, fp, + flag.p->answer ? 1 : 0, clr_frmt); + } + else { + Rast_print_colors(colors, (DCELL)min, (DCELL)max, fp, + flag.p->answer ? 1 : 0); + } exit(EXIT_SUCCESS); } diff --git a/vector/v.colors.out/tests/conftest.py b/vector/v.colors.out/tests/conftest.py new file mode 100644 index 00000000000..120b37b6e60 --- /dev/null +++ b/vector/v.colors.out/tests/conftest.py @@ -0,0 +1,27 @@ +"""Fixture for v.colors.out test""" + +import os +import pytest +import grass.script as gs + + +@pytest.fixture +def vector_color_dataset(tmp_path): + """Set up a GRASS session and create test vector with color rules.""" + project = tmp_path / "vector_color_project" + gs.create_project(project) + with gs.setup.init(project, env=os.environ.copy()) as session: + gs.run_command( + "g.region", + n=90, + s=-90, + e=180, + w=-180, + res=10, + env=session.env, + ) + gs.run_command( + "v.mkgrid", map="a", grid=[10, 10], type="point", env=session.env + ) + gs.run_command("v.colors", map="a", color="elevation", env=session.env) + yield session diff --git a/vector/v.colors.out/tests/v_colors_out_test.py b/vector/v.colors.out/tests/v_colors_out_test.py new file mode 100644 index 00000000000..1d40f9b7e33 --- /dev/null +++ b/vector/v.colors.out/tests/v_colors_out_test.py @@ -0,0 +1,175 @@ +"""Tests of v.colors.out""" + +import grass.script as gs + + +def validate_plain_text_output(data): + """Validate the structure and content of the plain text output.""" + assert data + assert len(data) == 8, "The output does not match the expected number of items (8)." + expected = { + "1 0:191:191", + "20.8 0:255:0", + "40.6 255:255:0", + "60.4 255:127:0", + "80.2 191:127:63", + "100 200:200:200", + "nv 255:255:255", + "default 255:255:255", + } + assert expected == data.keys(), ( + f"test failed: expected {expected} but got {data.keys()}" + ) + + +def test_v_colors_out_plain_output(vector_color_dataset): + """Test v.colors.out command for plain output format.""" + session = vector_color_dataset + data = gs.parse_command("v.colors.out", map="a", format="plain", env=session.env) + validate_plain_text_output(data) + + +def test_v_colors_out_without_format_option(vector_color_dataset): + """Test v.colors.out command without any format option.""" + session = vector_color_dataset + data = gs.parse_command("v.colors.out", map="a", env=session.env) + validate_plain_text_output(data) + + +def test_v_colors_out_with_p_flag(vector_color_dataset): + """Test v.colors.out command with percentage values.""" + session = vector_color_dataset + data = gs.parse_command("v.colors.out", map="a", flags="p", env=session.env) + assert data + assert len(data) == 8, "The output does not match the expected number of items (8)." + expected = { + "0% 0:191:191", + "20% 0:255:0", + "40% 255:255:0", + "60% 255:127:0", + "80% 191:127:63", + "100% 200:200:200", + "nv 255:255:255", + "default 255:255:255", + } + assert expected == data.keys(), ( + f"test failed: expected {expected} but got {data.keys()}" + ) + + +def validate_common_json_structure(data): + """Validate the common structure and content of the JSON output.""" + assert isinstance(data, list), "Output data should be a list of entries." + assert len(data) == 8, ( + "The length of the output JSON does not match the expected value of 8." + ) + + +def test_v_colors_out_json_with_default_option(vector_color_dataset): + """Test v.colors.out command for JSON output format for default color option.""" + session = vector_color_dataset + data = gs.parse_command("v.colors.out", map="a", format="json", env=session.env) + validate_common_json_structure(data) + expected = [ + {"value": 1, "color": "#00BFBF"}, + {"value": 20.8, "color": "#00FF00"}, + {"value": 40.6, "color": "#FFFF00"}, + {"value": 60.4, "color": "#FF7F00"}, + {"value": 80.2, "color": "#BF7F3F"}, + {"value": 100, "color": "#C8C8C8"}, + {"value": "nv", "color": "#FFFFFF"}, + {"value": "default", "color": "#FFFFFF"}, + ] + assert expected == data, f"test failed: expected {expected} but got {data}" + + +def test_v_colors_out_json_with_triplet_option(vector_color_dataset): + """Test v.colors.out command for JSON output format for triplet color option.""" + session = vector_color_dataset + data = gs.parse_command( + "v.colors.out", map="a", format="json", color_format="triplet", env=session.env + ) + validate_common_json_structure(data) + expected = [ + {"value": 1, "color": "0:191:191"}, + {"value": 20.8, "color": "0:255:0"}, + {"value": 40.6, "color": "255:255:0"}, + {"value": 60.4, "color": "255:127:0"}, + {"value": 80.2, "color": "191:127:63"}, + {"value": 100, "color": "200:200:200"}, + {"value": "nv", "color": "255:255:255"}, + {"value": "default", "color": "255:255:255"}, + ] + assert expected == data, f"test failed: expected {expected} but got {data}" + + +def test_v_colors_out_json_with_rgb_option(vector_color_dataset): + """Test v.colors.out command for JSON output format for rgb color option.""" + session = vector_color_dataset + data = gs.parse_command( + "v.colors.out", + map="a", + format="json", + color_format="rgb", + env=session.env, + ) + validate_common_json_structure(data) + expected = [ + {"value": 1, "color": "rgb(0, 191, 191)"}, + {"value": 20.8, "color": "rgb(0, 255, 0)"}, + {"value": 40.6, "color": "rgb(255, 255, 0)"}, + {"value": 60.4, "color": "rgb(255, 127, 0)"}, + {"value": 80.2, "color": "rgb(191, 127, 63)"}, + {"value": 100, "color": "rgb(200, 200, 200)"}, + {"value": "nv", "color": "rgb(255, 255, 255)"}, + {"value": "default", "color": "rgb(255, 255, 255)"}, + ] + assert expected == data, f"test failed: expected {expected} but got {data}" + + +def test_v_colors_out_json_with_hex_option(vector_color_dataset): + """Test v.colors.out command for JSON output format for hex color option.""" + session = vector_color_dataset + data = gs.parse_command( + "v.colors.out", + map="a", + format="json", + color_format="hex", + env=session.env, + ) + validate_common_json_structure(data) + expected = [ + {"value": 1, "color": "#00BFBF"}, + {"value": 20.8, "color": "#00FF00"}, + {"value": 40.6, "color": "#FFFF00"}, + {"value": 60.4, "color": "#FF7F00"}, + {"value": 80.2, "color": "#BF7F3F"}, + {"value": 100, "color": "#C8C8C8"}, + {"value": "nv", "color": "#FFFFFF"}, + {"value": "default", "color": "#FFFFFF"}, + ] + assert expected == data, f"test failed: expected {expected} but got {data}" + + +def test_v_colors_out_json_with_hsv_option(vector_color_dataset): + """Test v.colors.out command for JSON output format for hsv color option.""" + session = vector_color_dataset + data = gs.parse_command( + "v.colors.out", + map="a", + format="json", + color_format="hsv", + env=session.env, + ) + validate_common_json_structure(data) + expected = [ + {"value": 1, "color": "hsv(180, 100, 74)"}, + {"value": 20.8, "color": "hsv(120, 100, 100)"}, + {"value": 40.6, "color": "hsv(60, 100, 100)"}, + {"value": 60.4, "color": "hsv(29, 100, 100)"}, + {"value": 80.2, "color": "hsv(30, 67, 74)"}, + {"value": 100, "color": "hsv(0, 0, 78)"}, + {"value": "nv", "color": "hsv(0, 0, 100)"}, + {"value": "default", "color": "hsv(0, 0, 100)"}, + ] + assert expected == data, f"test failed: expected {expected} but got {data}" diff --git a/vector/v.colors/v.colors.html b/vector/v.colors/v.colors.html index 440ed5763ec..654413ba040 100644 --- a/vector/v.colors/v.colors.html +++ b/vector/v.colors/v.colors.html @@ -42,7 +42,7 @@ <h2>EXAMPLES</h2> <h3>Define color table based on categories</h3> -Define color table <tt>wave</tt> based on categories from layer 1 +Define color table <code>wave</code> based on categories from layer 1 <div class="code"><pre> v.colors map=soils_general layer=1 color=wave @@ -50,8 +50,8 @@ <h3>Define color table based on categories</h3> <h3>Define color table based on attribute values</h3> -Define color table <tt>ryg</tt> based on values from attribute -column <tt>AREA</tt>. Attribute table is linked to layer 1. +Define color table <code>ryg</code> based on values from attribute +column <code>AREA</code>. Attribute table is linked to layer 1. <div class="code"><pre> v.to.db map=soils_general layer=1 option=area column=AREA @@ -60,7 +60,7 @@ <h3>Define color table based on attribute values</h3> <h3>Define color table stored as RGB values in attribute table</h3> -Write color values to the attribute table (column <tt>GRASSRGB</tt>) +Write color values to the attribute table (column <code>GRASSRGB</code>) instead of creating color table. <div class="code"><pre> @@ -145,7 +145,7 @@ <h2>SEE ALSO</h2> page <a href="https://grasswiki.osgeo.org/wiki/Color_tables">Color tables</a> (from GRASS User Wiki) -<p><a href="http://colorbrewer.org">ColorBrewer</a> is an online tool designed to +<p><a href="https://colorbrewer2.org">ColorBrewer</a> is an online tool designed to help people select good color schemes for maps and other graphics. <h2>AUTHORS</h2> diff --git a/vector/v.db.select/v.db.select.html b/vector/v.db.select/v.db.select.html index 13bbbbb1b78..8447259be3b 100644 --- a/vector/v.db.select/v.db.select.html +++ b/vector/v.db.select/v.db.select.html @@ -16,7 +16,7 @@ <h4>Plain text</h4> table using the <em>v.db.select</em> GUI dialog. <p> -The individual fields (attribute values) are separated by a pipe (<tt>|</tt>) +The individual fields (attribute values) are separated by a pipe (<code>|</code>) which can be customized using the <b>separator</b> option. The records (rows) are separated by newlines. @@ -32,8 +32,8 @@ <h4>Plain text</h4> </pre></div> When escaping is enabled, the following characters in the fields are escaped: -backslash (<tt>\\</tt>), carriage return (<tt>\r</tt>), line feed (<tt>\n</tt>), -tabulator (<tt>\t</tt>), form feed (<tt>\f</tt>), and backslash (<tt>\b</tt>). +backslash (<code>\\</code>), carriage return (<code>\r</code>), line feed (<code>\n</code>), +tabulator (<code>\t</code>), form feed (<code>\f</code>), and backslash (<code>\b</code>). <p> No quoting or escaping is performed by default, so if these characters are in @@ -50,7 +50,7 @@ <h4>Plain text</h4> <h4>CSV</h4> CSV (comma-separated values) has many variations. This module by default produces -CSV with comma (<tt>,</tt>) as the field separator (delimiter). All text fields +CSV with comma (<code>,</code>) as the field separator (delimiter). All text fields (based on the type) are quoted with double quotes. Double quotes in fields are represented as two double quotes. Newline characters in the fields are present as-is in the output. Header is included by default containing column names. @@ -95,11 +95,11 @@ <h4>JSON</h4> tabulator, form feed, backslash, and double quote) for string values. Numbers in the database such as integers and doubles are represented as numbers, while texts (TEXT, VARCHAR, etc.) and dates in the database are represented -as strings in JSON. NULL values in database are represented as JSON <tt>null</tt>. +as strings in JSON. NULL values in database are represented as JSON <code>null</code>. Indentation and newlines in the output are minimal and not guaranteed. <p> -Records which are the result of the query are stored under key <tt>records</tt> +Records which are the result of the query are stored under key <code>records</code> as an array (list) of objects (collections of key-value pairs). The keys for attributes are lowercase or uppercase depending on how the columns were defined in the database. @@ -174,7 +174,7 @@ <h4>Vertical plain text</h4> the vertical separator (<b>vertical_separator</b> option). <p> -Example with (horizontal) separator <tt>=</tt> and vertical separator <tt>newline</tt>: +Example with (horizontal) separator <code>=</code> and vertical separator <code>newline</code>: <div class="code"><pre> cat=1 diff --git a/vector/v.decimate/v.decimate.html b/vector/v.decimate/v.decimate.html index e1bad03acbf..9f7b0001307 100644 --- a/vector/v.decimate/v.decimate.html +++ b/vector/v.decimate/v.decimate.html @@ -138,4 +138,4 @@ <h2>SEE ALSO</h2> <h2>AUTHOR</h2> -Vaclav Petras, <a href="http://geospatial.ncsu.edu/osgeorel/">NCSU OSGeoREL</a> +Vaclav Petras, <a href="https://geospatial.ncsu.edu/geoforall/">NCSU GeoForAll Lab</a> diff --git a/vector/v.delaunay/v.delaunay.html b/vector/v.delaunay/v.delaunay.html index 2c8b8db4531..de033403816 100644 --- a/vector/v.delaunay/v.delaunay.html +++ b/vector/v.delaunay/v.delaunay.html @@ -30,6 +30,8 @@ <h2>SEE ALSO</h2> <a href="v.voronoi.html">v.voronoi</a>, <a href="v.hull.html">v.hull</a> </em> +<p> +<a href="https://en.wikipedia.org/wiki/Delaunay_triangulation">Delaunay triangulation (Wikipedia)</a> <h2>AUTHORS</h2> diff --git a/vector/v.distance/Makefile b/vector/v.distance/Makefile index 77a73b52474..58e84e11846 100644 --- a/vector/v.distance/Makefile +++ b/vector/v.distance/Makefile @@ -3,7 +3,7 @@ MODULE_TOPDIR = ../.. PGM=v.distance -LIBES = $(VECTORLIB) $(DBMILIB) $(GISLIB) $(MATHLIB) +LIBES = $(VECTORLIB) $(DBMILIB) $(GISLIB) $(MATHLIB) $(PARSONLIB) DEPENDENCIES = $(VECTORDEP) $(DBMIDEP) $(GISDEP) EXTRA_INC = $(VECT_INC) diff --git a/vector/v.distance/local_proto.h b/vector/v.distance/local_proto.h index 21afd668d20..0e92c7f45b8 100644 --- a/vector/v.distance/local_proto.h +++ b/vector/v.distance/local_proto.h @@ -1,5 +1,6 @@ #include <grass/vector.h> #include <grass/dbmi.h> +#include <grass/parson.h> #ifndef _LOCAL_PROTO_ #define _LOCAL_PROTO_ @@ -23,6 +24,8 @@ #define TO_ATTR 10 /* attribute of nearest feature */ #define END 11 /* end of list */ +enum OutputFormat { PLAIN, JSON }; + /* Structure to store info about nearest feature for each category */ typedef struct { int from_cat; /* category (from) */ @@ -66,6 +69,7 @@ int line2area(struct Map_info *To, struct line_pnts *Points, int type, int area, int with_z); /* print.c */ -int print_upload(NEAR *, UPLOAD *, int, dbCatValArray *, dbCatVal *, char *); +int print_upload(NEAR *, UPLOAD *, int, dbCatValArray *, dbCatVal *, char *, + enum OutputFormat, JSON_Object *); #endif diff --git a/vector/v.distance/main.c b/vector/v.distance/main.c index 4aa372420f9..ae484efd146 100644 --- a/vector/v.distance/main.c +++ b/vector/v.distance/main.c @@ -30,6 +30,7 @@ #include <grass/gis.h> #include <grass/glocale.h> #include <grass/vector.h> +#include <grass/parson.h> #include "local_proto.h" /* Supported command lines: @@ -69,6 +70,7 @@ int main(int argc, char *argv[]) struct Option *out, *max, *min, *table; struct Option *upload, *column, *to_column; struct Option *sep; + struct Option *format; } opt; struct { struct Flag *print, *all, *square; @@ -112,6 +114,10 @@ int main(int argc, char *argv[]) dbCatValArray cvarr; dbColumn *column; char *sep; + enum OutputFormat format; + JSON_Value *root_value, *object_value; + JSON_Array *root_array; + JSON_Object *root_object; G_gisinit(argv[0]); @@ -237,6 +243,9 @@ int main(int argc, char *argv[]) opt.sep = G_define_standard_option(G_OPT_F_SEP); opt.sep->label = _("Field separator for printing output to stdout"); + opt.format = G_define_standard_option(G_OPT_F_FORMAT); + opt.format->guisection = _("Print"); + flag.print = G_define_flag(); flag.print->key = 'p'; flag.print->label = @@ -293,6 +302,15 @@ int main(int argc, char *argv[]) do_all = flag.all->answer; print_as_matrix = flag.square->answer; + if (strcmp(opt.format->answer, "json") == 0) { + format = JSON; + root_value = json_value_init_array(); + root_array = json_array(root_value); + } + else { + format = PLAIN; + } + if (do_all && update_table) G_fatal_error(_("Updating the from= table is not supported with -a")); @@ -1397,7 +1415,7 @@ int main(int argc, char *argv[]) update_notfound = ncatexist = 0; /* Update database / print to stdout / create output map */ - if (print) { /* print header */ + if (print && format == PLAIN) { /* print header */ fprintf(stdout, "from_cat"); if (do_all) fprintf(stdout, "%sto_cat", sep); @@ -1504,35 +1522,51 @@ int main(int argc, char *argv[]) } if (print) { /* print only */ - /* - input and output is the same && - calculate distances && - only one upload option given -> - print as a matrix - */ - if (print_as_matrix) { - if (i == 0) { - for (j = 0; j < nfrom; j++) { - if (j == 0) - fprintf(stdout, " "); - fprintf(stdout, "%s%d", sep, Near[j].to_cat); + switch (format) { + case PLAIN: + /* + input and output is the same && + calculate distances && + only one upload option given -> + print as a matrix + */ + if (print_as_matrix) { + if (i == 0) { + for (j = 0; j < nfrom; j++) { + if (j == 0) + fprintf(stdout, " "); + fprintf(stdout, "%s%d", sep, Near[j].to_cat); + } + fprintf(stdout, "\n"); + } + if (i % nfrom == 0) { + fprintf(stdout, "%d", Near[i].from_cat); + for (j = 0; j < nfrom; j++) { + print_upload(Near, Upload, i + j, &cvarr, catval, + sep, format, NULL); + } + fprintf(stdout, "\n"); } - fprintf(stdout, "\n"); } - if (i % nfrom == 0) { + else { fprintf(stdout, "%d", Near[i].from_cat); - for (j = 0; j < nfrom; j++) { - print_upload(Near, Upload, i + j, &cvarr, catval, sep); - } + if (do_all) + fprintf(stdout, "%s%d", sep, Near[i].to_cat); + print_upload(Near, Upload, i, &cvarr, catval, sep, format, + NULL); fprintf(stdout, "\n"); } - } - else { - fprintf(stdout, "%d", Near[i].from_cat); - if (do_all) - fprintf(stdout, "%s%d", sep, Near[i].to_cat); - print_upload(Near, Upload, i, &cvarr, catval, sep); - fprintf(stdout, "\n"); + break; + case JSON: + object_value = json_value_init_object(); + root_object = json_object(object_value); + json_object_set_number(root_object, "from_cat", + Near[i].from_cat); + json_object_set_number(root_object, "to_cat", Near[i].to_cat); + print_upload(Near, Upload, i, &cvarr, catval, sep, format, + root_object); + json_array_append_value(root_array, object_value); + break; } } else if (create_table) { /* insert new record */ @@ -1726,6 +1760,17 @@ int main(int argc, char *argv[]) } } } + + if (format == JSON) { + char *serialized_string = json_serialize_to_string_pretty(root_value); + if (serialized_string == NULL) { + G_fatal_error(_("Failed to initialize pretty JSON string.")); + } + puts(serialized_string); + json_free_serialized_string(serialized_string); + json_value_free(root_value); + } + G_percent(count, count, 1); if (driver) diff --git a/vector/v.distance/print.c b/vector/v.distance/print.c index 21f27c0cbb4..8e2369828eb 100644 --- a/vector/v.distance/print.c +++ b/vector/v.distance/print.c @@ -4,8 +4,19 @@ print out upload values */ int print_upload(NEAR *Near, UPLOAD *Upload, int i, dbCatValArray *cvarr, - dbCatVal *catval, char *sep) + dbCatVal *catval, char *sep, enum OutputFormat format, + JSON_Object *object) { + JSON_Value *relations_value, *relation_value; + JSON_Array *relations; + JSON_Object *relation_object; + char *name; + + if (format == JSON) { + relations_value = json_value_init_array(); + relations = json_array(relations_value); + } + int j; j = 0; @@ -14,61 +25,148 @@ int print_upload(NEAR *Near, UPLOAD *Upload, int i, dbCatValArray *cvarr, fprintf(stdout, "%snull", sep); } else { - switch (Upload[j].upload) { - case CAT: - if (Near[i].to_cat >= 0) - fprintf(stdout, "%s%d", sep, Near[i].to_cat); - else - fprintf(stdout, "%snull", sep); - break; - case DIST: - fprintf(stdout, "%s%.17g", sep, Near[i].dist); - break; - case FROM_X: - fprintf(stdout, "%s%.17g", sep, Near[i].from_x); - break; - case FROM_Y: - fprintf(stdout, "%s%.17g", sep, Near[i].from_y); - break; - case TO_X: - fprintf(stdout, "%s%.17g", sep, Near[i].to_x); - break; - case TO_Y: - fprintf(stdout, "%s%.17g", sep, Near[i].to_y); - break; - case FROM_ALONG: - fprintf(stdout, "%s%.17g", sep, Near[i].from_along); - break; - case TO_ALONG: - fprintf(stdout, "%s%.17g", sep, Near[i].to_along); - break; - case TO_ANGLE: - fprintf(stdout, "%s%f", sep, Near[i].to_angle); + switch (format) { + case JSON: + relation_value = json_value_init_object(); + relation_object = json_object(relation_value); + switch (Upload[j].upload) { + case CAT: + name = "to_cat"; + if (Near[i].to_cat >= 0) + json_object_set_number(relation_object, "value", + Near[i].to_cat); + else { + json_object_set_null(relation_object, "value"); + } + break; + case DIST: + name = "dist"; + json_object_set_number(relation_object, "value", + Near[i].dist); + break; + case FROM_X: + name = "from_x"; + json_object_set_number(relation_object, "value", + Near[i].from_x); + break; + case FROM_Y: + name = "from_y"; + json_object_set_number(relation_object, "value", + Near[i].from_y); + break; + case TO_X: + name = "to_x"; + json_object_set_number(relation_object, "value", + Near[i].to_x); + break; + case TO_Y: + name = "to_y"; + json_object_set_number(relation_object, "value", + Near[i].to_y); + break; + case FROM_ALONG: + name = "from_along"; + json_object_set_number(relation_object, "value", + Near[i].from_along); + break; + case TO_ALONG: + name = "to_along"; + json_object_set_number(relation_object, "value", + Near[i].to_along); + break; + case TO_ANGLE: + name = "to_angle"; + json_object_set_number(relation_object, "value", + Near[i].to_angle); + break; + case TO_ATTR: + name = "to_attr"; + if (catval) { + switch (cvarr->ctype) { + case DB_C_TYPE_INT: + json_object_set_number(relation_object, "value", + catval->val.i); + break; + case DB_C_TYPE_DOUBLE: + json_object_set_number(relation_object, "value", + catval->val.d); + break; + case DB_C_TYPE_STRING: + json_object_set_string( + relation_object, "value", + db_get_string(catval->val.s)); + break; + case DB_C_TYPE_DATETIME: + /* TODO: formatting datetime */ + break; + } + } + else { + json_object_set_null(relation_object, "value"); + } + break; + } + json_object_set_string(relation_object, "name", name); + json_array_append_value(relations, relation_value); break; - case TO_ATTR: - if (catval) { - switch (cvarr->ctype) { - case DB_C_TYPE_INT: - fprintf(stdout, "%s%d", sep, catval->val.i); - break; + case PLAIN: + switch (Upload[j].upload) { + case CAT: + if (Near[i].to_cat >= 0) + fprintf(stdout, "%s%d", sep, Near[i].to_cat); + else + fprintf(stdout, "%snull", sep); + break; + case DIST: + fprintf(stdout, "%s%.17g", sep, Near[i].dist); + break; + case FROM_X: + fprintf(stdout, "%s%.17g", sep, Near[i].from_x); + break; + case FROM_Y: + fprintf(stdout, "%s%.17g", sep, Near[i].from_y); + break; + case TO_X: + fprintf(stdout, "%s%.17g", sep, Near[i].to_x); + break; + case TO_Y: + fprintf(stdout, "%s%.17g", sep, Near[i].to_y); + break; + case FROM_ALONG: + fprintf(stdout, "%s%.17g", sep, Near[i].from_along); + break; + case TO_ALONG: + fprintf(stdout, "%s%.17g", sep, Near[i].to_along); + break; + case TO_ANGLE: + fprintf(stdout, "%s%f", sep, Near[i].to_angle); + break; + case TO_ATTR: + if (catval) { + switch (cvarr->ctype) { + case DB_C_TYPE_INT: + fprintf(stdout, "%s%d", sep, catval->val.i); + break; - case DB_C_TYPE_DOUBLE: - fprintf(stdout, "%s%.17g", sep, catval->val.d); - break; + case DB_C_TYPE_DOUBLE: + fprintf(stdout, "%s%.17g", sep, catval->val.d); + break; - case DB_C_TYPE_STRING: - fprintf(stdout, "%s%s", sep, - db_get_string(catval->val.s)); - break; + case DB_C_TYPE_STRING: + fprintf(stdout, "%s%s", sep, + db_get_string(catval->val.s)); + break; - case DB_C_TYPE_DATETIME: - /* TODO: formatting datetime */ - fprintf(stdout, "%s", sep); - break; + case DB_C_TYPE_DATETIME: + /* TODO: formatting datetime */ + fprintf(stdout, "%s", sep); + break; + } } - } - else { - fprintf(stdout, "%snull", sep); + else { + fprintf(stdout, "%snull", sep); + } + break; } break; } @@ -76,5 +174,8 @@ int print_upload(NEAR *Near, UPLOAD *Upload, int i, dbCatValArray *cvarr, j++; } + if (format == JSON) { + json_object_set_value(object, "distances", relations_value); + } return 0; } diff --git a/vector/v.distance/testsuite/test_v_distance.py b/vector/v.distance/testsuite/test_v_distance.py new file mode 100644 index 00000000000..98337d40937 --- /dev/null +++ b/vector/v.distance/testsuite/test_v_distance.py @@ -0,0 +1,53 @@ +import json +from itertools import zip_longest + +from grass.gunittest.case import TestCase +from grass.gunittest.main import test +from grass.gunittest.gmodules import SimpleModule + + +class TestVDistance(TestCase): + def test_json(self): + """Test json format""" + reference = [ + { + "from_cat": 1, + "to_cat": 112, + "distances": [ + {"value": 0.1428123184481199, "name": "dist"}, + {"value": "8,A", "name": "to_attr"}, + ], + }, + { + "from_cat": 2, + "to_cat": 44, + "distances": [ + {"value": 0.10232660032693719, "name": "dist"}, + {"value": "9,A", "name": "to_attr"}, + ], + }, + ] + + kwargs = { + "flags": "p", + "from": "busroute_a", + "to": "busstopsall", + "upload": ["dist,to_attr"], + "to_column": "routes", + "format": "json", + } + module = SimpleModule("v.distance", **kwargs) + self.runModule(module) + received = json.loads(module.outputs.stdout) + + for first, second in zip_longest(reference, received): + self.assertEqual(first["from_cat"], second["from_cat"]) + self.assertEqual(first["to_cat"], second["to_cat"]) + for f_d, s_d in zip_longest(first["distances"], second["distances"]): + self.assertEqual(f_d["name"], s_d["name"]) + self.assertAlmostEqual(f_d["value"], s_d["value"], places=6) + self.assertEqual(reference, received) + + +if __name__ == "__main__": + test() diff --git a/vector/v.distance/v.distance.html b/vector/v.distance/v.distance.html index 2be5c1bd537..ff3b4061f4e 100644 --- a/vector/v.distance/v.distance.html +++ b/vector/v.distance/v.distance.html @@ -152,10 +152,10 @@ <h3>Univariate statistics on results</h3> Create a vector map containing connecting lines and investigate mean distance to targets. An alternative solution is to use -the <tt>v.distance upload=dist</tt> option to upload distances into +the <code>v.distance upload=dist</code> option to upload distances into the <i>bugs</i> vector directly, then run v.univar on that. Also note -you can upload two columns at a time, e.g. <tt>v.distance -upload=cat,dist column=nearest_id,dist_to_nr</tt>. +you can upload two columns at a time, e.g. <code>v.distance +upload=cat,dist column=nearest_id,dist_to_nr</code>. <div class="code"><pre> # create working copy @@ -221,6 +221,46 @@ <h3>Print distance matrix</h3> ... </pre></div> +<h3>Print in JSON</h3> + +<div class="code"><pre> +v.distance -p from=busroute_a to=busstopsall upload=dist,to_attr to_column=routes format=json +</pre></div> + +<div class="code"><pre> +[ + { + "from_cat": 1, + "to_cat": 112, + "distances": [ + { + "value": 0.1428123184481199, + "name": "dist" + }, + { + "value": "8,A", + "name": "to_attr" + } + ] + }, + { + "from_cat": 2, + "to_cat": 44, + "distances": [ + { + "value": 0.10232660032693719, + "name": "dist" + }, + { + "value": "9,A", + "name": "to_attr" + } + ] + } +] +</pre></div> + + <h2>SEE ALSO</h2> <em> diff --git a/vector/v.edit/v.edit.html b/vector/v.edit/v.edit.html index 3768e65ad4d..f29b665841b 100644 --- a/vector/v.edit/v.edit.html +++ b/vector/v.edit/v.edit.html @@ -27,7 +27,7 @@ <h2>DESCRIPTION</h2> selects all features (and prints their id's to standard output) covered by two bounding boxes (center at 599505,4921010 and 603389.0625,4918292.1875, size 2*10000) with attribute -<tt>label='interstate'</tt>. +<code>label='interstate'</code>. <h2>NOTES</h2> @@ -108,7 +108,7 @@ <h3>Tool description</h3> <li><b>break</b> - Split given vector line or boundary into two lines on location given by <b>coords</b>. If <b>coords</b> not given, breaks all selected lines at each intersection (based - on <em><a href="v.clean.html">v.clean</a></em>, <tt>tool=break</tt>).</li> + on <em><a href="v.clean.html">v.clean</a></em>, <code>tool=break</code>).</li> <li><b>snap</b> - Snap vector features in given threshold. See also module <em><a href="v.clean.html">v.clean</a></em>. Note that this @@ -139,7 +139,7 @@ <h3>Tool description</h3> <li><b>chtype</b> - Change feature type of selected geometry objects. Points are converted to centroids, centroids to points, - lines to boundaries and boundaries to lines. + lines to boundaries and boundaries to lines.</li> <li><b>vertexadd</b> - Add vertex(ces) to the given vector lines or boundaries. Location of the new vertex is given by <b>coord</b> diff --git a/vector/v.external.out/v.external.out.html b/vector/v.external.out/v.external.out.html index f9c3575aa88..9241a6bda43 100644 --- a/vector/v.external.out/v.external.out.html +++ b/vector/v.external.out/v.external.out.html @@ -9,30 +9,30 @@ <h2>DESCRIPTION</h2> <h2>NOTES</h2> -Number of available output formats (<tt>v.external.out -f</tt>) +Number of available output formats (<code>v.external.out -f</code>) depends on OGR installation. 'PostgreSQL' format is presented also when GRASS comes with PostgreSQL support (check for '--with-postgres' -in <tt>g.version -b</tt> output). +in <code>g.version -b</code> output). <p> To store geometry and attribute data in PostGIS database ('PostgreSQL' format) GRASS uses built-in <em>GRASS-PostGIS data provider</em>. PostGIS data can be written also by OGR library -when <tt>GRASS_VECTOR_OGR</tt> environmental variable is defined or +when <code>GRASS_VECTOR_OGR</code> environmental variable is defined or GRASS is not compiled with PostgreSQL support. <p> Creation <b>options</b> refer to the output format specified by <b>format</b> option. See the list of valid creation options -at <a href="https://gdal.org/drivers/vector/">OGR formats +at <a href="https://gdal.org/en/stable/drivers/vector/">OGR formats specification page</a>, example -for <a href="https://gdal.org/drv_shapefile.html">ESRI +for <a href="https://gdal.org/en/stable/drivers/vector/shapefile.html">ESRI Shapefile</a> -or <a href="https://gdal.org/drv_pg.html">PostgreSQL/PostGIS</a> +or <a href="https://gdal.org/en/stable/drivers/vector/pg.html">PostgreSQL/PostGIS</a> format (section "Layer Creation Options"). Options are -comma-separated pairs (<tt>key=value</tt>), the options are +comma-separated pairs (<code>key=value</code>), the options are case-insensitive, -eg. <tt>options="SCHEMA=myschema,FID=cat"</tt>. +eg. <code>options="SCHEMA=myschema,FID=cat"</code>. <h3>PostgreSQL/PostGIS Creation Options</h3> @@ -40,39 +40,39 @@ <h3>PostgreSQL/PostGIS Creation Options</h3> (<b>format=PostgreSQL</b>) supports different creation <b>options</b> compared to PostgreSQL/PostGIS driver from OGR library: <ul> - <li><tt>SCHEMA=<name></tt> - name of schema where to create + <li><code>SCHEMA=<name></code> - name of schema where to create feature tables. If schema doesn't exists, it's automatically created when writing PostGIS data.</li> - <li><tt>FID=<column></tt> - name of column which will be used as - primary key (feature id), default: <tt>fid</tt></li> - <li><tt>GEOMETRY_NAME=<column></tt> name of column which will - be used for storing geometry data in feature table, default: <tt>geom</tt></li> - <li><tt>SPATIAL_INDEX=YES|NO</tt> - enable/disable spatial index on geometry column, default: YES</li> - <li><tt>PRIMARY_KEY=YES|NO</tt> - enable/disable primary key on FID column, default: YES</li> - <li><tt>SRID=<value></tt> - spatial reference identifier, + <li><code>FID=<column></code> - name of column which will be used as + primary key (feature id), default: <code>fid</code></li> + <li><code>GEOMETRY_NAME=<column></code> name of column which will + be used for storing geometry data in feature table, default: <code>geom</code></li> + <li><code>SPATIAL_INDEX=YES|NO</code> - enable/disable spatial index on geometry column, default: YES</li> + <li><code>PRIMARY_KEY=YES|NO</code> - enable/disable primary key on FID column, default: YES</li> + <li><code>SRID=<value></code> - spatial reference identifier, default: not defined</li> - <li><tt>TOPOLOGY=YES|NO</tt> - enable/disable + <li><code>TOPOLOGY=YES|NO</code> - enable/disable native <a href="https://grasswiki.osgeo.org/wiki/PostGIS_Topology">PostGIS topology</a>, default: NO</li> </ul> -Options relevant only to topological output (<tt>TOPOLOGY=YES</tt>): +Options relevant only to topological output (<code>TOPOLOGY=YES</code>): <ul> - <li><tt>TOPOSCHEMA_NAME=<schema name></tt> - name of PostGIS - Topology schema (relevant only for <tt>TOPOLOGY=YES</tt>), - default: <tt>topo_<input></tt></li> - <li><tt>TOPOGEOM_NAME=<column></tt> - name of column which + <li><code>TOPOSCHEMA_NAME=<schema name></code> - name of PostGIS + Topology schema (relevant only for <code>TOPOLOGY=YES</code>), + default: <code>topo_<input></code></li> + <li><code>TOPOGEOM_NAME=<column></code> - name of column which will be used for storing topogeometry data in feature table, - default: <tt>topo</tt></li> - <li><tt>TOPO_TOLERANCE=<value></tt> - tolerance for PostGIS + default: <code>topo</code></li> + <li><code>TOPO_TOLERANCE=<value></code> - tolerance for PostGIS Topology schema, - see <a href="http://www.postgis.net/docs/manual-2.0/CreateTopology.html">CreateTopology</a> - function for defails, default: <tt>0</tt></li> - <li><tt>TOPO_GEO_ONLY=YES|NO</tt> - store in PostGIS Topology schema - only data relevant to Topo-Geo data model, default: <tt>NO</tt></li> - <li><tt>SIMPLE_FEATURE=YES|NO</tt> - build simple features geometry - in <tt>GEOMETRY_NAME</tt> column from topogeometry data, default: + see <a href="https://postgis.net/docs/CreateTopology.html">CreateTopology</a> + function for defails, default: <code>0</code></li> + <li><code>TOPO_GEO_ONLY=YES|NO</code> - store in PostGIS Topology schema + only data relevant to Topo-Geo data model, default: <code>NO</code></li> + <li><code>SIMPLE_FEATURE=YES|NO</code> - build simple features geometry + in <code>GEOMETRY_NAME</code> column from topogeometry data, default: NO</li> </ul> @@ -122,7 +122,7 @@ <h3>PostGIS (simple features)</h3> # do some processing... </pre></div> -<i>Note:</i> If the environment variable <tt>GRASS_VECTOR_OGR</tt> +<i>Note:</i> If the environment variable <code>GRASS_VECTOR_OGR</code> is defined, or GRASS is compiled without PostgreSQL support then GRASS will use PostgreSQL driver from OGR library for reading and writing PostGIS data. @@ -181,7 +181,7 @@ <h2>REFERENCES</h2> <ul> <li><a href="https://trac.osgeo.org/grass/wiki/Grass7/VectorLib/OGRInterface">GRASS-OGR data provider</a></li> - <li><a href="https://gdal.org/api/">OGR vector library C API</a> documentation</li> + <li><a href="https://gdal.org/en/stable/api/">OGR vector library C API</a> documentation</li> <li><a href="https://trac.osgeo.org/grass/wiki/Grass7/VectorLib/PostGISInterface">GRASS-PostGIS data provider</a></li> <li><a href="https://www.postgresql.org/docs/9.1/static/libpq.html">libpq - C Library</a></li> </ul> diff --git a/vector/v.external/v.external.html b/vector/v.external/v.external.html index 200d3726363..da39db7dc0f 100644 --- a/vector/v.external/v.external.html +++ b/vector/v.external/v.external.html @@ -47,7 +47,7 @@ <h3>Supported OGR vector formats</h3> </pre></div> For details see -<a href="https://gdal.org/drivers/vector/">GDAL web site</a>. +<a href="https://gdal.org/en/stable/drivers/vector/">GDAL web site</a>. <h2>EXAMPLES</h2> @@ -64,7 +64,7 @@ <h3>PostGIS layers</h3> By default, PostGIS links are created by built-in PostGIS support, ie. using <em>GRASS-PostGIS data driver</em>. If the environment -variable <tt>GRASS_VECTOR_OGR</tt> exists, or GRASS is compiled +variable <code>GRASS_VECTOR_OGR</code> exists, or GRASS is compiled without PostgreSQL support then GRASS will use OGR-PostgreSQL driver for creating a link. @@ -124,7 +124,7 @@ <h3>Linking subset of features</h3> <h2>REFERENCES</h2> -<a href="https://gdal.org/api/">OGR vector library C +<a href="https://gdal.org/en/stable/api/">OGR vector library C API</a> documentation <h2>SEE ALSO</h2> @@ -142,7 +142,7 @@ <h2>SEE ALSO</h2> <p> <a href="https://gdal.org/">GDAL Library</a> <br> -<a href="http://postgis.org/">PostGIS</a> +<a href="https://postgis.net/">PostGIS</a> <p> See diff --git a/vector/v.generalize/network.c b/vector/v.generalize/network.c index 9c4cf5adeb5..62c6a8454c3 100644 --- a/vector/v.generalize/network.c +++ b/vector/v.generalize/network.c @@ -258,5 +258,7 @@ int graph_generalization(struct Map_info *In, struct Map_info *Out, Vect_destroy_list(prev[i]); G_free(prev); graph_free(&g); + Vect_destroy_line_struct(Points); + Vect_destroy_cats_struct(Cats); return output; } diff --git a/vector/v.in.ascii/v.in.ascii.html b/vector/v.in.ascii/v.in.ascii.html index cd1ab9c7cf1..b6f0b24aa1f 100644 --- a/vector/v.in.ascii/v.in.ascii.html +++ b/vector/v.in.ascii/v.in.ascii.html @@ -24,9 +24,9 @@ <h2>NOTES</h2> The input is read from the file specified by the <b>input</b> option or from standard input. -<p>The field separator may be a character, the word '<tt>tab</tt>' -(or '<tt>\t</tt>') for tab, '<tt>space</tt>' (or ' ') for a blank, -or '<tt>comma</tt>' (or ',') for a comma. +<p>The field separator may be a character, the word '<code>tab</code>' +(or '<code>\t</code>') for tab, '<code>space</code>' (or ' ') for a blank, +or '<code>comma</code>' (or ',') for a comma. <p>An attribute table is only created if it is needed, i.e. when at least one attribute column is present in the input file besides @@ -48,15 +48,15 @@ <h2>NOTES</h2> <p>If old version is requested, the <b>output</b> files from <em><a href="v.out.ascii.html">v.out.ascii</a></em> is placed in -the <tt>$LOCATION/$MAPSET/dig_ascii/</tt> -and <tt>$LOCATION/$MAPSET/dig_att</tt> directory. +the <code>$LOCATION/$MAPSET/dig_ascii/</code> +and <code>$LOCATION/$MAPSET/dig_att</code> directory. <h3>Import of files without category column</h3> If the input file does not contain a category column, there is the possibility to auto-generate these IDs (categories). To automatically add an additional column named 'cat', the <b>cat</b> parameter must be -set to the virtual column number 0 (<tt>cat=0</tt>). This is the +set to the virtual column number 0 (<code>cat=0</code>). This is the default action if the <b>cat</b> parameter is not set. <h3>Importing from a spreadsheet</h3> @@ -68,9 +68,9 @@ <h3>Importing from a spreadsheet</h3> contains any header lines, such as column headings, the <b>skip</b> parameter should be used. These skipped header lines will be written to the map's history file for later reference (read with -<tt>v.info -h</tt>). The skip option only works in <b>points</b> mode. +<code>v.info -h</code>). The skip option only works in <b>points</b> mode. -<p>Any line starting with the hash character ('<tt>#</tt>') will be treated as +<p>Any line starting with the hash character ('<code>#</code>') will be treated as a comment and skipped completely if located in the main data file. If located in the header, as defined by the <b>skip</b> parameter, it will be treated as a header line and written to the history file. @@ -82,7 +82,7 @@ <h3>Import of sexagesimal degree (degree, minutes, seconds, DMS)</h3> the positions are internally translated into decimal degrees during the import, the original DMS values are maintained in the attribute table. This requires both the latitude and the longitude columns to be -defined as <tt>varchar()</tt>, not as numbers. A warning will be +defined as <code>varchar()</code>, not as numbers. A warning will be issued which can be ignored. See <a href="vectorascii.html">GRASS ASCII vector format specification</a> for details. @@ -280,7 +280,7 @@ <h3>Example 8 - point format mode</h3> <h2>REFERENCES</h2> -<a href="sql.html">SQL command notes</a> for creating databases +<a href="sql.html">SQL command notes</a> for creating databases, <br> <a href="vectorascii.html">GRASS ASCII vector format</a> specification diff --git a/vector/v.in.dxf/read_dxf.c b/vector/v.in.dxf/read_dxf.c index 8cab43b95dd..9281f778175 100644 --- a/vector/v.in.dxf/read_dxf.c +++ b/vector/v.in.dxf/read_dxf.c @@ -10,11 +10,19 @@ struct dxf_file *dxf_open(char *file) struct dxf_file *dxf; dxf = (struct dxf_file *)G_malloc(sizeof(struct dxf_file)); + if (!dxf) + return NULL; dxf->name = G_store(file); - if (!(dxf->fp = fopen(file, "r"))) + if (!dxf->name) { + G_free(dxf); return NULL; - + } + if (!(dxf->fp = fopen(file, "r"))) { + G_free(dxf->name); + G_free(dxf); + return NULL; + } /* get the file size */ G_fseek(dxf->fp, 0L, SEEK_END); dxf->size = G_ftell(dxf->fp); diff --git a/vector/v.in.lidar/testsuite/mask_test.py b/vector/v.in.lidar/testsuite/mask_test.py index 407c02224a8..99244e72a23 100644 --- a/vector/v.in.lidar/testsuite/mask_test.py +++ b/vector/v.in.lidar/testsuite/mask_test.py @@ -9,6 +9,11 @@ for details. """ +import os + +from grass.gunittest.case import TestCase +from grass.gunittest.main import test + POINTS = """\ 17.46938776,18.67346939,1 20.93877551,17.44897959,2 @@ -77,11 +82,6 @@ """ -import os -from grass.gunittest.case import TestCase -from grass.gunittest.main import test - - class VectorMaskTest(TestCase): """Test case for watershed module diff --git a/vector/v.in.lidar/v.in.lidar.html b/vector/v.in.lidar/v.in.lidar.html index 72177b9f5ed..a71c02ee101 100644 --- a/vector/v.in.lidar/v.in.lidar.html +++ b/vector/v.in.lidar/v.in.lidar.html @@ -1,7 +1,7 @@ <h2>DESCRIPTION</h2> <em>v.in.lidar</em> converts LiDAR point clouds in LAS format to a GRASS -vector, using the <a href="http://www.liblas.org">libLAS</a> library. +vector, using the <a href="https://liblas.org">libLAS</a> library. The created vector is true 3D with x, y, z coordinates. <p> @@ -80,8 +80,8 @@ <h2>Project Creation</h2> information, and if the LAS driver supports it. If the source dataset CRS does not match the CRS of the current project (previously called location) <em>v.in.lidar</em> will -report an error message ("<tt>Coordinate reference system of dataset does not appear to -match current project</tt>") and then report the PROJ_INFO parameters of +report an error message ("<code>Coordinate reference system of dataset does not appear to +match current project</code>") and then report the PROJ_INFO parameters of the source dataset. <p>If the user wishes to ignore the difference between the coordinate @@ -105,30 +105,28 @@ <h2>NOTES</h2> <h2>EXAMPLE</h2> -This example is analogous to the example used in the GRASS wiki page for -<a href="https://grasswiki.osgeo.org/wiki/LIDAR#Import_LAS_as_vector_points">importing LAS as vector points</a>. -<p>The sample LAS data are in the file "Serpent Mound Model LAS Data.las", +The sample LAS data are in the file "Serpent Mound Model LAS Data.laz", available at -<a href="http://www.appliedimagery.com/downloads/sampledata/Serpent%20Mound%20Model%20LAS%20Data.las">appliedimagery.com</a> +<a href="https://github.com/PDAL/data/raw/4ee9ee43b195268a59113555908c1c0cdf955bd4/liblas/Serpent%20Mound%20Model%20LAS%20Data.laz">Serpent Mound Model LAS Data.laz</a> <div class="code"><pre> - # print LAS file info - v.in.lidar -p input="Serpent Mound Model LAS Data.las" +# print LAS file info +v.in.lidar -p input="Serpent Mound Model LAS Data.laz" - # create a project with CRS information of the LAS data - v.in.lidar -i input="Serpent Mound Model LAS Data.las" project=Serpent_Mound +# create a project with CRS information of the LAS data +v.in.lidar -i input="Serpent Mound Model LAS Data.laz" project=Serpent_Mound - # quit and restart GRASS in the newly created project "Serpent_Mound" - # real import of LiDAR LAS data, without topology and without attribute table - v.in.lidar -tb input="Serpent Mound Model LAS Data.las" output=Serpent_Mound_Model_pts +# quit and restart GRASS in the newly created project "Serpent_Mound" +# real import of LiDAR LAS data, without topology and without attribute table +v.in.lidar -tb input="Serpent Mound Model LAS Data.laz" output=Serpent_Mound_Model_pts </pre></div> <h2>REFERENCES</h2> <a href="https://www.asprs.org/committee-general/laser-las-file-format-exchange-activities.html"> ASPRS LAS format</a><br> -<a href="http://www.liblas.org/">LAS library</a> <br> -<a href="http://test.liblas.org/doxygen/liblas_8h.htm">LAS library C API</a> documentation +<a href="https://liblas.org/">LAS library</a> <br> +<a href="https://liblas.org/doxygen/liblas_8h.html">LAS library C API</a> documentation <h2>SEE ALSO</h2> diff --git a/vector/v.in.ogr/v.in.ogr.html b/vector/v.in.ogr/v.in.ogr.html index 2ab3ca938a6..e899a8a5701 100644 --- a/vector/v.in.ogr/v.in.ogr.html +++ b/vector/v.in.ogr/v.in.ogr.html @@ -23,11 +23,11 @@ <h3>Supported Vector Formats</h3> <em>v.in.ogr</em> uses the OGR library which supports various vector data formats including -<a href="https://gdal.org/drivers/vector/shapefile.html">ESRI Shapefile</a>, -<a href="https://gdal.org/drivers/vector/mitab.html">Mapinfo File</a>, UK .NTF, +<a href="https://gdal.org/en/stable/drivers/vector/shapefile.html">ESRI Shapefile</a>, +<a href="https://gdal.org/en/stable/drivers/vector/mitab.html">Mapinfo File</a>, UK .NTF, SDTS, TIGER, IHO S-57 (ENC), DGN, GML, GPX, AVCBin, REC, Memory, OGDI, and PostgreSQL, depending on the local OGR installation. For details -see the <a href="https://gdal.org/drivers/vector/">OGR format overview</a>. +see the <a href="https://gdal.org/en/stable/drivers/vector/">OGR format overview</a>. The <b>-f</b> prints a list of the vector formats supported by the system's OGR (Simple Features Library). The OGR (Simple Features Library) is part of the <a href="https://gdal.org">GDAL</a> library, @@ -142,7 +142,7 @@ <h3>File encoding</h3> ISO8859_1 which may not be appropriate for many languages. Unfortunately it is not clear what other values may be appropriate (see example below). To change encoding the user can set -up <tt><a href="https://gdal.org/user/configoptions.html">SHAPE_ENCODING</a></tt> +up <code><a href="https://gdal.org/en/stable/user/configoptions.html">SHAPE_ENCODING</a></code> environmental variable or simply to define encoding value using <b>encoding</b> parameter. @@ -257,7 +257,7 @@ <h3>PostGIS tables</h3> </pre></div> Generally, <em>v.in.ogr</em> just follows the -<a href="https://gdal.org/drivers/vector/">format-specific</a> +<a href="https://gdal.org/en/stable/drivers/vector/">format-specific</a> syntax defined by the OGR library. @@ -265,14 +265,14 @@ <h3>PostGIS tables</h3> <h3>Default connection settings as datasource (PostgreSQL only)</h3> If datasource (<b>input</b>) is specified as 'PG:' and the default DB -driver is <a href="grass-pg.html">PostgreSQL</a> (<tt>pg</tt>) than +driver is <a href="grass-pg.html">PostgreSQL</a> (<code>pg</code>) than the connection string is determined from the default DB settings, see examples below. <p> For schema support, first set a default schema with <em><a href="db.connect.html">db.connect</a></em>. If schema support -is used the schema name must be specified whenever a <tt>db.*</tt> +is used the schema name must be specified whenever a <code>db.*</code> module is called. User and password for connection to the database can be specified by <em><a href="db.login.html">db.login</a></em>. @@ -301,7 +301,7 @@ <h3>Default connection settings as datasource (PostgreSQL only)</h3> <h3>OpenStreetMap (OSM)</h3> -<a href="https://gdal.org/drivers/vector/osm.html">OSM data</a> are available in +<a href="https://gdal.org/en/stable/drivers/vector/osm.html">OSM data</a> are available in .osm (XML based) and .pbf (optimized binary) formats. The .pbf format is recommended because file sizes are smaller. The OSM driver will categorize features into 5 layers : @@ -357,8 +357,8 @@ <h3>OpenStreetMap (OSM)</h3> <h3>Oracle Spatial</h3> -Note that you have to set the environment-variables <tt>ORACLE_BASE, -ORACLE_SID, ORACLE_HOME</tt> and <tt>TNS_ADMIN</tt> accordingly. +Note that you have to set the environment-variables <code>ORACLE_BASE, +ORACLE_SID, ORACLE_HOME</code> and <code>TNS_ADMIN</code> accordingly. <div class="code"><pre> v.in.ogr input=OCI:username/password@database_instance output=grasslayer layer=roads_oci @@ -461,7 +461,7 @@ <h2>REFERENCES</h2> <ul> <li><a href="https://gdal.org/">OGR vector library</a></li> - <li><a href="https://gdal.org/api/vector_c_api.html">OGR vector library C API</a> documentation</li> + <li><a href="https://gdal.org/en/stable/api/vector_c_api.html">OGR vector library C API</a> documentation</li> </ul> <h2>SEE ALSO</h2> diff --git a/vector/v.in.pdal/v.in.pdal.html b/vector/v.in.pdal/v.in.pdal.html index 1042f8415bf..d5832c93cff 100644 --- a/vector/v.in.pdal/v.in.pdal.html +++ b/vector/v.in.pdal/v.in.pdal.html @@ -27,7 +27,7 @@ <h2>REFERENCES</h2> V. Petras, A. Petrasova, J. Jeziorska, H. Mitasova (2016): <em>Processing UAV and lidar point clouds in GRASS GIS</em>. XXIII ISPRS Congress 2016 - [<a href="http://www.int-arch-photogramm-remote-sens-spatial-inf-sci.net/XLI-B7/945/2016/">ISPRS Archives</a>, + [<a href="https://doi.org/10.5194/isprs-archives-XLI-B7-945-2016">ISPRS Archives</a>, <a href="https://www.researchgate.net/publication/304340172_Processing_UAV_and_lidar_point_clouds_in_GRASS_GIS">ResearchGate</a>]</li> </ul> @@ -43,4 +43,4 @@ <h2>SEE ALSO</h2> <h2>AUTHOR</h2> Vaclav Petras, -<a href="http://geospatial.ncsu.edu/osgeorel/">NCSU GeoForAll Lab</a> +<a href="https://geospatial.ncsu.edu/geoforall/">NCSU GeoForAll Lab</a> diff --git a/vector/v.label/v.label.html b/vector/v.label/v.label.html index 0331e6ff8a9..f697a882a9a 100644 --- a/vector/v.label/v.label.html +++ b/vector/v.label/v.label.html @@ -12,7 +12,7 @@ <h2>NOTES</h2> as the <em>size</em> option. <p> A description of the labels file follows.<p> -The file is located in <tt>$MAPSET/paint/labels/</tt>. +The file is located in <code>$MAPSET/paint/labels/</code>. The file is a plain-text ASCII file containing the following fields: <h4>Caution: The following information may be incomplete, out of date, and wrong!</h4> @@ -24,8 +24,8 @@ <h4>Caution: The following information may be incomplete, out of date, and wrong <dd><!--Up to four lines of text.--> Lines in multiple line labels will appear one above the next. More than one line of text can be specified by notating the end of a -line with a '<b><tt>\n</tt></b>'.<br> -(e.g. <tt>SPEARFISH<b>\n</b>SOUTH DAKOTA)</tt>. +line with a '<b><code>\n</code></b>'.<br> +(e.g. <code>SPEARFISH<b>\n</b>SOUTH DAKOTA)</code>. <!-- I don't think it exists. -HB 6/2005 <dt><b>SKIP</b>: @@ -105,11 +105,11 @@ <h4>Caution: The following information may be incomplete, out of date, and wrong <ol> <li>By color name:<br> -<tt>aqua black blue brown cyan gray green grey indigo -magenta orange purple red violet white yellow</tt></li> +<code>aqua black blue brown cyan gray green grey indigo +magenta orange purple red violet white yellow</code></li> <li>As red, green, blue component values. (0-255)<br> -for example: <tt>128:100:200</tt></li> +for example: <code>128:100:200</code></li> <!-- eh? <li>As red, green, blue percentages. @@ -124,7 +124,7 @@ <h4>Caution: The following information may be incomplete, out of date, and wrong <em><a href="d.labels.html">d.labels</a></em>.) --> -<li>Specify "<tt>none</tt>" to suppress the lettering.</li> +<li>Specify "<code>none</code>" to suppress the lettering.</li> </ol> @@ -139,9 +139,9 @@ <h4>Caution: The following information may be incomplete, out of date, and wrong appears to be in two colors. The text is drawn first in this color at a wider line width, and then redrawn in the text color at the regular line width. No highlight color -("<tt>none</tt>") is used by default, if unspecified by the +("<code>none</code>") is used by default, if unspecified by the user. To specify use of no highlight color, specify -"<tt>none</tt>". +"<code>none</code>". (See <a href="#textcolor">TEXT COLOR</a> above for a list of permissible color names.) @@ -157,7 +157,7 @@ <h4>Caution: The following information may be incomplete, out of date, and wrong <dt><b>BACKGROUND COLOR</b>: <dd>Text may be boxed in a solid color by specifying a background color. -Specify "<tt>none</tt>" for no background. The default background color +Specify "<code>none</code>" for no background. The default background color setting, if unspecified by the user, is <em>white</em>. (See <a href="#textcolor">TEXT COLOR</a> above for a list of permissible color names.) @@ -166,7 +166,7 @@ <h4>Caution: The following information may be incomplete, out of date, and wrong <dt><b>BORDER COLOR</b>: <dd>Select a color for the border around the background. -Specify "<tt>none</tt>" to suppress the border. +Specify "<code>none</code>" to suppress the border. The default border color used, if unspecified, is <em>black</em>. (See <a href="#textcolor">TEXT COLOR</a> above for a list of permissible color names.) @@ -197,7 +197,7 @@ <h2>EXAMPLE</h2> Since the label files are simple text files, you can merge them together if you like. For example if you set the label colors based on database attributes using multiple runs with the <b>where</b> option. -This example uses the standard UNIX <tt>cat</tt> program. +This example uses the standard UNIX <code>cat</code> program. <div class="code"><pre> cd $MAPSET/paint/labels/ diff --git a/vector/v.lidar.correction/v.lidar.correction.html b/vector/v.lidar.correction/v.lidar.correction.html index 5e45edc02f6..29d1bab645d 100644 --- a/vector/v.lidar.correction/v.lidar.correction.html +++ b/vector/v.lidar.correction/v.lidar.correction.html @@ -90,7 +90,7 @@ <h2>REFERENCES</h2> <br> <br> Performances of the filter can be seen in the -<a href="http://www.itc.nl/isprswgIII-3/filtertest/MainDoc.htm">ISPRS WG III/3 Comparison of Filters</a> +<a href="https://www.itc.nl/isprs/wgIII-3/filtertest/">ISPRS WG III/3 Comparison of Filters</a> report by Sithole, G. and Vosselman, G., 2003. <h2>SEE ALSO</h2> diff --git a/vector/v.lidar.edgedetection/v.lidar.edgedetection.html b/vector/v.lidar.edgedetection/v.lidar.edgedetection.html index 08fd672d266..77f0efe6e0b 100644 --- a/vector/v.lidar.edgedetection/v.lidar.edgedetection.html +++ b/vector/v.lidar.edgedetection/v.lidar.edgedetection.html @@ -142,7 +142,7 @@ <h2>REFERENCES</h2> <li>Brovelli M. A., Cannata M. and Longoni U.M., 2002. DTM LIDAR in area urbana, Bollettino SIFET N.2, pp. 7-26.</li> <li>Performances of the filter can be seen in the -<a href="http://www.itc.nl/isprswgIII-3/filtertest/MainDoc.htm">ISPRS WG III/3 Comparison of Filters</a> +<a href="https://www.itc.nl/isprs/wgIII-3/filtertest/">ISPRS WG III/3 Comparison of Filters</a> report by Sithole, G. and Vosselman, G., 2003.</li> </ul> diff --git a/vector/v.lidar.growing/v.lidar.growing.html b/vector/v.lidar.growing/v.lidar.growing.html index d98309eb59c..58c76c59fb2 100644 --- a/vector/v.lidar.growing/v.lidar.growing.html +++ b/vector/v.lidar.growing/v.lidar.growing.html @@ -68,7 +68,7 @@ <h2>REFERENCES</h2> Bollettino SIFET N.2, pp. 7-26. <p> Performances of the filter can be seen in the -<a href="http://www.itc.nl/isprswgIII-3/filtertest/MainDoc.htm">ISPRS WG III/3 Comparison of Filters</a> +<a href="https://www.itc.nl/isprs/wgIII-3/filtertest/">ISPRS WG III/3 Comparison of Filters</a> report by Sithole, G. and Vosselman, G., 2003. <h2>SEE ALSO</h2> diff --git a/vector/v.lrs/lrs.html b/vector/v.lrs/lrs.html index edc517788cd..63d99b6c1fd 100644 --- a/vector/v.lrs/lrs.html +++ b/vector/v.lrs/lrs.html @@ -167,14 +167,16 @@ <h2>NOTES</h2> <h2>SEE ALSO</h2> -<em>R. Blazek, 2004, <a href="http://gisws.media.osaka-cu.ac.jp/grass04/viewpaper.php?id=50">Introducing the Linear Reference System in GRASS</a>, Bangkok, GRASS User Conf. Proc.</em><br> -<em>R. Blazek, 2005, <a href="http://creativecity.gscc.osaka-cu.ac.jp/IJG/article/download/320/321">Introducing the Linear Reference System in GRASS</a>, International Journal of Geoinformatics, Vol. 1(3), pp. 95-100</em><br> +<em>R. Blazek, 2004, <a href="https://foss4g.asia/2004/Full%20Paper_PDF/Introducing%20the%20Linear%20Reference%20System%20in%20GRASS.pdf">Introducing the Linear Reference System in GRASS</a>, Bangkok, GRASS User Conf. Proc.</em><br> +<em>R. Blazek, 2005, <a href="https://web.archive.org/web/20240814152234/http://creativecity.gscc.osaka-cu.ac.jp/IJG/article/download/320/321">Introducing the Linear Reference System in GRASS</a>, International Journal of Geoinformatics, Vol. 1(3), pp. 95-100</em><br> <p> -<em><a href="v.build.polylines.html">v.build.polylines</a></em>, -<em><a href="v.lrs.create.html">v.lrs.create</a></em>, -<em><a href="v.lrs.segment.html">v.lrs.segment</a></em>, -<em><a href="v.lrs.where.html">v.lrs.where</a></em>, -<em><a href="v.lrs.label.html">v.lrs.label</a></em> +<em> +<a href="v.build.polylines.html">v.build.polylines</a>, +<a href="v.lrs.create.html">v.lrs.create</a>, +<a href="v.lrs.segment.html">v.lrs.segment</a>, +<a href="v.lrs.where.html">v.lrs.where</a>, +<a href="v.lrs.label.html">v.lrs.label</a> +</em> <h2>AUTHORS</h2> diff --git a/vector/v.lrs/v.lrs.create/v.lrs.create.html b/vector/v.lrs/v.lrs.create/v.lrs.create.html index 518bb80fedf..eb969065186 100644 --- a/vector/v.lrs/v.lrs.create/v.lrs.create.html +++ b/vector/v.lrs/v.lrs.create/v.lrs.create.html @@ -150,7 +150,7 @@ <h2>SEE ALSO</h2> <p> <em> <a href="lrs.html">LRS tutorial</a>,<br> -<a href="http://gisws.media.osaka-cu.ac.jp/grass04/viewpaper.php?id=50">Introducing the Linear Reference System in GRASS</a> +<a href="https://foss4g.asia/2004/Full%20Paper_PDF/Introducing%20the%20Linear%20Reference%20System%20in%20GRASS.pdf">Introducing the Linear Reference System in GRASS</a> </em> <h2>AUTHORS</h2> diff --git a/vector/v.lrs/v.lrs.label/v.lrs.label.html b/vector/v.lrs/v.lrs.label/v.lrs.label.html index dbd2f64ac67..fb96858b486 100644 --- a/vector/v.lrs/v.lrs.label/v.lrs.label.html +++ b/vector/v.lrs/v.lrs.label/v.lrs.label.html @@ -33,7 +33,7 @@ <h2>SEE ALSO</h2> <p> <em> <a href="lrs.html">LRS tutorial</a>,<br> -<a href="http://gisws.media.osaka-cu.ac.jp/grass04/viewpaper.php?id=50">Introducing the Linear Reference System in GRASS</a> +<a href="https://foss4g.asia/2004/Full%20Paper_PDF/Introducing%20the%20Linear%20Reference%20System%20in%20GRASS.pdf">Introducing the Linear Reference System in GRASS</a> </em> <h2>AUTHORS</h2> diff --git a/vector/v.lrs/v.lrs.segment/v.lrs.segment.html b/vector/v.lrs/v.lrs.segment/v.lrs.segment.html index 0a2b39dfd1f..259b54ae048 100644 --- a/vector/v.lrs/v.lrs.segment/v.lrs.segment.html +++ b/vector/v.lrs/v.lrs.segment/v.lrs.segment.html @@ -1,7 +1,7 @@ <h2>DESCRIPTION</h2> <em>v.lrs.segment</em> creates points/segments from input lines, -linear reference system and positions read from <tt>standard in</tt> +linear reference system and positions read from <code>standard in</code> or a file. <p> The format is as follows:<br> @@ -56,7 +56,7 @@ <h2>SEE ALSO</h2> <p> <em> <a href="lrs.html">LRS tutorial</a>,<br> -<a href="http://gisws.media.osaka-cu.ac.jp/grass04/viewpaper.php?id=50">Introducing the Linear Reference System in GRASS</a> +<a href="https://foss4g.asia/2004/Full%20Paper_PDF/Introducing%20the%20Linear%20Reference%20System%20in%20GRASS.pdf">Introducing the Linear Reference System in GRASS</a> </em> <h2>AUTHOR</h2> diff --git a/vector/v.lrs/v.lrs.where/v.lrs.where.html b/vector/v.lrs/v.lrs.where/v.lrs.where.html index cdf9651f389..e8c81834f8e 100644 --- a/vector/v.lrs/v.lrs.where/v.lrs.where.html +++ b/vector/v.lrs/v.lrs.where/v.lrs.where.html @@ -45,7 +45,7 @@ <h2>SEE ALSO</h2> <p> <em> <a href="lrs.html">LRS tutorial</a>,<br> -<a href="http://gisws.media.osaka-cu.ac.jp/grass04/viewpaper.php?id=50">Introducing the Linear Reference System in GRASS</a> +<a href="https://foss4g.asia/2004/Full%20Paper_PDF/Introducing%20the%20Linear%20Reference%20System%20in%20GRASS.pdf">Introducing the Linear Reference System in GRASS</a> </em> <h2>AUTHORS</h2> diff --git a/vector/v.mkgrid/v.mkgrid.html b/vector/v.mkgrid/v.mkgrid.html index a1e7d87a6df..3b9433f761d 100644 --- a/vector/v.mkgrid/v.mkgrid.html +++ b/vector/v.mkgrid/v.mkgrid.html @@ -157,8 +157,8 @@ <h3>Using hexagons for point density</h3> since the hexagons cannot cover all the area around the edges (the computational region extent needs to be enlarged if all points should be considered). -The last command sets the vector map color table to <tt>viridis</tt> -based on the <tt>count</tt> column. +The last command sets the vector map color table to <code>viridis</code> +based on the <code>count</code> column. <div class="code"><pre> v.colors map=hexagons use=attr column=count color=viridis diff --git a/vector/v.net.path/v.net.path.html b/vector/v.net.path/v.net.path.html index 60b26f3a591..216f35b103f 100644 --- a/vector/v.net.path/v.net.path.html +++ b/vector/v.net.path/v.net.path.html @@ -16,10 +16,10 @@ <h2>DESCRIPTION</h2> and also different costs for both directions of a vector line. For areas, costs will be calculated along boundary lines. <p> -The input vector needs to be prepared with <tt>v.net operation=connect</tt> +The input vector needs to be prepared with <code>v.net operation=connect</code> in order to connect points representing center nodes to the network. -<p>Nodes and arcs can be closed using <tt>cost = -1</tt>. +<p>Nodes and arcs can be closed using <code>cost = -1</code>. <p>Least cost paths are written to the output vector map with an attached attribute table. <p>Nodes can be @@ -43,7 +43,7 @@ <h2>DESCRIPTION</h2> <p> Points specified by category must be exactly on network nodes, and the -input vector map needs to be prepared with <tt>v.net operation=connect</tt>. +input vector map needs to be prepared with <code>v.net operation=connect</code>. <p> When specifying coordinates, the next network node to a given coordinate pair is used. @@ -52,19 +52,19 @@ <h2>DESCRIPTION</h2> The attribute table will contain the following attributes: <ul> - <li><tt>cat</tt> - path unique category assigned by module</li> - <li><tt>id</tt> - path id (read from input)</li> - <li><tt>fcat</tt> - from point category</li> - <li><tt>tcat</tt> - to point category</li> - <li><tt>sp</tt> - result status: + <li><code>cat</code> - path unique category assigned by module</li> + <li><code>id</code> - path id (read from input)</li> + <li><code>fcat</code> - from point category</li> + <li><code>tcat</code> - to point category</li> + <li><code>sp</code> - result status: <ul> <li> 0 - OK, path found</li> <li> 1 - node is not reachable</li> <li> 2 - point of given category does not exist</li> </ul></li> - <li><tt>cost</tt> - travelling costs (on the network, not to/from network)</li> - <li><tt>fdist</tt> - the distance from first point to the network</li> - <li><tt>tdist</tt> - the distance from the network to second point</li> + <li><code>cost</code> - travelling costs (on the network, not to/from network)</li> + <li><code>fdist</code> - the distance from first point to the network</li> + <li><code>tdist</code> - the distance from the network to second point</li> </ul> <p> @@ -84,7 +84,7 @@ <h2>DESCRIPTION</h2> <h2>NOTES</h2> -Nodes and arcs can be closed using <tt>cost = -1</tt>. +Nodes and arcs can be closed using <code>cost = -1</code>. <p>If the cost columns <b>arc_column</b>, <b>arc_backward_column</b> and <b>node_column</b> are not specified, the length of network segments is measured and zero costs are assumed for nodes. @@ -92,7 +92,7 @@ <h2>NOTES</h2> accurate results, the line length must be taken into account when assigning costs as attributes. For example, to get the <em>fastest path</em>, the columns 'max_speed' and 'length' are required. The correct fastest -path can then be found by specifying <tt>arc_column=length/max_speed</tt>. If not yet +path can then be found by specifying <code>arc_column=length/max_speed</code>. If not yet existing, the column containing the line length ("length") has to added to the attributes table using <em><a href="v.to.db.html">v.to.db</a></em>. diff --git a/vector/v.net/arcs.c b/vector/v.net/arcs.c index 20554fc19f9..2ea03111041 100644 --- a/vector/v.net/arcs.c +++ b/vector/v.net/arcs.c @@ -32,7 +32,6 @@ int create_arcs(FILE *file, struct Map_info *Pnts, struct Map_info *Out, points = Vect_new_line_struct(); points2 = Vect_new_line_struct(); - points = Vect_new_line_struct(); cats = Vect_new_cats_struct(); narcs = 0; diff --git a/vector/v.net/v.net.html b/vector/v.net/v.net.html index baef25e5484..c9a8cb57eb5 100644 --- a/vector/v.net/v.net.html +++ b/vector/v.net/v.net.html @@ -39,9 +39,9 @@ <h2>DESCRIPTION</h2> category. In order to assign unique costs to each line, a new layer needs to be created with<br> <!-- like this it is rather useless, still to be improved (eg as a full example below!) --> -<tt>v.category input=yourmap option=add cat=1 step=1 layer=3 output=newmap</tt><br> +<code>v.category input=yourmap option=add cat=1 step=1 layer=3 output=newmap</code><br> followed by<br> -<tt>v.db.addtable map=newmap layer=3 table=tablename</tt>.</li> +<code>v.db.addtable map=newmap layer=3 table=tablename</code>.</li> <li>Create nodes and arcs from a vector line/boundary file using the <em>node</em> operation. This is useful if you are mostly interested @@ -173,7 +173,7 @@ <h3>Merge in nodes from a separate map within given threshold</h3> v.category streets_net option=report </pre></div> -The nodes are stored in layer 2 unless <tt>node_layer=1</tt> is used. +The nodes are stored in layer 2 unless <code>node_layer=1</code> is used. <h3>Generating network for vector point map</h3> diff --git a/vector/v.out.ascii/v.out.ascii.html b/vector/v.out.ascii/v.out.ascii.html index 964df04b084..e87bcba9075 100644 --- a/vector/v.out.ascii/v.out.ascii.html +++ b/vector/v.out.ascii/v.out.ascii.html @@ -19,17 +19,17 @@ <h2>NOTES</h2> <p>If old version is requested, the <b>output</b> files from <em>v.out.ascii</em> is placed in -the <tt>$LOCATION/$MAPSET/dig_ascii/</tt> -and <tt>$LOCATION/$MAPSET/dig_att</tt> directory. +the <code>$LOCATION/$MAPSET/dig_ascii/</code> +and <code>$LOCATION/$MAPSET/dig_att</code> directory. <p>If <b>layer > 0</b> then only features with a category number will be exported. Use <em><a href="v.category.html">v.category</a></em> to add them if needed or define <b>layer=-1</b> to export also features without category. <p><em>v.out.ascii</em> in the old version mode (<b>-o</b>) does not -copy the <tt>dig_cats</tt> file associated with the binary +copy the <code>dig_cats</code> file associated with the binary vector <b>input</b> map to the new <b>output</b> file name. The user -must copy the <tt>dig_cats</tt> file to the new <b>output</b> name if +must copy the <code>dig_cats</code> file to the new <b>output</b> name if this is desired (e.g. using the UNIX <em>cp</em> command). <p>It is possible to output the coordinates of vertices in a non-points vector diff --git a/vector/v.out.dxf/v.out.dxf.html b/vector/v.out.dxf/v.out.dxf.html index 95057f05f22..1cf45b3d3e0 100644 --- a/vector/v.out.dxf/v.out.dxf.html +++ b/vector/v.out.dxf/v.out.dxf.html @@ -8,7 +8,7 @@ <h2>DESCRIPTION</h2> <h2>NOTES</h2> -DXF files output by AutoCAD have the suffix <tt>.dxf</tt> +DXF files output by AutoCAD have the suffix <code>.dxf</code> <h2>REFERENCES</h2> diff --git a/vector/v.out.lidar/v.out.lidar.html b/vector/v.out.lidar/v.out.lidar.html index 2d0708d98ae..295812480e0 100644 --- a/vector/v.out.lidar/v.out.lidar.html +++ b/vector/v.out.lidar/v.out.lidar.html @@ -1,7 +1,7 @@ <h2>DESCRIPTION</h2> <em>v.out.lidar</em> converts GRASS vector map to a LiDAR point clouds -in LAS format using the <a href="http://www.liblas.org">libLAS</a> library. +in LAS format using the <a href="https://liblas.org">libLAS</a> library. <p> The <b>-r</b> flag limits the export to the current computational region @@ -20,7 +20,7 @@ <h2>NOTES</h2> The typical file extensions for the LAS format are .las and .laz (compressed). The compressed LAS (.laz) format can be exported only if libLAS has been -compiled with <a href="http://www.laszip.org/">LASzip</a> support. +compiled with <a href="https://laszip.org/">LASzip</a> support. It is also good when libLAS was compiled with GDAL. This is needed when working with projections. @@ -39,7 +39,7 @@ <h2>REFERENCES</h2> <a href="https://www.asprs.org/committee-general/laser-las-file-format-exchange-activities.html"> ASPRS LAS format</a><br> -<a href="http://www.liblas.org/">LAS library</a> <br> +<a href="https://liblas.org/">LAS library</a> <br> <h2>SEE ALSO</h2> diff --git a/vector/v.out.ogr/testsuite/test_v_out_ogr.py b/vector/v.out.ogr/testsuite/test_v_out_ogr.py index d269ccff606..373ec8e1273 100644 --- a/vector/v.out.ogr/testsuite/test_v_out_ogr.py +++ b/vector/v.out.ogr/testsuite/test_v_out_ogr.py @@ -8,7 +8,6 @@ class TestOgrExport(TestCase): - # Vector map in NC test dataset test_map = "boundary_county" diff --git a/vector/v.out.ogr/v.out.ogr.html b/vector/v.out.ogr/v.out.ogr.html index 664a5c35a1a..e97d194281d 100644 --- a/vector/v.out.ogr/v.out.ogr.html +++ b/vector/v.out.ogr/v.out.ogr.html @@ -13,15 +13,15 @@ <h2>DESCRIPTION</h2> The OGR library supports many various formats including: <ul> - <li><a href="https://gdal.org/drv_geopackage.html">OGC GeoPackage</a></li> - <li><a href="https://gdal.org/drv_shapefile.html">ESRI Shapefile</a></li> - <li><a href="https://gdal.org/drv_pg.html">PostGIS</a></li> - <li><a href="https://gdal.org/drv_sqlite.html">SpatiaLite</a></li> - <li><a href="https://gdal.org/drv_csv.html">CSV</a></li> - <li><a href="https://gdal.org/drv_gml.html">GML</a></li> - <li><a href="https://gdal.org/drv_kml.html">KML</a></li> - <li><a href="https://gdal.org/drv_mitab.html">Mapinfo File</a></li> - <li><a href="https://gdal.org/drv_tiger.html">TIGER</a></li> + <li><a href="https://gdal.org/en/stable/drivers/vector/gpkg.html">OGC GeoPackage</a></li> + <li><a href="https://gdal.org/en/stable/drivers/vector/shapefile.html">ESRI Shapefile</a></li> + <li><a href="https://gdal.org/en/stable/drivers/vector/pg.html">PostGIS</a></li> + <li><a href="https://gdal.org/en/stable/drivers/vector/sqlite.html">SpatiaLite</a></li> + <li><a href="https://gdal.org/en/stable/drivers/vector/csv.html">CSV</a></li> + <li><a href="https://gdal.org/en/stable/drivers/vector/gml.html">GML</a></li> + <li><a href="https://gdal.org/en/stable/drivers/vector/kml.html">KML</a></li> + <li><a href="https://gdal.org/en/stable/drivers/vector/mitab.html">Mapinfo File</a></li> + <li><a href="https://gdal.org/en/stable/drivers/vector/tiger.html">TIGER</a></li> <li>... and many others</li> </ul> @@ -29,7 +29,7 @@ <h2>DESCRIPTION</h2> <p> For further available other supported formats go -<a href="https://gdal.org/drivers/vector/">here</a>. +<a href="https://gdal.org/en/stable/drivers/vector/">here</a>. <h2>NOTES</h2> @@ -63,7 +63,7 @@ <h2>NOTES</h2> <p> Performance: export to SQLite based formats including OGC GeoPackage may -become faster with the environmental variable <tt>OGR_SQLITE_CACHE=1024</tt> +become faster with the environmental variable <code>OGR_SQLITE_CACHE=1024</code> being set (value depends on available RAM, see <a href="https://trac.osgeo.org/gdal/wiki/ConfigOptions#OGR_SQLITE_CACHE">OGR ConfigOptions</a>). diff --git a/vector/v.out.postgis/v.out.postgis.html b/vector/v.out.postgis/v.out.postgis.html index 2307ce6a394..0c0906097db 100644 --- a/vector/v.out.postgis/v.out.postgis.html +++ b/vector/v.out.postgis/v.out.postgis.html @@ -6,48 +6,48 @@ <h2>DESCRIPTION</h2> <p> By default GRASS GIS topological features are converted into simple features -(see <a href="http://www.opengeospatial.org/standards/sfa">OGC Simple +(see <a href="https://www.ogc.org/publications/standard/sfa/">OGC Simple Feature Access</a> specification for details). Flag <b>-l</b> allows to export vector features as topological elements stored -in <a href="http://postgis.refractions.net/docs/Topology.html">PostGIS +in <a href="https://postgis.net/docs/Topology.html">PostGIS Topology</a> schema. Note that topological export requires PostGIS version 2 or later. <p> Additional creation options can be defined by <b>options</b> parameter: <ul> - <li><tt>FID=<column></tt> - name of column which will be used as - primary key (feature id), default: <tt>fid</tt></li> - <li><tt>GEOMETRY_NAME=<column></tt> name of column which will - be used for storing geometry data in feature table, default: <tt>geom</tt></li> - <li><tt>SPATIAL_INDEX=YES|NO</tt> - enable/disable creating spatial - index on geometry column, default: <tt>YES</tt></li> - <li><tt>PRIMARY_KEY=YES|NO</tt> - enable/disable adding primary key - on FID column, default: <tt>YES</tt></li> - <li><tt>SRID=<value></tt> - spatial reference identifier, + <li><code>FID=<column></code> - name of column which will be used as + primary key (feature id), default: <code>fid</code></li> + <li><code>GEOMETRY_NAME=<column></code> name of column which will + be used for storing geometry data in feature table, default: <code>geom</code></li> + <li><code>SPATIAL_INDEX=YES|NO</code> - enable/disable creating spatial + index on geometry column, default: <code>YES</code></li> + <li><code>PRIMARY_KEY=YES|NO</code> - enable/disable adding primary key + on FID column, default: <code>YES</code></li> + <li><code>SRID=<value></code> - spatial reference identifier, default: not defined</li> </ul> PostGIS Topology related options (relevant only for <b>-l</b> flag): <ul> - <li><tt>TOPOSCHEMA_NAME=<schema name></tt> - name of PostGIS - Topology schema, default: <tt>topo_<input></tt></li> - <li><tt>TOPOGEOM_NAME=<column></tt> - name of column which + <li><code>TOPOSCHEMA_NAME=<schema name></code> - name of PostGIS + Topology schema, default: <code>topo_<input></code></li> + <li><code>TOPOGEOM_NAME=<column></code> - name of column which will be used for storing topogeometry data in feature table, - default: <tt>topo</tt></li> - <li><tt>TOPO_TOLERANCE=<value></tt> - tolerance for PostGIS + default: <code>topo</code></li> + <li><code>TOPO_TOLERANCE=<value></code> - tolerance for PostGIS Topology schema, - see <a href="http://www.postgis.net/docs/manual-2.0/CreateTopology.html">CreateTopology</a> - function for details, default: <tt>0</tt></li> - <li><tt>TOPO_GEO_ONLY=YES|NO</tt> - store in PostGIS Topology schema + see <a href="https://postgis.net/docs/CreateTopology.html">CreateTopology</a> + function for details, default: <code>0</code></li> + <li><code>TOPO_GEO_ONLY=YES|NO</code> - store in PostGIS Topology schema only data relevant to Topo-Geo data model, - default: <tt>NO</tt></li> + default: <code>NO</code></li> </ul> <p> Creation <b>options</b> are comma-separated pairs -(<tt>key=value</tt>), the options are case-insensitive. Note +(<code>key=value</code>), the options are case-insensitive. Note that <b>options</b> defined by <em><a href="v.external.out.html">v.external.out</a></em> are ignored by <em>v.out.postgis</em>. @@ -63,9 +63,9 @@ <h2>NOTES</h2> areas) become polygons, isles become holes. Geometry of simple feature elements is stored in PostGIS feature table in the column named "geom". Name of the geometry column can be changed -by <b>options=</b><tt>GEOMETRY_NAME=<column></tt>. Note that for +by <b>options=</b><code>GEOMETRY_NAME=<column></code>. Note that for exporting vector features as simple features can be alternatively -used <a href="https://gdal.org/drv_pg.html">PostgreSQL driver</a> +used <a href="https://gdal.org/en/stable/drivers/vector/pg.html">PostgreSQL driver</a> from <a href="https://gdal.org/">OGR</a> library through <em><a href="v.out.ogr.html">v.out.ogr</a></em> module. @@ -83,7 +83,7 @@ <h2>NOTES</h2> <em>v.out.postgis</em> currently supports only three basic output simple feature types: Points, Linestrings and Polygons. Also 3D features of the same type are supported, eg. 3D points are exported -as <tt>PointZ</tt> simple feature. Faces are exported as 3D +as <code>PointZ</code> simple feature. Faces are exported as 3D polygons. 3D features are written to the output automatically if input vector map is 3D. If <b>-2</b> flag is given then the output is always 2D (z-coordinate is silently ignored for 3D input vector maps). @@ -95,7 +95,7 @@ <h2>NOTES</h2> <p> <em>v.out.postgis</em> also allows exporting vector features as <em>topological elements</em> -in <a href="http://postgis.refractions.net/docs/Topology.html">PostGIS +in <a href="https://postgis.net/docs/Topology.html">PostGIS Topology</a> schema. PostGIS Topology extension uses three tables to store basic topological elements which forms topological objects like areas or isles in GRASS terminology. <em>Nodes</em> (0-dimensional @@ -116,9 +116,9 @@ <h2>NOTES</h2> Tables <i>node</i>, <i>edge</i> and <i>face</i> are stored in given topological schema. By default <em>v.out.postgis</em> defines its name -as <tt>topo_<input></tt>. Alternatively, the name for topology +as <code>topo_<input></code>. Alternatively, the name for topology schema can be defined -by <b>options=</b><tt>TOPOSCHEMA_NAME=<name></tt>. +by <b>options=</b><code>TOPOSCHEMA_NAME=<name></code>. <h2>EXAMPLES</h2> @@ -162,7 +162,7 @@ <h3>Export data into specific database schema</h3> Database schema for storing exported data can be defined by <b>output_layer</b> as -<tt><schema_name>.<table_name></tt>. If the specified +<code><schema_name>.<table_name></code>. If the specified schema doesn't exist in the database, then it's automatically created. <p> @@ -177,7 +177,7 @@ <h3>Export data with creation options</h3> Example below demonstrates how to define name for geometry column and disable building spatial index. Spatial reference system is defined -by <tt>srid</tt> identifier which corresponds in this case with EPSG +by <code>srid</code> identifier which corresponds in this case with EPSG 3358 (North Carolina dataset). <div class="code"><pre> @@ -233,7 +233,7 @@ <h3>Export topological data</h3> By default <em>v.out.postgis</em> exports data as simple features. Flag <b>-l</b> allows exporting data as topological elements instead of simple features. Export topological elements is stored in -<a href="http://postgis.refractions.net/docs/Topology.html">PostGIS +<a href="https://postgis.net/docs/Topology.html">PostGIS Topology</a> schema. <div class="code"><pre> @@ -264,8 +264,8 @@ <h2>REQUIREMENTS</h2> <h2>REFERENCES</h2> <ul> - <li><a href="http://www.opengeospatial.org/standards/sfa">OGC Simple Feature Access</a> specification</li> - <li><a href="http://postgis.net/docs/Topology.html">PostGIS Topology</a> documentation</li> + <li><a href="https://www.ogc.org/publications/standard/sfa/">OGC Simple Feature Access</a> specification</li> + <li><a href="https://postgis.net/docs/Topology.html">PostGIS Topology</a> documentation</li> <li><a href="https://grass.osgeo.org/programming8/vlibPg.html">GRASS-PostGIS data provider</a></li> </ul> diff --git a/vector/v.out.vtk/v.out.vtk.html b/vector/v.out.vtk/v.out.vtk.html index 97d8c82c0e7..ff4f2945e5a 100644 --- a/vector/v.out.vtk/v.out.vtk.html +++ b/vector/v.out.vtk/v.out.vtk.html @@ -49,9 +49,9 @@ <h2>NOTES</h2> <li><i>vtk polygons</i> -- representing areas and faces </li> </ul> <p>The VTK file can be visualized with -<em><a href="http://www.vtk.org">VTK Toolkit</a></em>, -<em><a href="http://www.paraview.org">Paraview</a></em> and -<em><a href="http://mayavi.sourceforge.net">MayaVi</a></em>. +<em><a href="https://vtk.org/">VTK Toolkit</a></em>, +<em><a href="https://www.paraview.org/">Paraview</a></em> and +<em><a href="https://github.com/enthought/mayavi">MayaVi</a></em>. <h3>Attention</h3> <p>If areas or faces are exported, the data have to be triangulated within Paraview or diff --git a/vector/v.proj/v.proj.html b/vector/v.proj/v.proj.html index 9910b7258b4..8552b0327f6 100644 --- a/vector/v.proj/v.proj.html +++ b/vector/v.proj/v.proj.html @@ -79,9 +79,9 @@ <h2>REFERENCES</h2> <li> <a href="https://www.asprs.org/asprs-publications/grids-and-datums">ASPRS Grids and Datum</a></li> <li> <a href="http://geotiff.maptools.org/proj_list/">Projections Transform List</a> (PROJ)</li> <li> <a href="https://proj.org/operations/index.html">Coordinate operations</a> by PROJ (projections, conversions, transformations, pipeline operator)</li> - <li> <a href="http://www.mapref.org">MapRef - + <li> <a href="https://mapref.org">MapRef - The Collection of Map Projections and Reference Systems for Europe</a></li> - <li> <a href="http://www.crs-geo.eu">Information and Service System for European Coordinate Reference Systems - CRS</a></li> + <li> <a href="https://www.crs-geo.eu">Information and Service System for European Coordinate Reference Systems - CRS</a></li> </ul> <h2>SEE ALSO</h2> diff --git a/vector/v.random/testsuite/test_v_random.py b/vector/v.random/testsuite/test_v_random.py index 7b68434f46b..52dfc83be8c 100644 --- a/vector/v.random/testsuite/test_v_random.py +++ b/vector/v.random/testsuite/test_v_random.py @@ -9,6 +9,7 @@ License (>=v2). Read the file COPYING that comes with GRASS for details. """ + from grass.gunittest.case import TestCase from grass.gunittest.main import test diff --git a/vector/v.random/v.random.html b/vector/v.random/v.random.html index 5a9ce903d40..abfc25d8c67 100644 --- a/vector/v.random/v.random.html +++ b/vector/v.random/v.random.html @@ -36,7 +36,7 @@ <h3>Restriction to vector areas</h3> <h2>NOTES</h2> Importantly, attributes will only be transferred if <b>layer</b> > 0 -(e.g., <tt>layer=1</tt>). +(e.g., <code>layer=1</code>). <h2>EXAMPLES</h2> diff --git a/vector/v.reclass/v.reclass.html b/vector/v.reclass/v.reclass.html index 4fc3e1b81cd..6a75033376c 100644 --- a/vector/v.reclass/v.reclass.html +++ b/vector/v.reclass/v.reclass.html @@ -51,12 +51,12 @@ <h3>Example 1: Reclass by rules</h3> <em>land</em> with area category values selected from database by SQL select statement: <br> -<tt>select id from tland where use = 'E13' and owner = 'Jara Cimrman'</tt> +<code>select id from tland where use = 'E13' and owner = 'Jara Cimrman'</code> changed to category 1; <br> values selected from database by SQL select statement: <br> -<tt>select id from tland where use = 'E14'</tt> changed to category 2. +<code>select id from tland where use = 'E14'</code> changed to category 2. <h3>Example 2: Reclass by attribute column</h3> diff --git a/vector/v.segment/v.segment.html b/vector/v.segment/v.segment.html index 43f83abdd44..eda0b89c916 100644 --- a/vector/v.segment/v.segment.html +++ b/vector/v.segment/v.segment.html @@ -1,7 +1,7 @@ <h2>DESCRIPTION</h2> <em>v.segment</em> generates segments or points from input lines and from -positions read from a text file or '<tt>stdin</tt>'. It includes the creation +positions read from a text file or '<code>stdin</code>'. It includes the creation of parallel lines or points in given destination from the line. <p>The format is: @@ -15,7 +15,7 @@ <h2>DESCRIPTION</h2> line. <p> -The user could send to <tt>stdin</tt> something like: +The user could send to <code>stdin</code> something like: <div class="code"><pre> P 1 356 24.56 P 2 495 12.31 diff --git a/vector/v.select/v.select.html b/vector/v.select/v.select.html index cf799cfe21e..f4004a897b0 100644 --- a/vector/v.select/v.select.html +++ b/vector/v.select/v.select.html @@ -240,7 +240,7 @@ <h3>RELATE feature A is spatially related to feature B (using GEOS)</h3> d.vect map=v_select_TOUCHES </pre></div> -The result of <tt>relate='T********'</tt> is the same as seen +The result of <code>relate='T********'</code> is the same as seen above in the example 'TOUCHES'. See the <a href="https://en.wikipedia.org/wiki/DE-9IM">DE-9IM</a> page diff --git a/vector/v.support/v.support.html b/vector/v.support/v.support.html index 1c81680e1ef..565c4fbfe63 100644 --- a/vector/v.support/v.support.html +++ b/vector/v.support/v.support.html @@ -1,6 +1,8 @@ <h2>DESCRIPTION</h2> -<em>v.support</em> is used to set/update vector map metadata. +<em>v.support</em> is used to set/update vector map metadata. While GRASS +GIS typically generates these metadata entries automatically, <em>v.support</em> +allows users to manually edit them when necessary. <h2>EXAMPLE</h2> diff --git a/vector/v.to.db/lines.c b/vector/v.to.db/lines.c index 5550c393a77..9cafd890e31 100644 --- a/vector/v.to.db/lines.c +++ b/vector/v.to.db/lines.c @@ -264,6 +264,11 @@ int read_lines(struct Map_info *Map) } G_percent(line_num, nlines, 2); } + Vect_destroy_line_struct(EndPoints); + Vect_destroy_cats_struct(RCats); + Vect_destroy_line_struct(Points); + Vect_destroy_cats_struct(LCats); + Vect_destroy_cats_struct(Cats); return 0; } diff --git a/vector/v.to.db/v.to.db.html b/vector/v.to.db/v.to.db.html index f0ba460d450..6b32084261d 100644 --- a/vector/v.to.db/v.to.db.html +++ b/vector/v.to.db/v.to.db.html @@ -95,9 +95,9 @@ <h3>Updating attribute tables</h3> <p>Compute D<sub>L</sub>, the Fractal Dimension (Mandelbrot, 1982), of the boundary defining a polygon based on the formula: -<br><tt> +<br><code> D = 2 * (log perimeter) / (log area):<br> -</tt> +</code> <div class="code"><pre> g.copy vect=soils,mysoils v.db.addcolumn mysoils col="d double precision" diff --git a/vector/v.to.rast/do_areas.c b/vector/v.to.rast/do_areas.c index c3be7f5c18d..413ab8346d4 100644 --- a/vector/v.to.rast/do_areas.c +++ b/vector/v.to.rast/do_areas.c @@ -94,13 +94,14 @@ int sort_areas(struct Map_info *Map, struct line_pnts *Points, int field, CELL cat; G_begin_polygon_area_calculations(); - Cats = Vect_new_cats_struct(); /* first count valid areas */ nareas = Vect_get_num_areas(Map); if (nareas == 0) return 0; + Cats = Vect_new_cats_struct(); + /* allocate list to hold valid area info */ list = (struct list *)G_calloc(nareas * sizeof(char), sizeof(struct list)); @@ -155,6 +156,7 @@ int sort_areas(struct Map_info *Map, struct line_pnts *Points, int field, /* sort the list by size */ qsort(list, nareas * sizeof(char), sizeof(struct list), compare); } + Vect_destroy_cats_struct(Cats); return nareas_selected; } diff --git a/vector/v.to.rast/main.c b/vector/v.to.rast/main.c index 930ba11592a..3878c1ad6d2 100644 --- a/vector/v.to.rast/main.c +++ b/vector/v.to.rast/main.c @@ -12,7 +12,7 @@ * OGR support by Martin Landa <landa.martin gmail.com> * Markus Metz (labelcol, cats, where options) * PURPOSE: Converts vector map to raster map - * COPYRIGHT: (C) 2003-2018 by the GRASS Development Team + * COPYRIGHT: (C) 2003-2024 by the GRASS Development Team * * This program is free software under the GNU General Public * License (>=v2). Read the file COPYING that comes with GRASS diff --git a/vector/v.to.rast/v.to.rast.html b/vector/v.to.rast/v.to.rast.html index 4ebe8bf4922..7cbfc68f48a 100644 --- a/vector/v.to.rast/v.to.rast.html +++ b/vector/v.to.rast/v.to.rast.html @@ -49,7 +49,7 @@ <h2>NOTES</h2> Labeled areas and/or centroids will produce filled raster coverages with edges that straddle the original area boundary <b>as long as the boundary is NOT labeled</b>. -<br>(Use <tt>v.category option=del type=boundary</tt> to remove.)</li> +<br>(Use <code>v.category option=del type=boundary</code> to remove.)</li> <li> Labeled lines and boundaries will produce lines of raster cells which touch the original vector line. This tends to be more aggressive than area-only conversions.</li> diff --git a/vector/v.to.rast/vect2rast.c b/vector/v.to.rast/vect2rast.c index 6a5905f162c..e1a5cf09c85 100644 --- a/vector/v.to.rast/vect2rast.c +++ b/vector/v.to.rast/vect2rast.c @@ -149,6 +149,27 @@ int vect_to_rast(const char *vector_map, const char *raster_map, dense = 0; } + if (ftype & GV_POINT) { + G_verbose_message( + _("Number of points selected from vector map <%s>: %d"), vector_map, + Vect_get_num_primitives(&Map, GV_POINT)); + } + if (ftype & GV_LINE) { + G_verbose_message( + _("Number of lines selected from vector map <%s>: %d"), vector_map, + Vect_get_num_primitives(&Map, GV_LINE)); + } + if (ftype & GV_BOUNDARY) { + G_verbose_message( + _("Number of boundaries selected from vector map <%s>: %d"), + vector_map, Vect_get_num_primitives(&Map, GV_BOUNDARY)); + } + if (ftype & GV_CENTROID) { + G_verbose_message( + _("Number of centroids selected from vector map <%s>: %d"), + vector_map, Vect_get_num_primitives(&Map, GV_CENTROID)); + } + nlines = Vect_get_num_primitives(&Map, ftype); nplines_all = nlines; npasses = begin_rasterization(cache_mb, format, dense); diff --git a/vector/v.to.rast3/v.to.rast3.html b/vector/v.to.rast3/v.to.rast3.html index 14bfb0eada0..42a50851491 100644 --- a/vector/v.to.rast3/v.to.rast3.html +++ b/vector/v.to.rast3/v.to.rast3.html @@ -14,7 +14,7 @@ <h2>NOTES</h2> <img src="v_to_rast3_test.png" border="0"><br> <table border="0" width="600"> <tr><td><center> -<i>This screenshot shows the result of the v.to.rast3 test. Visualized +<i>Fig: This screenshot shows the result of the v.to.rast3 test. Visualized are the cube of the GRASS region, the vector points as black dots and the voxel cells as wireframe model. Only cells with non-null values are shown.</i> diff --git a/vector/v.voronoi/v.voronoi.html b/vector/v.voronoi/v.voronoi.html index c058a0587fd..fa79d1507a8 100644 --- a/vector/v.voronoi/v.voronoi.html +++ b/vector/v.voronoi/v.voronoi.html @@ -91,6 +91,8 @@ <h2>SEE ALSO</h2> <a href="v.delaunay.html">v.delaunay</a>, <a href="v.hull.html">v.hull</a> </em> +<p> +<a href="https://en.wikipedia.org/wiki/Voronoi_diagram">Voronoi diagram (Wikipedia)</a> <h2>AUTHORS</h2> diff --git a/vector/v.what.rast/v.what.rast.html b/vector/v.what.rast/v.what.rast.html index fe067a18138..05c5eddbd89 100644 --- a/vector/v.what.rast/v.what.rast.html +++ b/vector/v.what.rast/v.what.rast.html @@ -34,10 +34,10 @@ <h2>NOTES</h2> geometry instead, use <em><a href="v.drape.html">v.drape</a></em>. <p> Categories and values are output unsorted with the print flag. To sort them -pipe the output of this module into the UNIX <tt>sort</tt> tool -(<tt>sort -n</tt>). If you need coordinates, after sorting use -<em><a href="v.out.ascii.html">v.out.ascii</a></em> and the UNIX <tt>paste</tt> tool -(<tt>paste -d'|'</tt>). In the case of a NULL result, a "<tt>*</tt>" +pipe the output of this module into the UNIX <code>sort</code> tool +(<code>sort -n</code>). If you need coordinates, after sorting use +<em><a href="v.out.ascii.html">v.out.ascii</a></em> and the UNIX <code>paste</code> tool +(<code>paste -d'|'</code>). In the case of a NULL result, a "<code>*</code>" will be printed in lieu of the value. <p> The interpolation flag is only useful for continuous value raster maps, diff --git a/vector/vectorintro.html b/vector/vectorintro.html index 814526ec8b1..4755ee1a0db 100644 --- a/vector/vectorintro.html +++ b/vector/vectorintro.html @@ -298,7 +298,7 @@ <h3>Vector-Raster-DB conversion</h3> With <a href="v.to.points.html">v.to.points</a>, <a href="v.to.rast.html">v.to.rast</a> and <a href="v.to.rast3.html">v.to.rast3</a> -conversions are performed. Note that a raster mask ("MASK") will not be +conversions are performed. Note that a raster mask will not be respected since it is only applied when <em>reading</em> an existing GRASS raster map. diff --git a/visualization/ximgview/ximgview.html b/visualization/ximgview/ximgview.html index c5012ca7d41..1e07dd7eb38 100644 --- a/visualization/ximgview/ximgview.html +++ b/visualization/ximgview/ximgview.html @@ -23,7 +23,7 @@ <h2>EXAMPLE</h2> export GRASS_RENDER_FILE_READ=TRUE d.erase -ximgview $GRASS_RENDER_FILE percent=50 & +ximgview $GRASS_RENDER_FILE percent=50 & d.rast elevation.dem d.vect roads </pre></div> @@ -31,10 +31,14 @@ <h2>EXAMPLE</h2> <h2>SEE ALSO</h2> <em> -<a href="pngdriver.html">PNG driver</a><br> -<a href="cairodriver.html">cairo driver</a><br> +<a href="pngdriver.html">PNG driver</a>, +<a href="cairodriver.html">cairo driver</a>, <a href="wximgview.html">wximgview</a> </em> +<br> +<em> +<a href="variables.html">variables</a> +</em> <h2>AUTHOR</h2>