diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 144b415cb39..57ad644c0b9 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,36 +7,35 @@ assignees: '' --- -**Describe the bug** -A clear and concise description of what the bug is. +## Describe the bug + -**To Reproduce** -Steps to reproduce the behavior: +## To reproduce + -**Expected behavior** -A clear and concise description of what you expected to happen. +## Expected behavior + -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**System description (please complete the following information):** +## Screenshots + +## System description + +--> -**Additional context** -Add any other context about the problem here. +## Additional context + diff --git a/.github/actions/create-upload-suggestions/action.yml b/.github/actions/create-upload-suggestions/action.yml index 858263d8588..16f7ed4c337 100644 --- a/.github/actions/create-upload-suggestions/action.yml +++ b/.github/actions/create-upload-suggestions/action.yml @@ -176,7 +176,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@1746f4ab65b179e0ea60a494b83293b640dd5bba # v4.3.2 + - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 id: upload-diff if: >- ${{ (steps.files_changed.outputs.files_changed == 'true') && @@ -199,7 +199,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@1746f4ab65b179e0ea60a494b83293b640dd5bba # v4.3.2 + - uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 id: upload-changes if: >- ${{ always() && diff --git a/.github/workflows/additional_checks.yml b/.github/workflows/additional_checks.yml index 180d8c23ab4..4e8108b86ac 100644 --- a/.github/workflows/additional_checks.yml +++ b/.github/workflows/additional_checks.yml @@ -27,7 +27,7 @@ jobs: steps: - name: Checkout repository contents - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 with: fetch-depth: 31 diff --git a/.github/workflows/clang-format-check.yml b/.github/workflows/clang-format-check.yml index 6f1586f4b7c..0cf81068b2b 100644 --- a/.github/workflows/clang-format-check.yml +++ b/.github/workflows/clang-format-check.yml @@ -16,7 +16,7 @@ jobs: name: Formatting Check runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 with: persist-credentials: false - uses: DoozyX/clang-format-lint-action@11b773b1598aa4ae3b32f023701bca5201c3817d # v0.17 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 9a97d72c02e..02e9c8ea3fb 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -42,7 +42,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 - name: Set up Python uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 with: @@ -56,7 +56,7 @@ jobs: sudo apt-get install -y --no-install-recommends --no-install-suggests - name: Initialize CodeQL - uses: github/codeql-action/init@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1 + uses: github/codeql-action/init@8f596b4ae3cb3c588a5c46780b86dd53fef16c52 # v3.25.2 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/codeql-config.yml @@ -81,6 +81,6 @@ jobs: run: .github/workflows/build_ubuntu-22.04.sh "${HOME}/install" - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1 + uses: github/codeql-action/analyze@8f596b4ae3cb3c588a5c46780b86dd53fef16c52 # v3.25.2 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml index 8d56eb1d8df..d8bdfefbb33 100644 --- a/.github/workflows/coverity.yml +++ b/.github/workflows/coverity.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-22.04 if: github.repository == 'OSGeo/grass' steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 - name: Get dependencies run: | diff --git a/.github/workflows/create_release_draft.yml b/.github/workflows/create_release_draft.yml index 4a1bdfb0c94..a8b143b77fb 100644 --- a/.github/workflows/create_release_draft.yml +++ b/.github/workflows/create_release_draft.yml @@ -28,7 +28,7 @@ jobs: contents: write steps: - name: Checks-out repository - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 with: ref: ${{ github.ref }} fetch-depth: 0 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index e8616b01718..d2b2d6605ab 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -49,7 +49,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 with: fetch-depth: 0 - name: Docker meta diff --git a/.github/workflows/gcc.yml b/.github/workflows/gcc.yml index a7e1963bd16..2816fa14250 100644 --- a/.github/workflows/gcc.yml +++ b/.github/workflows/gcc.yml @@ -35,7 +35,7 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 - name: Get dependencies run: | sudo apt-get update -y diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 9dd0b1a9742..1e834e50aad 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -38,7 +38,7 @@ jobs: /usr/bin/sudo /usr/bin/find /usr/local /opt/homebrew -mindepth 1 -maxdepth 1 \ -type f -print -delete hash -r - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 - name: Setup Mamba uses: mamba-org/setup-micromamba@422500192359a097648154e8db4e39bdb6c6eed7 # v1.8.1 with: @@ -74,7 +74,7 @@ jobs: nc_spm_full_v2alpha2.tar.gz" - name: Make HTML test report available if: ${{ always() }} - uses: actions/upload-artifact@1746f4ab65b179e0ea60a494b83293b640dd5bba # v4.3.2 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: name: testreport-macOS path: testreport diff --git a/.github/workflows/osgeo4w.yml b/.github/workflows/osgeo4w.yml index e8bfecc532b..356b232095b 100644 --- a/.github/workflows/osgeo4w.yml +++ b/.github/workflows/osgeo4w.yml @@ -32,7 +32,7 @@ jobs: run: | git config --global core.autocrlf false git config --global core.eol lf - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 - uses: msys2/setup-msys2@cc11e9188b693c2b100158c3322424c4cc1dadea # v2.22.0 with: path-type: inherit diff --git a/.github/workflows/periodic_update.yml b/.github/workflows/periodic_update.yml index 75c50089ed9..b5a5715e8db 100644 --- a/.github/workflows/periodic_update.yml +++ b/.github/workflows/periodic_update.yml @@ -21,7 +21,7 @@ jobs: - name: Create URL to the run output id: vars run: echo "run-url=https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" >> $GITHUB_OUTPUT - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 - name: "Check that autoconf scripts are up-to-date:" run: | rm -f config.guess config.sub @@ -33,7 +33,7 @@ jobs: run: git status --ignored - name: Create Pull Request id: cpr - uses: peter-evans/create-pull-request@9153d834b60caba6d51c9b9510b087acf9f33f83 # v6.0.4 + uses: peter-evans/create-pull-request@6d6857d36972b65feb161a90e484f2984215f83e # v6.0.5 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/post-pr-reviews.yml b/.github/workflows/post-pr-reviews.yml index 9bc2997c076..7bfa9b9091f 100644 --- a/.github/workflows/post-pr-reviews.yml +++ b/.github/workflows/post-pr-reviews.yml @@ -19,7 +19,7 @@ jobs: steps: - name: Create a .git directory needed by reviewdog run: git init - - uses: actions/download-artifact@8caf195ad4b1dee92908e23f56eeb0696f1dd42d # v4.1.5 + - uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7 id: diff continue-on-error: true with: diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index caa530f90e1..03cf1e92133 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -31,7 +31,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 - name: Set up Python uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index ab44d3abd8a..777637e8322 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -29,7 +29,7 @@ jobs: - os: ubuntu-22.04 python-version: "3.10" min-python-version: "3.7" - black-version: "23.1.0" + black-version: "24.4.0" flake8-version: "3.9.2" pylint-version: "2.12.2" bandit-version: "1.7.8" @@ -49,7 +49,7 @@ jobs: echo Pylint: ${{ matrix.pylint-version }} echo Bandit: ${{matrix.bandit-version}} - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 - name: Set up Python uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 @@ -60,7 +60,7 @@ jobs: run: python -m pip install --upgrade pip - name: Install Black only - run: pip install black==${{ matrix.black-version }} + run: pip install black[jupyter]==${{ matrix.black-version }} - name: Run Black run: black . @@ -102,13 +102,13 @@ jobs: bandit -c pyproject.toml -iii -r . -f sarif -o bandit.sarif --exit-zero - name: Upload Bandit Scan Results - uses: actions/upload-artifact@1746f4ab65b179e0ea60a494b83293b640dd5bba # v4.3.2 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: name: bandit.sarif path: bandit.sarif - name: Upload SARIF File into Security Tab - uses: github/codeql-action/upload-sarif@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1 + uses: github/codeql-action/upload-sarif@8f596b4ae3cb3c588a5c46780b86dd53fef16c52 # v3.25.2 with: sarif_file: bandit.sarif @@ -175,7 +175,7 @@ jobs: cp -rp dist.$ARCH/docs/html/libpython sphinx-grass - name: Make Sphinx documentation available - uses: actions/upload-artifact@1746f4ab65b179e0ea60a494b83293b640dd5bba # v4.3.2 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: name: sphinx-grass path: sphinx-grass diff --git a/.github/workflows/super-linter.yml b/.github/workflows/super-linter.yml index c768fc80175..34086aa18ff 100644 --- a/.github/workflows/super-linter.yml +++ b/.github/workflows/super-linter.yml @@ -28,13 +28,13 @@ jobs: statuses: write steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 with: # super-linter needs the full git history to get the # list of files that changed across commits fetch-depth: 0 - name: Lint code base - uses: super-linter/super-linter/slim@9d8128f17796a16326ceed5a607d5639a47feb82 # v6.4.0 + uses: super-linter/super-linter/slim@4758be622215d0954c8353ee4877ffd60111cf8e # v6.4.1 env: DEFAULT_BRANCH: main # To report GitHub Actions status checks diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 032ba3391c7..bce6b0bb089 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -61,7 +61,7 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 - name: Invert inclusion list to an exclusion list id: get-exclude @@ -151,7 +151,7 @@ jobs: - name: Make HTML test report available if: ${{ always() }} - uses: actions/upload-artifact@1746f4ab65b179e0ea60a494b83293b640dd5bba # v4.3.2 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: name: testreport-${{ matrix.os }}-${{ matrix.config }}-${{ matrix.extra-include }} path: testreport diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 61509b4280b..65cb46a2f53 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -39,10 +39,11 @@ repos: rev: v0.33.0 hooks: - id: markdownlint - - repo: https://github.com/psf/black - rev: 23.1.0 + # 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.4.0 hooks: - - id: black + - id: black-jupyter exclude: | (?x)^( python/libgrass_interface_generator/ diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..0b17de48af4 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,133 @@ + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official email address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[grass-coc@osgeo.org](mailto:grass-coc@osgeo.org). +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/README.md b/README.md index 49a84e661a4..c7bb195cc6b 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,10 @@ [![OSGeo4W](https://github.com/OSGeo/grass/workflows/OSGeo4W/badge.svg)](https://github.com/OSGeo/grass/actions?query=workflow%3AOSGeo4W) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/OSGeo/grass/badge)](https://securityscorecards.dev/viewer/?uri=github.com/OSGeo/grass) [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/2470/badge)](https://www.bestpractices.dev/projects/2470) +[![Coverity](https://scan.coverity.com/projects/1038/badge.svg)](https://scan.coverity.com/projects/grass) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.5176030.svg)](https://doi.org/10.5281/zenodo.5176030) [![Join the chat at https://gitter.im/grassgis/community](https://badges.gitter.im/grassgis/community.svg)](https://gitter.im/grassgis/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](code_of_conduct.md) ## Description diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000000..f722250e8c1 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,38 @@ +# GRASS GIS Security Policy + +## Reporting a Vulnerability +At GRASS GIS, we take security vulnerabilities seriously. We appreciate your efforts in responsibly disclosing any issues you may find. To report a security vulnerability, please follow these steps: +1. **Privately disclose the issue** by submitting a Security Advisory through [GitHub Security](https://github.com/OSGeo/grass/security). Please do not create publicly viewable issues for security vulnerabilities. +2. **Provide detailed information** regarding the vulnerability, including: + - Description of the vulnerability + - Steps to reproduce + - Versions affected + - Any mitigating factors +3. **Our Response**: + - Our security team will acknowledge receiving your report within 48 hours. + - We will work to validate and reproduce the issue. + - Once confirmed, we will work on a fix and release schedule. +4. **Public Disclosure**: + - We aim to release patches for vulnerabilities as soon as possible. + - We will coordinate with you regarding public disclosure, ensuring a reasonable timeline for users to update before the details are made public. + + +## Supported Versions +Please refer to our [Release Schedule](https://trac.osgeo.org/grass/wiki/Release/Schedule) for details on which versions are currently supported. + + +## Security Measures +- Code Review: We conduct code reviews to catch potential vulnerabilities during code submission +- Dependency Management: We track dependencies and update them regularly to mitigate known security issues. +- Secure Development Practices: We use a series of security tools to detect potential vulnerabilities in existing and newly submitted code. + + +## Vulnerability Scope +Our security policy covers vulnerabilities in the GRASS GIS core codebase, official addons, and any official distributions provided by the GRASS GIS team. + +While packages in Linux and other unix-like distributions are out of scope of this document, distribution maintainers traditionally do a great job in patching their distributions for security vulnerabilities. Please, refer to a specific distribution or package source if you are using packages for a specific software distribution. + +## Responsible Disclosure +We adhere to responsible disclosure practices. We appreciate your cooperation in allowing us time to address any reported vulnerabilities before disclosing them publicly. We ask that you refrain from disclosing any details of the vulnerability until we have had adequate time to provide a fix. + +Thank you for helping to keep GRASS GIS secure! diff --git a/binaryInstall.src b/binaryInstall.src index c9736d7c95a..5ab008dd796 100755 --- a/binaryInstall.src +++ b/binaryInstall.src @@ -319,7 +319,7 @@ echo"" echo "Generating display font configuration file..." -GISBASE="$DESTDIR" GISRC=junk LD_LIBRARY_PATH_VAR="${DESTDIR}/lib:$LD_LIBRARY_PATH_VAR" "${DESTDIR}/bin/g.mkfontcap" -o +GISBASE="$DESTDIR" GISRC=junk LD_LIBRARY_PATH_VAR="${DESTDIR}/lib:$LD_LIBRARY_PATH_VAR" "${DESTDIR}/bin/g.mkfontcap" --overwrite echo"" diff --git a/db/drivers/mysql/db.c b/db/drivers/mysql/db.c index 7e2a8146310..5c98c652e95 100644 --- a/db/drivers/mysql/db.c +++ b/db/drivers/mysql/db.c @@ -23,6 +23,7 @@ int db__driver_open_database(dbHandle *handle) const char *name; dbConnection default_connection; MYSQL *res; + dbString sql; db_get_connection(&default_connection); name = db_get_handle_dbname(handle); @@ -63,14 +64,25 @@ int db__driver_open_database(dbHandle *handle) db_d_report_error(); return DB_FAILED; } + + db_init_string(&sql); + db_set_string(&sql, "SET SQL_MODE=ANSI_QUOTES;"); + /* Set SQL ANSI_QUOTES MODE which allow to use double quotes instead of * backticks */ - if (mysql_query(connection, "SET SQL_MODE=ANSI_QUOTES") != 0) { + if (mysql_query(connection, db_get_string(&sql)) != 0) { db_d_append_error("%s %s", _("Unable to set SQL ANSI_QUOTES mode:"), mysql_error(connection)); db_d_report_error(); + db_free_string(&sql); + mysql_close(connection); + return DB_FAILED; } + G_debug(3, "db__driver_open_database(): Set ODBC MySQL DB %s", + db_get_string(&sql)); + + db_free_string(&sql); } return DB_OK; diff --git a/db/drivers/odbc/db.c b/db/drivers/odbc/db.c index 6da5ebdd336..e67fa147633 100644 --- a/db/drivers/odbc/db.c +++ b/db/drivers/odbc/db.c @@ -13,6 +13,7 @@ int db__driver_open_database(dbHandle *handle) SQLRETURN ret; SQLINTEGER err; dbConnection connection; + SQLCHAR dbms_name[256]; /* Open connection */ if (open_connection() != DB_OK) @@ -39,6 +40,47 @@ int db__driver_open_database(dbHandle *handle) return DB_FAILED; } + /* Find ODBC DB driver */ + SQLGetInfo(ODconn, SQL_DBMS_NAME, (SQLPOINTER)dbms_name, sizeof(dbms_name), + NULL); + + if (strcmp((CHAR *)dbms_name, "MySQL") == 0 || + strcmp((CHAR *)dbms_name, "MariaDB") == 0) { + dbString sql; + cursor *c; + + c = alloc_cursor(); + if (c == NULL) + return DB_FAILED; + + db_init_string(&sql); + db_set_string(&sql, "SET SQL_MODE=ANSI_QUOTES;"); + + /* Set SQL ANSI_QUOTES MODE which allow to use double quotes instead of + * backticks */ + ret = SQLExecDirect(c->stmt, (SQLCHAR *)db_get_string(&sql), SQL_NTS); + + if ((ret != SQL_SUCCESS) && (ret != SQL_SUCCESS_WITH_INFO)) { + SQLGetDiagRec(SQL_HANDLE_STMT, c->stmt, 1, NULL, &err, msg, + sizeof(msg), NULL); + db_d_append_error("SQLExecDirect():\n%s\n%s (%d)\n", + db_get_string(&sql), msg, (int)err); + db_d_report_error(); + free_cursor(c); + db_free_string(&sql); + SQLDisconnect(ODconn); + close_connection(); + + return DB_FAILED; + } + + G_debug(3, "db__driver_open_database(): Set ODBC %s DB %s", dbms_name, + db_get_string(&sql)); + + free_cursor(c); + db_free_string(&sql); + } + return DB_OK; } diff --git a/doc/notebooks/hydrology.ipynb b/doc/notebooks/hydrology.ipynb index f19ce35c436..b9e23acf3c1 100644 --- a/doc/notebooks/hydrology.ipynb +++ b/doc/notebooks/hydrology.ipynb @@ -69,7 +69,9 @@ "\n", "# Add a raster, vector and legend to the map\n", "elev_map.d_rast(map=\"elevation\")\n", - "elev_map.d_legend(raster=\"elevation\", at=(65, 90, 85, 88), fontsize=12, flags=\"b\", title=\"DTM\")\n", + "elev_map.d_legend(\n", + " raster=\"elevation\", at=(65, 90, 85, 88), fontsize=12, flags=\"b\", title=\"DTM\"\n", + ")\n", "\n", "# Display map\n", "elev_map.show()" @@ -90,9 +92,27 @@ "metadata": {}, "outputs": [], "source": [ - "gs.run_command(\"r.fill.dir\", input=\"elevation\", output=\"elev_fill1\", direction=\"dir1\", areas=\"area1\")\n", - "gs.run_command(\"r.fill.dir\", input=\"elev_fill1\", output=\"elev_fill2\", direction=\"dir2\", areas=\"area2\")\n", - "gs.run_command(\"r.fill.dir\", input=\"elev_fill2\", output=\"elev_fill3\", direction=\"dir3\", areas=\"area3\")\n", + "gs.run_command(\n", + " \"r.fill.dir\",\n", + " input=\"elevation\",\n", + " output=\"elev_fill1\",\n", + " direction=\"dir1\",\n", + " areas=\"area1\",\n", + ")\n", + "gs.run_command(\n", + " \"r.fill.dir\",\n", + " input=\"elev_fill1\",\n", + " output=\"elev_fill2\",\n", + " direction=\"dir2\",\n", + " areas=\"area2\",\n", + ")\n", + "gs.run_command(\n", + " \"r.fill.dir\",\n", + " input=\"elev_fill2\",\n", + " output=\"elev_fill3\",\n", + " direction=\"dir3\",\n", + " areas=\"area3\",\n", + ")\n", "gs.mapcalc(\"depr_bin = if((elevation-elev_fill3) < 0., 1, null())\")\n", "gs.run_command(\"r.colors\", map=\"depr_bin\", color=\"blues\")" ] @@ -129,13 +149,15 @@ "metadata": {}, "outputs": [], "source": [ - "gs.run_command(\"r.watershed\", \n", - " elevation=\"elevation@PERMANENT\",\n", - " drainage=\"drainage\", # Drainage Direction\n", - " accumulation=\"flowacc\", # Flow Accumulation\n", - " basin=\"watersheds\",\n", - " stream=\"streams\",\n", - " threshold=80000)\n", + "gs.run_command(\n", + " \"r.watershed\",\n", + " elevation=\"elevation@PERMANENT\",\n", + " drainage=\"drainage\", # Drainage Direction\n", + " accumulation=\"flowacc\", # Flow Accumulation\n", + " basin=\"watersheds\",\n", + " stream=\"streams\",\n", + " threshold=80000,\n", + ")\n", "\n", "# Convert streams raster to vector\n", "gs.run_command(\"r.to.vect\", input=\"streams\", output=\"streams\", type=\"line\")" @@ -160,7 +182,7 @@ "\n", "# We can modify with color table for rasters with `r.colors`.\n", "# Note that if the raster is located in a different mapset (for example,\n", - "# elevation is in PERMANENT, not user1), the `r.colors` will not change \n", + "# elevation is in PERMANENT, not user1), the `r.colors` will not change\n", "# the color in InteractiveMap.\n", "gs.run_command(\"r.colors\", map=\"drainage\", color=\"aspect\")\n", "\n", @@ -193,13 +215,19 @@ "outputs": [], "source": [ "# Count cells in each watershed\n", - "gs.run_command(\"r.stats.zonal\", base=\"watersheds\", cover=\"elevation\", method=\"count\", output=\"watersheds_count\")\n", + "gs.run_command(\n", + " \"r.stats.zonal\",\n", + " base=\"watersheds\",\n", + " cover=\"elevation\",\n", + " method=\"count\",\n", + " output=\"watersheds_count\",\n", + ")\n", "\n", "# Get projection resolution\n", - "proj=gs.parse_command(\"g.region\", flags=\"m\")\n", + "proj = gs.parse_command(\"g.region\", flags=\"m\")\n", "\n", "# Multiply N-S resollution by E-W resolution to get cell area\n", - "cell_area = float(proj[\"nsres\"])*float(proj[\"ewres\"])\n", + "cell_area = float(proj[\"nsres\"]) * float(proj[\"ewres\"])\n", "\n", "# Calculate watersheds areas and convert from m2 to km2\n", "gs.mapcalc(\"'watershed_area' = float('watersheds_count'*{})/1000000\".format(cell_area))" @@ -220,18 +248,20 @@ "source": [ "# Display a map of watershed areas.\n", "gs.run_command(\"r.colors\", map=\"watershed_area\", color=\"plasma\")\n", - " \n", + "\n", "watershed_map = gj.Map()\n", "watershed_map.d_rast(map=\"watershed_area\")\n", - "watershed_map.d_legend(raster=\"watershed_area\",\n", - " bgcolor=\"none\",\n", - " color=\"black\",\n", - " border_color=\"none\",\n", - " at=(3, 40, 84, 88),\n", - " lines=2,\n", - " fontsize=15,\n", - " title=\"Area\",\n", - " units=\" km2\")\n", + "watershed_map.d_legend(\n", + " raster=\"watershed_area\",\n", + " bgcolor=\"none\",\n", + " color=\"black\",\n", + " border_color=\"none\",\n", + " at=(3, 40, 84, 88),\n", + " lines=2,\n", + " fontsize=15,\n", + " title=\"Area\",\n", + " units=\" km2\",\n", + ")\n", "watershed_map.show()" ] }, @@ -265,7 +295,14 @@ "# Display slope map\n", "slope_map = gj.Map()\n", "slope_map.d_rast(map=\"slope\")\n", - "slope_map.d_legend(raster=\"slope\", at=(65, 90, 85, 90), fontsize=15, flags=\"b\", title=\"Slope\", units=\"°\")\n", + "slope_map.d_legend(\n", + " raster=\"slope\",\n", + " at=(65, 90, 85, 90),\n", + " fontsize=15,\n", + " flags=\"b\",\n", + " title=\"Slope\",\n", + " units=\"°\",\n", + ")\n", "slope_map.show()" ] }, @@ -284,14 +321,16 @@ "source": [ "separator = \"|\"\n", "\n", - "columns = defaultdict(list) # each value in each column is appended to a list\n", + "columns = defaultdict(list) # each value in each column is appended to a list\n", "\n", - "text = gs.read_command(\"r.univar\", map=\"elevation\", zones=\"watersheds\", separator=separator, flags=\"t\")\n", + "text = gs.read_command(\n", + " \"r.univar\", map=\"elevation\", zones=\"watersheds\", separator=separator, flags=\"t\"\n", + ")\n", "reader = csv.DictReader(text.splitlines(), delimiter=separator)\n", - "for row in reader: # read a row as {column1: value1, column2: value2,...}\n", - " for (k,v) in row.items(): # go over each column name and value \n", - " columns[k].append(v) # append the value into the appropriate list\n", - " # based on column name k\n", + "for row in reader: # read a row as {column1: value1, column2: value2,...}\n", + " for k, v in row.items(): # go over each column name and value\n", + " columns[k].append(v) # append the value into the appropriate list\n", + " # based on column name k\n", "\n", "watersheds = columns[\"zone\"]\n", "means = np.array(columns[\"mean\"], dtype=np.float32)\n", @@ -333,7 +372,9 @@ "outputs": [], "source": [ "# Convert to vector\n", - "gs.run_command(\"r.to.vect\", flags=\"s\", input=\"watersheds\", output=\"watersheds_vector\", type=\"area\")" + "gs.run_command(\n", + " \"r.to.vect\", flags=\"s\", input=\"watersheds\", output=\"watersheds_vector\", type=\"area\"\n", + ")" ] }, { @@ -345,14 +386,16 @@ "# Display\n", "watershed_vect_map = gj.Map()\n", "watershed_vect_map.d_rast(map=\"elevation\")\n", - "watershed_vect_map.d_vect(map=\"watersheds_vector\",\n", - " fill_color=\"none\",\n", - " width=1.5,\n", - " color=\"black\",\n", - " attribute_column=\"value\",\n", - " label_bgcolor=\"black\",\n", - " label_color=\"white\",\n", - " label_size=10)\n", + "watershed_vect_map.d_vect(\n", + " map=\"watersheds_vector\",\n", + " fill_color=\"none\",\n", + " width=1.5,\n", + " color=\"black\",\n", + " attribute_column=\"value\",\n", + " label_bgcolor=\"black\",\n", + " label_color=\"white\",\n", + " label_size=10,\n", + ")\n", "watershed_vect_map.show()" ] } diff --git a/doc/notebooks/jupyter_example.ipynb b/doc/notebooks/jupyter_example.ipynb index a0f660b5cd3..48628e08263 100644 --- a/doc/notebooks/jupyter_example.ipynb +++ b/doc/notebooks/jupyter_example.ipynb @@ -64,7 +64,9 @@ "outputs": [], "source": [ "gs.parse_command(\"g.region\", raster=\"lakes\", flags=\"pg\")\n", - "gs.run_command(\"r.buffer\", input=\"lakes\", output=\"lakes_buff\", distances=[60, 120, 240, 500])\n", + "gs.run_command(\n", + " \"r.buffer\", input=\"lakes\", output=\"lakes_buff\", distances=[60, 120, 240, 500]\n", + ")\n", "\n", "# Start a Map\n", "r_buffer_map = gj.Map()\n", @@ -101,7 +103,9 @@ "v_buffer_map = gj.Map()\n", "\n", "# Add vector layers and legend\n", - "v_buffer_map.d_vect(map=\"boundary_state\", fill_color=\"#5A91ED\", legend_label=\"State boundary\")\n", + "v_buffer_map.d_vect(\n", + " map=\"boundary_state\", fill_color=\"#5A91ED\", legend_label=\"State boundary\"\n", + ")\n", "v_buffer_map.d_vect(map=\"buffer\", fill_color=\"#F8766D\", legend_label=\"Inner portion\")\n", "v_buffer_map.d_legend_vect(at=(10, 35))\n", "\n", diff --git a/doc/notebooks/jupyter_tutorial.ipynb b/doc/notebooks/jupyter_tutorial.ipynb index 8213ea970ca..73d656905f7 100644 --- a/doc/notebooks/jupyter_tutorial.ipynb +++ b/doc/notebooks/jupyter_tutorial.ipynb @@ -142,7 +142,7 @@ "source": [ "# Then, we return to the first instance and continue to modify and display it\n", "# Notice that layers a drawn in the order they are added\n", - "example_map.run(\"d.vect\", map = \"zipcodes\", color=\"red\", fill_color=\"none\")\n", + "example_map.run(\"d.vect\", map=\"zipcodes\", color=\"red\", fill_color=\"none\")\n", "example_map.show()" ] }, @@ -277,11 +277,11 @@ "metadata": {}, "outputs": [], "source": [ - "import folium \n", + "import folium\n", "\n", "\n", "# Create a map\n", - "m = folium.Map(location=[35.761168,-78.668271], zoom_start=13)\n", + "m = folium.Map(location=[35.761168, -78.668271], zoom_start=13)\n", "\n", "# Create and add elevation layer to map\n", "gj.Raster(\"elevation\", opacity=0.5).add_to(m)\n", @@ -291,7 +291,7 @@ "tooltip = \"Click me!\"\n", "# and add a marker\n", "folium.Marker(\n", - " [35.781608,-78.675800], popup=\"Point of Interest\", tooltip=tooltip\n", + " [35.781608, -78.675800], popup=\"Point of Interest\", tooltip=tooltip\n", ").add_to(m)\n", "\n", "# Display map\n", @@ -313,10 +313,10 @@ "metadata": {}, "outputs": [], "source": [ - "import ipyleaflet \n", + "import ipyleaflet\n", "\n", "# Create map\n", - "m = ipyleaflet.Map(center=[35.761168,-78.668271], zoom=13)\n", + "m = ipyleaflet.Map(center=[35.761168, -78.668271], zoom=13)\n", "\n", "# Create and add elevation layer to map\n", "gj.Raster(\"elevation\", opacity=0.5).add_to(m)\n", @@ -325,12 +325,12 @@ "# Like make a tooltip\n", "title = \"Click me!\"\n", "# and add a marker\n", - "marker = ipyleaflet.Marker(name='marker', location=(35.781608,-78.675800), title=title)\n", + "marker = ipyleaflet.Marker(name=\"marker\", location=(35.781608, -78.675800), title=title)\n", "\n", "# Add the marker to the map\n", "m.add(marker)\n", "\n", - "control = ipyleaflet.LayersControl(position='topright')\n", + "control = ipyleaflet.LayersControl(position=\"topright\")\n", "m.add(control)\n", "m" ] @@ -451,9 +451,9 @@ "metadata": {}, "outputs": [], "source": [ - "series = gj.SeriesMap(height = 500)\n", + "series = gj.SeriesMap(height=500)\n", "series.add_rasters([\"elevation\", \"elevation_shade\", \"slope\"])\n", - "#series.add_vectors([\"streams\", \"streets\", \"viewpoints\"])\n", + "# series.add_vectors([\"streams\", \"streets\", \"viewpoints\"])\n", "series.d_vect(map=\"streets\")\n", "series.d_barscale()\n", "series.show() # Create Slider" diff --git a/doc/notebooks/parallelization_tutorial.ipynb b/doc/notebooks/parallelization_tutorial.ipynb index 35a49fa2766..2c5d902e9a4 100644 --- a/doc/notebooks/parallelization_tutorial.ipynb +++ b/doc/notebooks/parallelization_tutorial.ipynb @@ -139,6 +139,7 @@ "import time\n", "from grass.pygrass.modules.grid import GridModule\n", "import time\n", + "\n", "start = time.time()\n", "grid = GridModule(\n", " \"v.surf.idw\",\n", diff --git a/doc/notebooks/scripting_example.ipynb b/doc/notebooks/scripting_example.ipynb index 612904da115..f71ebb8f284 100644 --- a/doc/notebooks/scripting_example.ipynb +++ b/doc/notebooks/scripting_example.ipynb @@ -74,7 +74,9 @@ "outputs": [], "source": [ "gs.parse_command(\"g.region\", raster=\"lakes\", flags=\"pg\")\n", - "gs.run_command(\"r.buffer\", input=\"lakes\", output=\"lakes_buff\", distances=[60, 120, 240, 500])\n", + "gs.run_command(\n", + " \"r.buffer\", input=\"lakes\", output=\"lakes_buff\", distances=[60, 120, 240, 500]\n", + ")\n", "gs.run_command(\"d.erase\")\n", "gs.run_command(\"d.rast\", map=\"lakes_buff\")\n", "gs.run_command(\"d.legend\", raster=\"lakes_buff\", range=(2, 5), at=(80, 100, 2, 10))\n", @@ -102,8 +104,12 @@ "gs.parse_command(\"g.region\", vector=\"boundary_state\", flags=\"pg\")\n", "gs.run_command(\"d.erase\") # erase the display before drawing again\n", "!rm -f $GRASS_LEGEND_FILE # and remove the legend file\n", - "gs.run_command(\"d.vect\", map=\"boundary_state\", fill_color=\"#5A91ED\", legend_label=\"State boundary\")\n", - "gs.run_command(\"d.vect\", map=\"buffer\", fill_color=\"#F8766D\", legend_label=\"Inner portion\")\n", + "gs.run_command(\n", + " \"d.vect\", map=\"boundary_state\", fill_color=\"#5A91ED\", legend_label=\"State boundary\"\n", + ")\n", + "gs.run_command(\n", + " \"d.vect\", map=\"buffer\", fill_color=\"#F8766D\", legend_label=\"Inner portion\"\n", + ")\n", "gs.run_command(\"d.legend.vect\", at=(10, 35))\n", "Image(filename=\"map.png\")" ] diff --git a/doc/notebooks/solar_potential.ipynb b/doc/notebooks/solar_potential.ipynb index cb3fd985910..98253ad6450 100644 --- a/doc/notebooks/solar_potential.ipynb +++ b/doc/notebooks/solar_potential.ipynb @@ -93,7 +93,18 @@ "metadata": {}, "outputs": [], "source": [ - "gs.run_command(\"r.sunmask\", elevation=\"elevation\", year=\"2001\", month=\"12\", day=\"22\", hour=\"16\", minute=\"15\", sec=\"0\", timezone=\"-5\", flags=\"s\")" + "gs.run_command(\n", + " \"r.sunmask\",\n", + " elevation=\"elevation\",\n", + " year=\"2001\",\n", + " month=\"12\",\n", + " day=\"22\",\n", + " hour=\"16\",\n", + " minute=\"15\",\n", + " sec=\"0\",\n", + " timezone=\"-5\",\n", + " flags=\"s\",\n", + ")" ] }, { @@ -111,7 +122,15 @@ "metadata": {}, "outputs": [], "source": [ - "gs.run_command(\"r.sun\", elevation=\"elevation\", aspect=\"aspect\", slope=\"slope\", incidout=\"incident\", day=\"356\", time=\"16.25\")\n", + "gs.run_command(\n", + " \"r.sun\",\n", + " elevation=\"elevation\",\n", + " aspect=\"aspect\",\n", + " slope=\"slope\",\n", + " incidout=\"incident\",\n", + " day=\"356\",\n", + " time=\"16.25\",\n", + ")\n", "gs.parse_command(\"r.info\", map=\"incident\", flags=\"g\")\n", "gs.run_command(\"r.colors\", map=\"incident\", co=\"bcyr\", flags=\"e\")" ] @@ -181,10 +200,26 @@ "metadata": {}, "outputs": [], "source": [ - "gs.run_command(\"r.sun\", elevation=\"elevation\", aspect=\"aspect\", slope=\"slope\", day=\"356\", glob_rad=\"winter\", insol_time=\"its356\")\n", + "gs.run_command(\n", + " \"r.sun\",\n", + " elevation=\"elevation\",\n", + " aspect=\"aspect\",\n", + " slope=\"slope\",\n", + " day=\"356\",\n", + " glob_rad=\"winter\",\n", + " insol_time=\"its356\",\n", + ")\n", "gs.run_command(\"r.colors\", map=\"winter\", co=\"gyr\", flags=\"e\")\n", "\n", - "gs.run_command(\"r.sun\", elevation=\"elevation\", aspect=\"aspect\", slope=\"slope\", day=\"172\", glob_rad=\"summer\", insol_time=\"its172\")\n", + "gs.run_command(\n", + " \"r.sun\",\n", + " elevation=\"elevation\",\n", + " aspect=\"aspect\",\n", + " slope=\"slope\",\n", + " day=\"172\",\n", + " glob_rad=\"summer\",\n", + " insol_time=\"its172\",\n", + ")\n", "gs.run_command(\"r.colors\", map=\"summer\", co=\"gyr\", flags=\"e\")" ] }, diff --git a/doc/notebooks/temporal.ipynb b/doc/notebooks/temporal.ipynb index 9a4433dc7fa..07e030a372c 100644 --- a/doc/notebooks/temporal.ipynb +++ b/doc/notebooks/temporal.ipynb @@ -32,7 +32,9 @@ "!curl http://fatra.cnr.ncsu.edu/temporal-grass-workshop/NC_spm_temporal_workshop.zip -o ../../data/NC_spm_temporal_workshop.zip\n", "\n", "# Unpack zip to grassdata\n", - "shutil.unpack_archive(\"../../data/NC_spm_temporal_workshop.zip\", \"../../data/grassdata\", \"zip\")" + "shutil.unpack_archive(\n", + " \"../../data/NC_spm_temporal_workshop.zip\", \"../../data/grassdata\", \"zip\"\n", + ")" ] }, { @@ -219,7 +221,7 @@ "source": [ "precip_map = gj.TimeSeriesMap(use_region=True)\n", "precip_map.add_raster_series(\"precip_sum_2010\")\n", - "precip_map.d_legend(color=\"black\", at=(10,40,2,6)) #Add legend\n", + "precip_map.d_legend(color=\"black\", at=(10, 40, 2, 6)) # Add legend\n", "precip_map.d_vect(map=\"boundary_county\", fill_color=\"none\")\n", "precip_map.d_barscale()\n", "precip_map.show()" @@ -240,7 +242,11 @@ "source": [ "from IPython.display import Image\n", "\n", - "Image(precip_map.save(\"image.gif\", duration=500, label=True, text_size=16, text_color=\"gray\"))" + "Image(\n", + " precip_map.save(\n", + " \"image.gif\", duration=500, label=True, text_size=16, text_color=\"gray\"\n", + " )\n", + ")" ] }, { diff --git a/doc/notebooks/viewshed_analysis.ipynb b/doc/notebooks/viewshed_analysis.ipynb index 263931ba25c..a4f0b61744a 100644 --- a/doc/notebooks/viewshed_analysis.ipynb +++ b/doc/notebooks/viewshed_analysis.ipynb @@ -65,8 +65,17 @@ "outputs": [], "source": [ "gs.parse_command(\"g.region\", raster=\"elevation\", flags=\"apg\")\n", - "gs.write_command(\"v.in.ascii\", input=\"-\", stdin=\"%s|%s\" % (642212, 224767), output=\"viewpoints\")\n", - "gs.run_command(\"r.viewshed\", input=\"elevation\", output=\"tower_los\", coordinates=\"642212,224767\", observer_elevation=\"165\", max_distance=\"10000\")" + "gs.write_command(\n", + " \"v.in.ascii\", input=\"-\", stdin=\"%s|%s\" % (642212, 224767), output=\"viewpoints\"\n", + ")\n", + "gs.run_command(\n", + " \"r.viewshed\",\n", + " input=\"elevation\",\n", + " output=\"tower_los\",\n", + " coordinates=\"642212,224767\",\n", + " observer_elevation=\"165\",\n", + " max_distance=\"10000\",\n", + ")" ] }, { diff --git a/docker/debian/Dockerfile b/docker/debian/Dockerfile index 776b8c5a1d4..c15299386cd 100644 --- a/docker/debian/Dockerfile +++ b/docker/debian/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:stable@sha256:fef26aa981ff8c3eeb09b20555ec6e02b38e6b227ec0b88bef7fde778cbf848a +FROM debian:stable@sha256:84725fb1debbf461f54a23030be0bcde8449f2fa4eba8adf857b1a4c707fec8d # currently Debian 12 # docker run -it --rm debian:stable bash diff --git a/general/g.mapsets/tests/conftest.py b/general/g.mapsets/tests/conftest.py index ca58228607c..58986369c03 100644 --- a/general/g.mapsets/tests/conftest.py +++ b/general/g.mapsets/tests/conftest.py @@ -5,7 +5,6 @@ Fixture for ReprojectionRenderer test with simple GRASS location, raster, vector. """ - from types import SimpleNamespace import grass.script as gs @@ -28,7 +27,7 @@ def simple_dataset(tmp_path_factory): gs.run_command("g.region", s=0, n=80, w=0, e=120, b=0, t=50, res=10, res3=10) # Create Mock Mapsets for mapset in TEST_MAPSETS: - gs.run_command("g.mapset", location=location, mapset=mapset, flags="c") + gs.run_command("g.mapset", project=location, mapset=mapset, flags="c") yield SimpleNamespace( mapsets=TEST_MAPSETS, accessible_mapsets=ACCESSIBLE_MAPSETS diff --git a/general/g.mkfontcap/main.c b/general/g.mkfontcap/main.c index 0a4a80fbae6..8122ba158e5 100644 --- a/general/g.mkfontcap/main.c +++ b/general/g.mkfontcap/main.c @@ -5,7 +5,7 @@ * PURPOSE: Generates the font configuration file by scanning various * directories for GRASS stroke and Freetype-compatible fonts. * - * COPYRIGHT: (C) 2007-2015 by the GRASS Development Team + * COPYRIGHT: (C) 2007-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 @@ -47,7 +47,7 @@ static int compare_fonts(const void *, const void *); int main(int argc, char *argv[]) { - struct Flag *tostdout, *overwrite; + struct Flag *tostdout; struct Option *extradirs; struct GModule *module; @@ -65,11 +65,6 @@ int main(int argc, char *argv[]) "Generates the font configuration file by scanning various directories " "for fonts."); - overwrite = G_define_flag(); - overwrite->key = 'o'; - overwrite->description = - _("Overwrite font configuration file if already existing"); - tostdout = G_define_flag(); tostdout->key = 's'; tostdout->description = @@ -99,11 +94,11 @@ int main(int argc, char *argv[]) G_asprintf(&fontcapfile, "%s/etc/fontcap", gisbase); if (!access(fontcapfile, F_OK)) { /* File exists? */ - if (!overwrite->answer) - G_fatal_error( - _("Fontcap file %s already exists; use -%c flag if you " - "wish to overwrite it"), - fontcapfile, overwrite->key); + if (!G_get_overwrite()) + G_fatal_error(_("Fontcap file %s already exists; use " + "--overwrite flag if you " + "wish to overwrite it"), + fontcapfile); } } diff --git a/general/g.region/testsuite/test_g_region.py b/general/g.region/testsuite/test_g_region.py index b2f9d8aa83e..7362c062f1d 100644 --- a/general/g.region/testsuite/test_g_region.py +++ b/general/g.region/testsuite/test_g_region.py @@ -2,6 +2,7 @@ @author Anna Petrasova """ + from grass.gunittest.case import TestCase from grass.gunittest.gmodules import call_module import grass.script as gs diff --git a/gui/wxpython/animation/controller.py b/gui/wxpython/animation/controller.py index d6488d53263..ed03f7dadb5 100644 --- a/gui/wxpython/animation/controller.py +++ b/gui/wxpython/animation/controller.py @@ -13,6 +13,7 @@ @author Anna Petrasova """ + import os import wx diff --git a/gui/wxpython/animation/data.py b/gui/wxpython/animation/data.py index d19c2d21054..5c2cdf35ef1 100644 --- a/gui/wxpython/animation/data.py +++ b/gui/wxpython/animation/data.py @@ -15,6 +15,7 @@ @author Anna Petrasova """ + import os import copy diff --git a/gui/wxpython/animation/frame.py b/gui/wxpython/animation/frame.py index 38f07866264..e5bbd389d4c 100644 --- a/gui/wxpython/animation/frame.py +++ b/gui/wxpython/animation/frame.py @@ -17,6 +17,7 @@ @author Anna Petrasova """ + import os import wx import wx.aui diff --git a/gui/wxpython/animation/toolbars.py b/gui/wxpython/animation/toolbars.py index 0fc21c70366..1ac3c2efedb 100644 --- a/gui/wxpython/animation/toolbars.py +++ b/gui/wxpython/animation/toolbars.py @@ -17,6 +17,7 @@ @author Anna Petrasova """ + import wx from gui_core.toolbars import BaseToolbar, BaseIcons from icons.icon import MetaIcon diff --git a/gui/wxpython/animation/utils.py b/gui/wxpython/animation/utils.py index b6c0d27abac..9be0fe78c17 100644 --- a/gui/wxpython/animation/utils.py +++ b/gui/wxpython/animation/utils.py @@ -17,6 +17,7 @@ @author Anna Perasova """ + import os import wx import hashlib diff --git a/gui/wxpython/core/treemodel.py b/gui/wxpython/core/treemodel.py index 1c1fc2deee9..46f255021e9 100644 --- a/gui/wxpython/core/treemodel.py +++ b/gui/wxpython/core/treemodel.py @@ -15,6 +15,7 @@ @author Anna Petrasova """ + import copy from grass.script.utils import naturally_sort diff --git a/gui/wxpython/datacatalog/dialogs.py b/gui/wxpython/datacatalog/dialogs.py index 948238c7146..5e463e83543 100644 --- a/gui/wxpython/datacatalog/dialogs.py +++ b/gui/wxpython/datacatalog/dialogs.py @@ -14,7 +14,6 @@ @author Anna Petrasova """ - import wx from gui_core.widgets import FloatValidator, IntegerValidator from core.giface import Notification diff --git a/gui/wxpython/gcp/statusbar.py b/gui/wxpython/gcp/statusbar.py index 1d10ad0b9b4..1424f7f4846 100644 --- a/gui/wxpython/gcp/statusbar.py +++ b/gui/wxpython/gcp/statusbar.py @@ -16,7 +16,6 @@ @author Anna Kratochvilova (statusbar refactoring) """ - import wx from core.gcmd import GMessage diff --git a/gui/wxpython/gui_core/menu.py b/gui/wxpython/gui_core/menu.py index 55ba0d40d48..3c1376cea0d 100644 --- a/gui/wxpython/gui_core/menu.py +++ b/gui/wxpython/gui_core/menu.py @@ -22,6 +22,7 @@ @author Vaclav Petras (menu customization) @author Tomas Zigo RecentFilesMenu """ + import re import os import wx diff --git a/gui/wxpython/gui_core/pystc.py b/gui/wxpython/gui_core/pystc.py index b11bf0f8418..bf268f88f33 100644 --- a/gui/wxpython/gui_core/pystc.py +++ b/gui/wxpython/gui_core/pystc.py @@ -15,7 +15,6 @@ @author Anna Petrasova (dark theme) """ - import keyword import wx diff --git a/gui/wxpython/gui_core/query.py b/gui/wxpython/gui_core/query.py index 638a64fa948..3a250af4d42 100644 --- a/gui/wxpython/gui_core/query.py +++ b/gui/wxpython/gui_core/query.py @@ -13,6 +13,7 @@ @author Anna Kratochvilova """ + import wx from gui_core.treeview import TreeListView diff --git a/gui/wxpython/icons/grass_icons.py b/gui/wxpython/icons/grass_icons.py index 2c13429d9ec..e4cd9b55ed7 100644 --- a/gui/wxpython/icons/grass_icons.py +++ b/gui/wxpython/icons/grass_icons.py @@ -3,6 +3,7 @@ http://robert.szczepanek.pl/icons.php https://svn.osgeo.org/osgeo/graphics/toolbar-icons/24x24/ """ + __author__ = "Robert Szczepanek" import os diff --git a/gui/wxpython/image2target/ii2t_statusbar.py b/gui/wxpython/image2target/ii2t_statusbar.py index 1d10ad0b9b4..1424f7f4846 100644 --- a/gui/wxpython/image2target/ii2t_statusbar.py +++ b/gui/wxpython/image2target/ii2t_statusbar.py @@ -16,7 +16,6 @@ @author Anna Kratochvilova (statusbar refactoring) """ - import wx from core.gcmd import GMessage diff --git a/gui/wxpython/iscatt/frame.py b/gui/wxpython/iscatt/frame.py index 242731ef182..9ff5a6ceac6 100644 --- a/gui/wxpython/iscatt/frame.py +++ b/gui/wxpython/iscatt/frame.py @@ -18,7 +18,6 @@ @author Stepan Turek (mentor: Martin Landa) """ - import os import wx diff --git a/gui/wxpython/iscatt/iscatt_core.py b/gui/wxpython/iscatt/iscatt_core.py index d97cdb84a56..1c5b2887edb 100644 --- a/gui/wxpython/iscatt/iscatt_core.py +++ b/gui/wxpython/iscatt/iscatt_core.py @@ -17,6 +17,7 @@ @author Stepan Turek (mentor: Martin Landa) """ + import os import numpy as np diff --git a/gui/wxpython/location_wizard/wizard.py b/gui/wxpython/location_wizard/wizard.py index 4127d5047d4..4f3a2924847 100644 --- a/gui/wxpython/location_wizard/wizard.py +++ b/gui/wxpython/location_wizard/wizard.py @@ -33,6 +33,7 @@ @author Martin Landa @author Hamish Bowman (planetary ellipsoids) """ + import os import locale import functools diff --git a/gui/wxpython/mapwin/graphics.py b/gui/wxpython/mapwin/graphics.py index 1522cf9f4dd..0bc94bae6d2 100644 --- a/gui/wxpython/mapwin/graphics.py +++ b/gui/wxpython/mapwin/graphics.py @@ -15,7 +15,6 @@ @author Stepan Turek (handlers support, GraphicsSet) """ - from copy import copy import wx diff --git a/gui/wxpython/photo2image/ip2i_statusbar.py b/gui/wxpython/photo2image/ip2i_statusbar.py index c1fcf9a9907..3415906aeee 100644 --- a/gui/wxpython/photo2image/ip2i_statusbar.py +++ b/gui/wxpython/photo2image/ip2i_statusbar.py @@ -16,7 +16,6 @@ @author Anna Kratochvilova (statusbar refactoring) """ - import wx from core.gcmd import GMessage diff --git a/gui/wxpython/psmap/utils.py b/gui/wxpython/psmap/utils.py index af68405446f..f7e4cea1ad3 100644 --- a/gui/wxpython/psmap/utils.py +++ b/gui/wxpython/psmap/utils.py @@ -15,6 +15,7 @@ @author Anna Kratochvilova """ + import wx from math import ceil, floor, sin, cos, pi diff --git a/gui/wxpython/rdigit/controller.py b/gui/wxpython/rdigit/controller.py index 26e9f455e68..2e98ca32ccc 100644 --- a/gui/wxpython/rdigit/controller.py +++ b/gui/wxpython/rdigit/controller.py @@ -13,6 +13,7 @@ @author Anna Petrasova """ + import os import tempfile import wx diff --git a/gui/wxpython/rlisetup/functions.py b/gui/wxpython/rlisetup/functions.py index d77962a97c1..2b9433ea4d6 100644 --- a/gui/wxpython/rlisetup/functions.py +++ b/gui/wxpython/rlisetup/functions.py @@ -3,6 +3,7 @@ @author: lucadelu """ + import wx import os import sys diff --git a/gui/wxpython/startup/guiutils.py b/gui/wxpython/startup/guiutils.py index ca04e52aefc..d94da9f9b16 100644 --- a/gui/wxpython/startup/guiutils.py +++ b/gui/wxpython/startup/guiutils.py @@ -14,7 +14,6 @@ This is for code which depend on something from GUI (wx or wxGUI). """ - import os import wx diff --git a/gui/wxpython/tplot/g.gui.tplot.py b/gui/wxpython/tplot/g.gui.tplot.py index ae07e0e4022..1a411dabf31 100755 --- a/gui/wxpython/tplot/g.gui.tplot.py +++ b/gui/wxpython/tplot/g.gui.tplot.py @@ -104,7 +104,7 @@ # %option # % key: size # % type: string -# % label: The size for output image +# % label: Size of output image as width,height in pixels # % description: It works only with output parameter # % required: no # %end diff --git a/gui/wxpython/vdigit/wxdigit.py b/gui/wxpython/vdigit/wxdigit.py index 9cf6005a077..0c36100e561 100644 --- a/gui/wxpython/vdigit/wxdigit.py +++ b/gui/wxpython/vdigit/wxdigit.py @@ -26,7 +26,6 @@ @author Martin Landa """ - import grass.script.core as grass from grass.pydispatch.signal import Signal diff --git a/gui/wxpython/vdigit/wxdisplay.py b/gui/wxpython/vdigit/wxdisplay.py index 21f86f3718d..988afc481ac 100644 --- a/gui/wxpython/vdigit/wxdisplay.py +++ b/gui/wxpython/vdigit/wxdisplay.py @@ -17,7 +17,6 @@ @author Martin Landa """ - import locale import os diff --git a/gui/wxpython/vnet/toolbars.py b/gui/wxpython/vnet/toolbars.py index bb0a1aacdd5..5f80e50cd9d 100644 --- a/gui/wxpython/vnet/toolbars.py +++ b/gui/wxpython/vnet/toolbars.py @@ -85,7 +85,7 @@ def _toolbarData(self): icons["pointDelete"], self.OnDeletePoint, ), - (None,) # , + (None,), # , # ('isec_turn_edit', icons['isec_turn_edit'], # self.dialog.OnDefIsecTurnCosts, # wx.ITEM_CHECK), diff --git a/gui/wxpython/wxplot/profile.py b/gui/wxpython/wxplot/profile.py index 591608767d7..1b55da68919 100644 --- a/gui/wxpython/wxplot/profile.py +++ b/gui/wxpython/wxplot/profile.py @@ -234,9 +234,11 @@ def SetupProfile(self): # transect self.seglist.append( ( - segment_geodesic_cum_dist - if self._is_lat_lon_proj and haveCtypes - else cumdist, + ( + segment_geodesic_cum_dist + if self._is_lat_lon_proj and haveCtypes + else cumdist + ), val, ) ) diff --git a/imagery/i.pca/testsuite/test_pca.py b/imagery/i.pca/testsuite/test_pca.py index 9da7991f876..8e9df84f2eb 100644 --- a/imagery/i.pca/testsuite/test_pca.py +++ b/imagery/i.pca/testsuite/test_pca.py @@ -8,6 +8,7 @@ License (>=v2). Read the file COPYING that comes with GRASS for details. """ + from grass.gunittest.case import TestCase diff --git a/imagery/i.segment/testsuite/test_isegment.py b/imagery/i.segment/testsuite/test_isegment.py index 09046f980af..66d24d41661 100644 --- a/imagery/i.segment/testsuite/test_isegment.py +++ b/imagery/i.segment/testsuite/test_isegment.py @@ -8,6 +8,7 @@ License (>=v2). Read the file COPYING that comes with GRASS for details. """ + from grass.gunittest.case import TestCase diff --git a/imagery/i.signatures/testsuite/test_i_signatures.py b/imagery/i.signatures/testsuite/test_i_signatures.py index 34f38774bc0..651d510ed03 100644 --- a/imagery/i.signatures/testsuite/test_i_signatures.py +++ b/imagery/i.signatures/testsuite/test_i_signatures.py @@ -8,6 +8,7 @@ Read the file COPYING that comes with GRASS for details """ + import os import shutil import json diff --git a/imagery/i.svm.predict/testsuite/test_i_svm_predict.py b/imagery/i.svm.predict/testsuite/test_i_svm_predict.py index 706af4f4fa2..3662124f45d 100644 --- a/imagery/i.svm.predict/testsuite/test_i_svm_predict.py +++ b/imagery/i.svm.predict/testsuite/test_i_svm_predict.py @@ -8,6 +8,7 @@ License (>=v2). Read the file COPYING that comes with GRASS for details. """ + import unittest import shutil diff --git a/imagery/i.svm.train/testsuite/test_i_svm_train.py b/imagery/i.svm.train/testsuite/test_i_svm_train.py index d895ffcd22f..f097405d98f 100644 --- a/imagery/i.svm.train/testsuite/test_i_svm_train.py +++ b/imagery/i.svm.train/testsuite/test_i_svm_train.py @@ -8,6 +8,7 @@ License (>=v2). Read the file COPYING that comes with GRASS for details. """ + import os import unittest import ctypes diff --git a/imagery/i.vi/testsuite/test_vi.py b/imagery/i.vi/testsuite/test_vi.py index a0da8245a5e..0c383417257 100644 --- a/imagery/i.vi/testsuite/test_vi.py +++ b/imagery/i.vi/testsuite/test_vi.py @@ -8,6 +8,7 @@ License (>=v2). Read the file COPYING that comes with GRASS for details. """ + from grass.gunittest.case import TestCase diff --git a/lib/driver/font2.c b/lib/driver/font2.c index 7b80a5e8870..67e26572542 100644 --- a/lib/driver/font2.c +++ b/lib/driver/font2.c @@ -146,7 +146,7 @@ static void read_fontmap(const char *name) fp = fopen(buf, "r"); if (!fp) { G_warning("Unable to open font map '%s': %s. " - "Try running 'g.mkfontcap -o'", + "Try running 'g.mkfontcap --overwrite'", buf, strerror(errno)); return; } diff --git a/lib/gis/aprintf.c b/lib/gis/aprintf.c index 67cde7f97a5..2c7b235f214 100644 --- a/lib/gis/aprintf.c +++ b/lib/gis/aprintf.c @@ -199,11 +199,11 @@ static int oaprintf(struct options *opts, const char *format, va_list ap) while (*++c && *q != *c) ; if (*c) { - va_list ap_copy; + va_list aq; char tmp; /* copy ap for ovprintf() */ - va_copy(ap_copy, ap); + va_copy(aq, ap); /* found a conversion specifier */ if (*c == 's') { @@ -254,7 +254,7 @@ static int oaprintf(struct options *opts, const char *format, va_list ap) } if (*p_spec) { /* illegal string specifier? */ - va_end(ap_copy); + va_end(aq); *(q + 1) = 0; G_fatal_error( _("Failed to parse string specifier: %s"), p); @@ -290,7 +290,7 @@ static int oaprintf(struct options *opts, const char *format, va_list ap) if (use_ovprintf) { tmp = *(q + 1); *(q + 1) = 0; - nbytes += ovprintf(opts, p, ap_copy); + nbytes += ovprintf(opts, p, aq); *(q + 1) = tmp; } } @@ -298,7 +298,7 @@ static int oaprintf(struct options *opts, const char *format, va_list ap) /* else use ovprintf() for non-string specifiers */ tmp = *(q + 1); *(q + 1) = 0; - nbytes += ovprintf(opts, p, ap_copy); + nbytes += ovprintf(opts, p, aq); *(q + 1) = tmp; /* once ap is passed to another function that calls @@ -345,7 +345,7 @@ static int oaprintf(struct options *opts, const char *format, va_list ap) /* otherwise, no argument is required for m% */ } } - va_end(ap_copy); + va_end(aq); break; } else if (p_spec - spec < SPEC_BUF_SIZE - 2) diff --git a/lib/gis/renamed_options b/lib/gis/renamed_options index 965168b29d9..00893c68c83 100644 --- a/lib/gis/renamed_options +++ b/lib/gis/renamed_options @@ -95,6 +95,8 @@ g.mapset|gisdbase:dbase g.mapsets|addmapset:operation g.mapsets|removemapset:operation g.mapsets|measure:method +# g.mkfontcap +g.mkfontcap|-o:--overwrite # g.pnmcomp g.pnmcomp|background:bgcolor ################################### diff --git a/lib/gis/testsuite/gis_lib_env_test.py b/lib/gis/testsuite/gis_lib_env_test.py index 91382f552c0..fafc00a37ed 100644 --- a/lib/gis/testsuite/gis_lib_env_test.py +++ b/lib/gis/testsuite/gis_lib_env_test.py @@ -2,6 +2,7 @@ @author Soeren Gebbert """ + from grass.gunittest.case import TestCase import grass.lib.gis as libgis diff --git a/lib/gmath/fft.c b/lib/gmath/fft.c index c0dea7b7d0e..3615fb602b2 100644 --- a/lib/gmath/fft.c +++ b/lib/gmath/fft.c @@ -25,20 +25,16 @@ #include -#if defined(HAVE_FFTW_H) || defined(HAVE_DFFTW_H) || defined(HAVE_FFTW3_H) +#if defined(HAVE_FFTW3_H) || defined(HAVE_FFTW_H) || defined(HAVE_DFFTW_H) -#ifdef HAVE_FFTW_H -#include -#endif - -#ifdef HAVE_DFFTW_H -#include -#endif - -#ifdef HAVE_FFTW3_H +#if defined(HAVE_FFTW3_H) #include #define c_re(c) ((c)[0]) #define c_im(c) ((c)[1]) +#elif defined(HAVE_FFTW_H) +#include +#elif defined(HAVE_DFFTW_H) +#include #endif #include diff --git a/lib/imagery/testsuite/test_imagery_find.py b/lib/imagery/testsuite/test_imagery_find.py index a943e2d3520..72e99bc2776 100644 --- a/lib/imagery/testsuite/test_imagery_find.py +++ b/lib/imagery/testsuite/test_imagery_find.py @@ -8,6 +8,7 @@ Read the file COPYING that comes with GRASS for details """ + import os import shutil diff --git a/lib/imagery/testsuite/test_imagery_sigfile.py b/lib/imagery/testsuite/test_imagery_sigfile.py index c8bc90a4f95..104cd5a59b4 100644 --- a/lib/imagery/testsuite/test_imagery_sigfile.py +++ b/lib/imagery/testsuite/test_imagery_sigfile.py @@ -8,6 +8,7 @@ Read the file COPYING that comes with GRASS for details """ + import os import stat import ctypes @@ -383,9 +384,9 @@ def test_symmetric_complete_difference(self): self.assertEqual(ref_err, "The_Doors") # Clean up memory to help track memory leaks when run by valgrind - S.semantic_labels[ - 0 - ] = None # C should not call free() on memory allocated by python + S.semantic_labels[0] = ( + None # C should not call free() on memory allocated by python + ) I_free_signatures(ctypes.byref(S)) I_free_group_ref(ctypes.byref(R)) if ret: diff --git a/lib/imagery/testsuite/test_imagery_signature_management.py b/lib/imagery/testsuite/test_imagery_signature_management.py index b2ec954a74e..21f052372e2 100644 --- a/lib/imagery/testsuite/test_imagery_signature_management.py +++ b/lib/imagery/testsuite/test_imagery_signature_management.py @@ -8,6 +8,7 @@ Read the file COPYING that comes with GRASS for details """ + import os import shutil import ctypes diff --git a/lib/imagery/testsuite/test_imagery_sigsetfile.py b/lib/imagery/testsuite/test_imagery_sigsetfile.py index 6e5a119c236..b8bcb566520 100644 --- a/lib/imagery/testsuite/test_imagery_sigsetfile.py +++ b/lib/imagery/testsuite/test_imagery_sigsetfile.py @@ -8,6 +8,7 @@ Read the file COPYING that comes with GRASS for details """ + import os import stat import ctypes diff --git a/lib/pngdriver/pngdriver.h b/lib/pngdriver/pngdriver.h index 62b61aa700e..0a4b9564e72 100644 --- a/lib/pngdriver/pngdriver.h +++ b/lib/pngdriver/pngdriver.h @@ -54,13 +54,17 @@ extern void read_image(void); extern void read_ppm(void); extern void read_pgm(void); extern void read_bmp(void); +#ifdef HAVE_PNG_H extern void read_png(void); +#endif extern void write_image(void); extern void write_ppm(void); extern void write_pgm(void); extern void write_bmp(void); +#ifdef HAVE_PNG_H extern void write_png(void); +#endif extern void png_init_color_table(void); extern unsigned int png_get_color(int, int, int, int); diff --git a/lib/raster/testsuite/test_raster_metadata.py b/lib/raster/testsuite/test_raster_metadata.py index 7da2c0fe4cf..43bbfaeea99 100644 --- a/lib/raster/testsuite/test_raster_metadata.py +++ b/lib/raster/testsuite/test_raster_metadata.py @@ -8,6 +8,7 @@ Read the file COPYING that comes with GRASS for details """ + import random import string diff --git a/lib/raster3d/testsuite/raster3d_lib_test.py b/lib/raster3d/testsuite/raster3d_lib_test.py index 5ec919c94ba..a0020ab9ef1 100644 --- a/lib/raster3d/testsuite/raster3d_lib_test.py +++ b/lib/raster3d/testsuite/raster3d_lib_test.py @@ -2,6 +2,7 @@ @author Soeren Gebbert """ + from grass.gunittest.case import TestCase diff --git a/man/parser_standard_options.py b/man/parser_standard_options.py index 3454084f330..6a6d769ab5e 100644 --- a/man/parser_standard_options.py +++ b/man/parser_standard_options.py @@ -3,6 +3,7 @@ @author: pietro """ + import argparse import os import sys diff --git a/mswindows/GRASS-Installer.nsi.tmpl b/mswindows/GRASS-Installer.nsi.tmpl index 47e7d6a025a..2b2722a5083 100644 --- a/mswindows/GRASS-Installer.nsi.tmpl +++ b/mswindows/GRASS-Installer.nsi.tmpl @@ -557,7 +557,7 @@ Section "GRASS" SecGRASS FileWrite $0 'set GISRC=dummy$\r$\n' FileWrite $0 '$\r$\n' FileWrite $0 'rem run g.mkfontcap outside a grass session$\r$\n' - FileWrite $0 '"%GISBASE%\bin\g.mkfontcap.exe" -o$\r$\n' + FileWrite $0 '"%GISBASE%\bin\g.mkfontcap.exe" --overwrite$\r$\n' FileWrite $0 'exit$\r$\n' FileClose $0 done_create_run_gmkfontcap.bat: diff --git a/mswindows/osgeo4w/postinstall.bat b/mswindows/osgeo4w/postinstall.bat index e9f75cecfdf..61d2d98b7d4 100644 --- a/mswindows/osgeo4w/postinstall.bat +++ b/mswindows/osgeo4w/postinstall.bat @@ -24,6 +24,6 @@ rem GISRC must be set set GISRC=dummy rem run g.mkfontcap outside a GRASS session -"%GISBASE%\bin\g.mkfontcap.exe" -o +"%GISBASE%\bin\g.mkfontcap.exe" --overwrite del "%BATCH%.tmpl diff --git a/pyproject.toml b/pyproject.toml index e06ff451bd0..c99b85ab4dc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,24 +1,11 @@ [tool.black] +required-version = '24' line-length = 88 -target-version = ['py37', 'py38', 'py39', 'py310'] -include = '\.pyi?$' -exclude = ''' +target-version = ['py38', 'py39', 'py310', 'py311', 'py312'] +# 'extend-exclude' excludes files or directories in addition to the defaults +extend-exclude = ''' ( - # exclude directories in the root of the project - /( - \.eggs - | \.git - | \.hg - | \.mypy_cache - | \.tox - | \.venv - | _build - | buck-out - | build - | bin\..* - | dist\..* - )/ - | python/libgrass_interface_generator/ + python/libgrass_interface_generator/ ) ''' diff --git a/python/grass/grassdb/create.py b/python/grass/grassdb/create.py index f7e7b509405..041b5ed28f8 100644 --- a/python/grass/grassdb/create.py +++ b/python/grass/grassdb/create.py @@ -9,7 +9,6 @@ .. sectionauthor:: Vaclav Petras """ - import os import shutil import getpass diff --git a/python/grass/grassdb/manage.py b/python/grass/grassdb/manage.py index c79df87ce07..0446263615e 100644 --- a/python/grass/grassdb/manage.py +++ b/python/grass/grassdb/manage.py @@ -9,7 +9,6 @@ .. sectionauthor:: Vaclav Petras """ - import os import shutil import sys diff --git a/python/grass/gunittest/multireport.py b/python/grass/gunittest/multireport.py index 54c1c915909..014d8296e4b 100644 --- a/python/grass/gunittest/multireport.py +++ b/python/grass/gunittest/multireport.py @@ -9,7 +9,6 @@ :authors: Vaclav Petras """ - import sys import os import argparse diff --git a/python/grass/gunittest/runner.py b/python/grass/gunittest/runner.py index 8879527a24c..891e1b65a5a 100644 --- a/python/grass/gunittest/runner.py +++ b/python/grass/gunittest/runner.py @@ -12,7 +12,6 @@ a template. It is not expected that something will left. """ - import sys import time diff --git a/python/grass/gunittest/testsuite/test_assertions.py b/python/grass/gunittest/testsuite/test_assertions.py index fb14e50bbd8..bf0f946d37d 100644 --- a/python/grass/gunittest/testsuite/test_assertions.py +++ b/python/grass/gunittest/testsuite/test_assertions.py @@ -2,7 +2,6 @@ Tests assertion methods. """ - import os import grass.script.core as gcore diff --git a/python/grass/gunittest/testsuite/test_checkers.py b/python/grass/gunittest/testsuite/test_checkers.py index 8d6571a8aa4..d35ffcb19d8 100644 --- a/python/grass/gunittest/testsuite/test_checkers.py +++ b/python/grass/gunittest/testsuite/test_checkers.py @@ -11,7 +11,6 @@ @author Vaclav Petras """ - from grass.script.utils import parse_key_val, try_remove from grass.gunittest.case import TestCase diff --git a/python/grass/imaging/operations.py b/python/grass/imaging/operations.py index d1837a485b0..8c60c4c599e 100644 --- a/python/grass/imaging/operations.py +++ b/python/grass/imaging/operations.py @@ -53,7 +53,6 @@ .. sectionauthor:: Vaclav Petras """ - # import similar to what is in visvis try: import PIL diff --git a/python/grass/jupyter/tests/seriesmap_test.py b/python/grass/jupyter/tests/seriesmap_test.py index 79f3b20dbda..e19faf5f663 100644 --- a/python/grass/jupyter/tests/seriesmap_test.py +++ b/python/grass/jupyter/tests/seriesmap_test.py @@ -1,6 +1,5 @@ """Test SeriesMap functions""" - from pathlib import Path import pytest diff --git a/python/grass/jupyter/tests/timeseriesmap_test.py b/python/grass/jupyter/tests/timeseriesmap_test.py index ed7e5c6e156..d75ec8372e4 100644 --- a/python/grass/jupyter/tests/timeseriesmap_test.py +++ b/python/grass/jupyter/tests/timeseriesmap_test.py @@ -1,6 +1,5 @@ """Test TimeSeriesMap functions""" - from pathlib import Path import pytest diff --git a/python/grass/pydispatch/dispatcher.py b/python/grass/pydispatch/dispatcher.py index a34706f8cf7..0915629fdd9 100644 --- a/python/grass/pydispatch/dispatcher.py +++ b/python/grass/pydispatch/dispatcher.py @@ -25,6 +25,7 @@ deletion, (considerably speeds up the cleanup process vs. the original code.) """ + import weakref from grass.pydispatch import saferef, robustapply, errors diff --git a/python/grass/pydispatch/robust.py b/python/grass/pydispatch/robust.py index f65471037d7..9923d3afc90 100644 --- a/python/grass/pydispatch/robust.py +++ b/python/grass/pydispatch/robust.py @@ -1,4 +1,5 @@ """Module implementing error-catching version of send (sendRobust)""" + from grass.pydispatch.dispatcher import Any, Anonymous, liveReceivers, getAllReceivers from grass.pydispatch.robustapply import robustApply diff --git a/python/grass/pydispatch/robustapply.py b/python/grass/pydispatch/robustapply.py index ac4211bbddf..1c10bc917c6 100644 --- a/python/grass/pydispatch/robustapply.py +++ b/python/grass/pydispatch/robustapply.py @@ -5,6 +5,7 @@ and subset the given arguments to match only those which are acceptable. """ + import sys if sys.hexversion >= 0x3000000: diff --git a/python/grass/pydispatch/signal.py b/python/grass/pydispatch/signal.py index edb962853da..1e968e99dca 100644 --- a/python/grass/pydispatch/signal.py +++ b/python/grass/pydispatch/signal.py @@ -4,7 +4,6 @@ @author Vaclav Petras """ - from grass.pydispatch import dispatcher diff --git a/python/grass/pygrass/messages/__init__.py b/python/grass/pygrass/messages/__init__.py index 60e8e43e31c..36356433381 100644 --- a/python/grass/pygrass/messages/__init__.py +++ b/python/grass/pygrass/messages/__init__.py @@ -13,6 +13,7 @@ @author Soeren Gebbert """ + import sys from multiprocessing import Process, Lock, Pipe diff --git a/python/grass/pygrass/modules/grid/patch.py b/python/grass/pygrass/modules/grid/patch.py index 3d7c5432e43..be555583591 100644 --- a/python/grass/pygrass/modules/grid/patch.py +++ b/python/grass/pygrass/modules/grid/patch.py @@ -3,6 +3,7 @@ @author: pietro """ + from grass.pygrass.gis.region import Region from grass.pygrass.raster import RasterRow from grass.pygrass.utils import coor2pixel diff --git a/python/grass/pygrass/modules/interface/__init__.py b/python/grass/pygrass/modules/interface/__init__.py index 37546d463dd..a05211e21c3 100644 --- a/python/grass/pygrass/modules/interface/__init__.py +++ b/python/grass/pygrass/modules/interface/__init__.py @@ -3,6 +3,7 @@ @author: pietro """ + from grass.pygrass.modules.interface import flag from grass.pygrass.modules.interface import parameter from grass.pygrass.modules.interface import module diff --git a/python/grass/pygrass/modules/interface/env.py b/python/grass/pygrass/modules/interface/env.py index 98e5208ef5b..4fbef04dae3 100644 --- a/python/grass/pygrass/modules/interface/env.py +++ b/python/grass/pygrass/modules/interface/env.py @@ -3,6 +3,7 @@ @author: pietro """ + import os import sys diff --git a/python/grass/pygrass/modules/interface/testsuite/test_flag.py b/python/grass/pygrass/modules/interface/testsuite/test_flag.py index 26b9ba531ff..909c6d00d55 100644 --- a/python/grass/pygrass/modules/interface/testsuite/test_flag.py +++ b/python/grass/pygrass/modules/interface/testsuite/test_flag.py @@ -3,6 +3,7 @@ @author: pietro """ + from grass.gunittest.case import TestCase from grass.gunittest.main import test diff --git a/python/grass/pygrass/modules/interface/testsuite/test_modules.py b/python/grass/pygrass/modules/interface/testsuite/test_modules.py index 10b4c3f2529..d95bf6177af 100644 --- a/python/grass/pygrass/modules/interface/testsuite/test_modules.py +++ b/python/grass/pygrass/modules/interface/testsuite/test_modules.py @@ -3,6 +3,7 @@ @author: pietro """ + from fnmatch import fnmatch from io import BytesIO diff --git a/python/grass/pygrass/modules/interface/testsuite/test_parameter.py b/python/grass/pygrass/modules/interface/testsuite/test_parameter.py index e3bf9921bfd..57f17cb3027 100644 --- a/python/grass/pygrass/modules/interface/testsuite/test_parameter.py +++ b/python/grass/pygrass/modules/interface/testsuite/test_parameter.py @@ -3,6 +3,7 @@ @author: pietro """ + from grass.gunittest.case import TestCase from grass.gunittest.main import test diff --git a/python/grass/pygrass/modules/interface/typedict.py b/python/grass/pygrass/modules/interface/typedict.py index f2ece769d6c..c8fdeb48a76 100644 --- a/python/grass/pygrass/modules/interface/typedict.py +++ b/python/grass/pygrass/modules/interface/typedict.py @@ -3,6 +3,7 @@ @author: pietro """ + from collections import OrderedDict from copy import deepcopy diff --git a/python/grass/pygrass/modules/testsuite/test_import_isolation.py b/python/grass/pygrass/modules/testsuite/test_import_isolation.py index d91ae1a830b..d1321a554d0 100644 --- a/python/grass/pygrass/modules/testsuite/test_import_isolation.py +++ b/python/grass/pygrass/modules/testsuite/test_import_isolation.py @@ -9,6 +9,7 @@ Created on Wed Jul 15 11:34:32 2015 """ + import sys import fnmatch diff --git a/python/grass/pygrass/raster/category.py b/python/grass/pygrass/raster/category.py index b7a1294d96c..b43a10ae48d 100644 --- a/python/grass/pygrass/raster/category.py +++ b/python/grass/pygrass/raster/category.py @@ -3,6 +3,7 @@ @author: pietro """ + import ctypes from operator import itemgetter diff --git a/python/grass/pygrass/raster/history.py b/python/grass/pygrass/raster/history.py index 6140a6197b2..5041c9e55d8 100644 --- a/python/grass/pygrass/raster/history.py +++ b/python/grass/pygrass/raster/history.py @@ -3,6 +3,7 @@ @author: pietro """ + import ctypes import grass.lib.raster as libraster import datetime diff --git a/python/grass/pygrass/raster/raster_type.py b/python/grass/pygrass/raster/raster_type.py index 0b8244fe551..aa54a6d6df6 100644 --- a/python/grass/pygrass/raster/raster_type.py +++ b/python/grass/pygrass/raster/raster_type.py @@ -3,6 +3,7 @@ @author: pietro """ + import grass.lib.raster as libraster import ctypes import numpy as np diff --git a/python/grass/pygrass/raster/rowio.py b/python/grass/pygrass/raster/rowio.py index 650419122c3..62c5683ef68 100644 --- a/python/grass/pygrass/raster/rowio.py +++ b/python/grass/pygrass/raster/rowio.py @@ -3,6 +3,7 @@ @author: pietro """ + import ctypes import grass.lib.rowio as librowio diff --git a/python/grass/pygrass/raster/segment.py b/python/grass/pygrass/raster/segment.py index 6e9c4c4c781..60966d0f846 100644 --- a/python/grass/pygrass/raster/segment.py +++ b/python/grass/pygrass/raster/segment.py @@ -3,6 +3,7 @@ @author: pietro """ + import ctypes import grass.lib.gis as libgis import grass.lib.raster as libraster diff --git a/python/grass/pygrass/raster/testsuite/test_numpy.py b/python/grass/pygrass/raster/testsuite/test_numpy.py index c773e628f96..a2a6e1cdd26 100644 --- a/python/grass/pygrass/raster/testsuite/test_numpy.py +++ b/python/grass/pygrass/raster/testsuite/test_numpy.py @@ -3,6 +3,7 @@ @author: lucadelu """ + from grass.gunittest.case import TestCase from grass.gunittest.main import test from numpy.random import random diff --git a/python/grass/pygrass/shell/conversion.py b/python/grass/pygrass/shell/conversion.py index 344dfd2811b..76802c08ce1 100644 --- a/python/grass/pygrass/shell/conversion.py +++ b/python/grass/pygrass/shell/conversion.py @@ -4,7 +4,6 @@ @author: pietro """ - dcont = """ {key} {value} diff --git a/python/grass/pygrass/vector/abstract.py b/python/grass/pygrass/vector/abstract.py index e1dda3a8d7d..bafbb006d6c 100644 --- a/python/grass/pygrass/vector/abstract.py +++ b/python/grass/pygrass/vector/abstract.py @@ -3,6 +3,7 @@ @author: pietro """ + import ctypes import datetime import grass.lib.vector as libvect diff --git a/python/grass/pygrass/vector/basic.py b/python/grass/pygrass/vector/basic.py index 8917629ca43..2b8c013b433 100644 --- a/python/grass/pygrass/vector/basic.py +++ b/python/grass/pygrass/vector/basic.py @@ -3,6 +3,7 @@ @author: pietro """ + import ctypes import grass.lib.vector as libvect from collections.abc import Iterable diff --git a/python/grass/pygrass/vector/find.py b/python/grass/pygrass/vector/find.py index ac56265447c..50599af5ab5 100644 --- a/python/grass/pygrass/vector/find.py +++ b/python/grass/pygrass/vector/find.py @@ -3,6 +3,7 @@ @author: pietro """ + import grass.lib.vector as libvect from grass.pygrass.errors import must_be_open diff --git a/python/grass/pygrass/vector/geometry.py b/python/grass/pygrass/vector/geometry.py index 81d9f3a8d78..855f374b47e 100644 --- a/python/grass/pygrass/vector/geometry.py +++ b/python/grass/pygrass/vector/geometry.py @@ -3,6 +3,7 @@ @author: pietro """ + import ctypes import re from collections import namedtuple diff --git a/python/grass/pygrass/vector/testsuite/test_filters.py b/python/grass/pygrass/vector/testsuite/test_filters.py index 6d3d7247f66..9252b134c02 100644 --- a/python/grass/pygrass/vector/testsuite/test_filters.py +++ b/python/grass/pygrass/vector/testsuite/test_filters.py @@ -3,6 +3,7 @@ @author: pietro """ + from grass.gunittest.case import TestCase from grass.gunittest.main import test diff --git a/python/grass/pygrass/vector/testsuite/test_geometry.py b/python/grass/pygrass/vector/testsuite/test_geometry.py index a81c7d4a96f..d4ad7431278 100644 --- a/python/grass/pygrass/vector/testsuite/test_geometry.py +++ b/python/grass/pygrass/vector/testsuite/test_geometry.py @@ -3,6 +3,7 @@ @author: pietro """ + import sys import unittest import numpy as np diff --git a/python/grass/pygrass/vector/testsuite/test_geometry_attrs.py b/python/grass/pygrass/vector/testsuite/test_geometry_attrs.py index a1b75e0e9a6..4c17ef03d4c 100644 --- a/python/grass/pygrass/vector/testsuite/test_geometry_attrs.py +++ b/python/grass/pygrass/vector/testsuite/test_geometry_attrs.py @@ -3,6 +3,7 @@ @author: pietro """ + import sys import unittest import numpy as np diff --git a/python/grass/pygrass/vector/testsuite/test_table.py b/python/grass/pygrass/vector/testsuite/test_table.py index d80be7beb04..d603817de98 100644 --- a/python/grass/pygrass/vector/testsuite/test_table.py +++ b/python/grass/pygrass/vector/testsuite/test_table.py @@ -3,6 +3,7 @@ @author: pietro """ + import os import sqlite3 import tempfile as tmp diff --git a/python/grass/pygrass/vector/testsuite/test_vector.py b/python/grass/pygrass/vector/testsuite/test_vector.py index 12a553e02e2..a57856eca45 100644 --- a/python/grass/pygrass/vector/testsuite/test_vector.py +++ b/python/grass/pygrass/vector/testsuite/test_vector.py @@ -3,6 +3,7 @@ @author: pietro """ + from grass.gunittest.case import TestCase from grass.gunittest.main import test diff --git a/python/grass/pygrass/vector/testsuite/test_vector3d.py b/python/grass/pygrass/vector/testsuite/test_vector3d.py index 25fe46c8914..16ee0fd969e 100644 --- a/python/grass/pygrass/vector/testsuite/test_vector3d.py +++ b/python/grass/pygrass/vector/testsuite/test_vector3d.py @@ -3,6 +3,7 @@ @author: pietro """ + import numpy as np from grass.gunittest.case import TestCase diff --git a/python/grass/script/task.py b/python/grass/script/task.py index 1c4e29f69a6..325b5640edc 100644 --- a/python/grass/script/task.py +++ b/python/grass/script/task.py @@ -17,6 +17,7 @@ .. sectionauthor:: Martin Landa """ + import os import re import sys diff --git a/python/grass/script/testsuite/test_names.py b/python/grass/script/testsuite/test_names.py index 72db4aff174..6d48596c392 100644 --- a/python/grass/script/testsuite/test_names.py +++ b/python/grass/script/testsuite/test_names.py @@ -109,7 +109,11 @@ def works_for_vector_table_column(self, name): try: gs.run_command("v.edit", map=name, tool="create") gs.run_command("v.db.addtable", map=name) - gs.run_command("v.db.addcolumn", map=name, columns=name) + gs.run_command( + "v.db.addcolumn", + map=name, + columns=f"{name} integer", + ) works = True except gs.CalledModuleError: works = False diff --git a/python/grass/temporal/abstract_dataset.py b/python/grass/temporal/abstract_dataset.py index 4d6d4d81b51..720b2210ad8 100644 --- a/python/grass/temporal/abstract_dataset.py +++ b/python/grass/temporal/abstract_dataset.py @@ -9,6 +9,7 @@ :authors: Soeren Gebbert """ + from abc import ABCMeta, abstractmethod from .core import ( get_tgis_message_interface, diff --git a/python/grass/temporal/c_libraries_interface.py b/python/grass/temporal/c_libraries_interface.py index 260fdeae1e1..9db4a897077 100644 --- a/python/grass/temporal/c_libraries_interface.py +++ b/python/grass/temporal/c_libraries_interface.py @@ -51,6 +51,7 @@ class RPCDefs: WRITE_SEMANTIC_LABEL = 15 READ_SEMANTIC_LABEL = 16 REMOVE_SEMANTIC_LABEL = 17 + READ_MAP_HISTORY = 18 G_FATAL_ERROR = 49 TYPE_RASTER = 0 @@ -981,6 +982,142 @@ def _read_vector_info(name, mapset): ############################################################################### +def _read_map_history(lock, conn, data): + """Read map history from the spatial database using C-library functions + + :param lock: A multiprocessing.Lock instance + :param conn: A multiprocessing.Pipe instance used to send True or False + :param data: The list of data entries [function_id, maptype, name, mapset] + """ + kvp = None + try: + maptype = data[1] + name = data[2] + mapset = data[3] + if maptype == RPCDefs.TYPE_RASTER: + kvp = _read_raster_history(name, mapset) + elif maptype == RPCDefs.TYPE_VECTOR: + kvp = _read_vector_history(name, mapset) + elif maptype == RPCDefs.TYPE_RASTER3D: + kvp = _read_raster3d_history(name, mapset) + except: + raise + finally: + conn.send(kvp) + + +############################################################################### + + +def _read_raster_history(name, mapset): + """Read the raster history from the file system and store the content + into a dictionary + + This method uses the ctypes interface to the gis and raster libraries + to read the map history + + :param name: The name of the map + :param mapset: The mapset of the map + :returns: The key value pairs of the map specific metadata, or None in + case of an error + """ + + kvp = {} + + if not libgis.G_find_raster(name, mapset): + return None + + # Read the raster history + hist = libraster.History() + ret = libraster.Rast_read_history(name, mapset, byref(hist)) + if ret < 0: + logging.warning(_("Unable to read history file")) + return None + else: + kvp["creation_time"] = decode( + libraster.Rast_get_history(byref(hist), libraster.HIST_MAPID) + ) + kvp["creator"] = decode( + libraster.Rast_get_history(byref(hist), libraster.HIST_CREATOR) + ) + + return kvp + + +############################################################################### + + +def _read_raster3d_history(name, mapset): + """Read the 3D raster map info from the file system and store the content + into a dictionary + + This method uses the ctypes interface to the gis and raster3d libraries + to read the map metadata information + + :param name: The name of the map + :param mapset: The mapset of the map + :returns: The key value pairs of the map specific metadata, or None in + case of an error + """ + + kvp = {} + + if not libgis.G_find_raster3d(name, mapset): + return None + + # Read the region information + hist = libraster.History() + ret = libraster3d.Rast3d_read_history(name, mapset, byref(hist)) + if ret < 0: + logging.warning(_("Unable to read history file")) + return None + else: + kvp["creation_time"] = decode( + libraster.Rast_get_history(byref(hist), libraster3d.HIST_MAPID) + ) + kvp["creator"] = decode( + libraster.Rast_get_history(byref(hist), libraster3d.HIST_CREATOR) + ) + + return kvp + + +############################################################################### + + +def _read_vector_history(name, mapset): + """Read the vector history from the file system and store the content + into a dictionary + + This method uses the ctypes interface to the gis and raster libraries + to read the map history + + :param name: The name of the map + :param mapset: The mapset of the map + :returns: The key value pairs of the map specific metadata, or None in + case of an error + """ + + kvp = {} + + if not libgis.G_find_vector(name, mapset): + return None + + # Read the vector history + Map = libvector.Map_info() + if libvector.Vect_open_old(byref(Map), name, mapset, "1") > 0: + kvp["creation_time"] = decode(libvector.Vect_get_map_date(byref(Map))) + kvp["creator"] = decode(libvector.Vect_get_person(byref(Map))) + else: + None + libvector.Vect_close(byref(Map)) + + return kvp + + +############################################################################### + + def _convert_timestamp_from_grass(ts): """Convert a GRASS file based timestamp into the temporal framework format datetime or integer. @@ -1124,6 +1261,7 @@ def error_handler(data): functions[RPCDefs.WRITE_SEMANTIC_LABEL] = _write_semantic_label functions[RPCDefs.READ_SEMANTIC_LABEL] = _read_semantic_label functions[RPCDefs.REMOVE_SEMANTIC_LABEL] = _remove_semantic_label + functions[RPCDefs.READ_MAP_HISTORY] = _read_map_history functions[RPCDefs.G_FATAL_ERROR] = _fatal_error libgis.G_gisinit("c_library_server") @@ -1416,6 +1554,21 @@ def read_raster_full_info(self, name, mapset): ) return self.safe_receive("read_raster_full_info") + def read_raster_history(self, name, mapset): + """Read the raster map history from the file system and store the content + into a dictionary + + :param name: The name of the map + :param mapset: The mapset of the map + :returns: The key value pairs of the map history (creation, creation_time), + or None in case of an error + """ + self.check_server() + self.client_conn.send( + [RPCDefs.READ_MAP_HISTORY, RPCDefs.TYPE_RASTER, name, mapset, None] + ) + return self.safe_receive("read_raster_history") + def has_raster_timestamp(self, name, mapset): """Check if a file based raster timestamp exists @@ -1575,6 +1728,21 @@ def read_raster3d_info(self, name, mapset): ) return self.safe_receive("read_raster3d_info") + def read_raster3d_history(self, name, mapset): + """Read the 3D raster map history from the file system and store the content + into a dictionary + + :param name: The name of the map + :param mapset: The mapset of the map + :returns: The key value pairs of the map history (creation, creation_time), + or None in case of an error + """ + self.check_server() + self.client_conn.send( + [RPCDefs.READ_MAP_HISTORY, RPCDefs.TYPE_RASTER3D, name, mapset, None] + ) + return self.safe_receive("read_raster3d_history") + def has_raster3d_timestamp(self, name, mapset): """Check if a file based 3D raster timestamp exists @@ -1697,6 +1865,21 @@ def read_vector_full_info(self, name, mapset): ) return self.safe_receive("read_vector_full_info") + def read_vector_history(self, name, mapset): + """Read the vector map history from the file system and store the content + into a dictionary + + :param name: The name of the map + :param mapset: The mapset of the map + :returns: The key value pairs of the map history (creation, creation_time), + or None in case of an error + """ + self.check_server() + self.client_conn.send( + [RPCDefs.READ_MAP_HISTORY, RPCDefs.TYPE_VECTOR, name, mapset, None] + ) + return self.safe_receive("read_vector_history") + def has_vector_timestamp(self, name, mapset, layer=None): """Check if a file based vector timestamp exists diff --git a/python/grass/temporal/extract.py b/python/grass/temporal/extract.py index 9cb91ac2230..3f8c9cb0738 100644 --- a/python/grass/temporal/extract.py +++ b/python/grass/temporal/extract.py @@ -8,6 +8,7 @@ :authors: Soeren Gebbert """ + from .core import ( get_tgis_message_interface, get_current_mapset, diff --git a/python/grass/temporal/factory.py b/python/grass/temporal/factory.py index cc3652ff4ca..b7ef8ab5218 100644 --- a/python/grass/temporal/factory.py +++ b/python/grass/temporal/factory.py @@ -17,6 +17,7 @@ :authors: Soeren Gebbert """ + from .core import get_tgis_message_interface from .space_time_datasets import ( SpaceTimeRaster3DDataset, diff --git a/python/grass/temporal/gui_support.py b/python/grass/temporal/gui_support.py index 44e32b6e2e8..09b858657bc 100644 --- a/python/grass/temporal/gui_support.py +++ b/python/grass/temporal/gui_support.py @@ -9,6 +9,7 @@ :authors: Soeren Gebbert """ + from .core import get_available_temporal_mapsets, init_dbif from .factory import dataset_factory import grass.script as gscript diff --git a/python/grass/temporal/mapcalc.py b/python/grass/temporal/mapcalc.py index e23d8cac165..54edeab885d 100644 --- a/python/grass/temporal/mapcalc.py +++ b/python/grass/temporal/mapcalc.py @@ -8,6 +8,7 @@ :authors: Soeren Gebbert """ + import copy from datetime import datetime from multiprocessing import Process diff --git a/python/grass/temporal/open_stds.py b/python/grass/temporal/open_stds.py index 837f1a860a7..83aacc1c819 100644 --- a/python/grass/temporal/open_stds.py +++ b/python/grass/temporal/open_stds.py @@ -17,6 +17,7 @@ :authors: Soeren Gebbert """ + from .core import init_dbif, get_current_mapset, get_tgis_message_interface from .factory import dataset_factory from .abstract_map_dataset import AbstractMapDataset diff --git a/python/grass/temporal/register.py b/python/grass/temporal/register.py index 4d0780ecabf..3804d8c3f72 100644 --- a/python/grass/temporal/register.py +++ b/python/grass/temporal/register.py @@ -18,7 +18,7 @@ """ from datetime import datetime -import grass.script as gscript +import grass.script as gs from .core import get_tgis_message_interface, init_dbif, get_current_mapset from .open_stds import open_old_stds from .abstract_map_dataset import AbstractMapDataset @@ -82,6 +82,7 @@ def register_maps_in_space_time_dataset( start_time_in_file = False end_time_in_file = False semantic_label_in_file = False + overwrite = gs.overwrite() msgr = get_tgis_message_interface() @@ -96,15 +97,13 @@ def register_maps_in_space_time_dataset( increment = str(increment) if maps and file: - msgr.fatal(_("%s= and %s= are mutually exclusive") % ("maps", "file")) + msgr.fatal(_("maps and file are mutually exclusive")) if end and increment: - msgr.fatal(_("%s= and %s= are mutually exclusive") % ("end", "increment")) + msgr.fatal(_("end and increment are mutually exclusive")) if end and interval: - msgr.fatal( - _("%s= and the %s flag are mutually exclusive") % ("end", "interval") - ) + msgr.fatal(_("end and the interval flag are mutually exclusive")) if increment and not start: msgr.fatal(_("The increment option requires the start option")) @@ -113,13 +112,14 @@ def register_maps_in_space_time_dataset( msgr.fatal(_("The interval flag requires the start option")) if end and not start: - msgr.fatal(_("Please specify %s= and %s=") % ("start_time", "end_time")) + msgr.fatal(_("Please specify start_time and end_time")) if not maps and not file: - msgr.fatal(_("Please specify %s= or %s=") % ("maps", "file")) + msgr.fatal(_("Please specify maps or file")) + # We may need the mapset mapset = get_current_mapset() - dbif, connection_state_changed = init_dbif(None) + dbif, connection_state_changed = init_dbif(dbif) # create new stds only in the current mapset # remove all connections to any other mapsets @@ -136,23 +136,17 @@ def register_maps_in_space_time_dataset( dbif.close() msgr.fatal( _( - "Space time %(sp)s dataset <%(name)s> with relative" - " time found, but no relative unit set for %(sp)s " + "Space time {sp} dataset <{name}> with relative" + " time found, but no relative unit set for {sp} " "maps" - ) - % {"name": name, "sp": sp.get_new_map_instance(None).get_type()} + ).format(name=name, sp=sp.get_new_map_instance(None).get_type()) ) maplist = [] # Map names as comma separated string if maps: - if maps.find(",") < 0: - maplist = [ - maps, - ] - else: - maplist = maps.split(",") + maplist = maps.split(",") # Build the map list again with the ids for idx, maplist_item in enumerate(maplist): @@ -244,148 +238,140 @@ def register_maps_in_space_time_dataset( msgr.debug(2, "Gathering map information...") - for count in range(len(maplist)): + for count, row in enumerate(maplist): if count % 50 == 0: msgr.percent(count, num_maps, 1) # Get a new instance of the map type - map = dataset_factory(type, maplist[count]["id"]) + map_object = dataset_factory(type, row["id"]) - if map.map_exists() is not True: + map_object_id = map_object.get_map_id() + map_object_layer = map_object.get_layer() + map_object_type = map_object.get_type() + if not map_object.map_exists(): msgr.fatal( - _("Unable to update %(t)s map <%(id)s>. " "The map does not exist.") - % {"t": map.get_type(), "id": map.get_map_id()} + _("Unable to update {t} map <{mid}>. The map does not exist.").format( + t=map_object_type, mid=map_object_id + ) ) # Use the time data from file - if "start" in maplist[count]: - start = maplist[count]["start"] - if "end" in maplist[count]: - end = maplist[count]["end"] + if "start" in row: + start = row["start"] + if "end" in row: + end = row["end"] # Use the semantic label from file - if "semantic_label" in maplist[count]: - semantic_label = maplist[count]["semantic_label"] + if "semantic_label" in row: + semantic_label = row["semantic_label"] else: semantic_label = None - is_in_db = False + is_in_db = map_object.is_in_db(dbif, mapset) # Put the map into the database of the current mapset - if not map.is_in_db(dbif, mapset): + if not is_in_db: # Break in case no valid time is provided - if (start == "" or start is None) and not map.has_grass_timestamp(): + if (start == "" or start is None) and not map_object.has_grass_timestamp(): dbif.close() - if map.get_layer(): + if map_object_layer: msgr.fatal( _( - "Unable to register %(t)s map <%(id)s> with " - "layer %(l)s. The map has timestamp and " + "Unable to register {t} map <{mid}> with " + "layer {l}. The map has timestamp and " "the start time is not set." + ).format( + t=map_object_type, + mid=map_object_id, + l=map_object_layer, ) - % { - "t": map.get_type(), - "id": map.get_map_id(), - "l": map.get_layer(), - } ) else: msgr.fatal( _( - "Unable to register %(t)s map <%(id)s>. The" + "Unable to register {t} map <{mid}>. The" " map has no timestamp and the start time " "is not set." - ) - % {"t": map.get_type(), "id": map.get_map_id()} + ).format(t=map_object_type, mid=map_object_id) ) if start != "" and start is not None: # We need to check if the time is absolute and the unit was specified time_object = check_datetime_string(start) if isinstance(time_object, datetime) and unit: - msgr.fatal( - _("%(u)s= can only be set for relative time") % {"u": "unit"} - ) + msgr.fatal(_("unit can only be set for relative time")) if not isinstance(time_object, datetime) and not unit: - msgr.fatal( - _("%(u)s= must be set in case of relative time" " stamps") - % {"u": "unit"} - ) + msgr.fatal(_("unit must be set in case of relative time stamps")) if unit: - map.set_time_to_relative() + map_object.set_time_to_relative() else: - map.set_time_to_absolute() + map_object.set_time_to_absolute() else: - is_in_db = True # Check the overwrite flag - if not gscript.overwrite(): - if map.get_layer(): + if not overwrite: + if map_object_layer: msgr.warning( _( "Map is already registered in temporal " - "database. Unable to update %(t)s map " - "<%(id)s> with layer %(l)s. Overwrite flag" + "database. Unable to update {t} map " + "<{mid}> with layer {l}. Overwrite flag" " is not set." + ).format( + t=map_object_type, + mid=map_object_id, + l=str(map_object_layer), ) - % { - "t": map.get_type(), - "id": map.get_map_id(), - "l": str(map.get_layer()), - } ) else: msgr.warning( _( "Map is already registered in temporal " - "database. Unable to update %(t)s map " - "<%(id)s>. Overwrite flag is not set." - ) - % {"t": map.get_type(), "id": map.get_map_id()} + "database. Unable to update {t} map " + "<{mid}>. Overwrite flag is not set." + ).format(t=map_object_type, mid=map_object_id) ) # Simple registration is allowed if name: - map_object_list.append(map) + map_object_list.append(map_object) # Jump to next map continue - # Select information from temporal database - map.select(dbif) + # Reload properties from database + map_object.select(dbif) # Save the datasets that must be updated - datasets = map.get_registered_stds(dbif) + datasets = map_object.get_registered_stds(dbif) if datasets is not None: for dataset in datasets: if dataset != "": datatsets_to_modify[dataset] = dataset - if name and map.get_temporal_type() != sp.get_temporal_type(): + if name and map_object.get_temporal_type() != sp.get_temporal_type(): dbif.close() - if map.get_layer(): + if map_object_layer: msgr.fatal( _( - "Unable to update %(t)s map <%(id)s> " - "with layer %(l)s. The temporal types " + "Unable to update {t} map <{id}> " + "with layer {l}. The temporal types " "are different." + ).format( + t=map_object_type, + mid=map_object_id, + l=map_object_layer, ) - % { - "t": map.get_type(), - "id": map.get_map_id(), - "l": map.get_layer(), - } ) else: msgr.fatal( _( - "Unable to update %(t)s map <%(id)s>. " + "Unable to update {t} map <{mid}>. " "The temporal types are different." - ) - % {"t": map.get_type(), "id": map.get_map_id()} + ).format(t=map_object_type, mid=map_object_id) ) # Load the data from the grass file database - map.load() + map_object.load() # Try to read an existing time stamp from the grass spatial database # in case this map wasn't already registered in the temporal database @@ -393,7 +379,7 @@ def register_maps_in_space_time_dataset( # this map # as method argument or in the input file if not is_in_db and not start: - map.read_timestamp_from_grass() + map_object.read_timestamp_from_grass() # Set the valid time if start: @@ -402,8 +388,8 @@ def register_maps_in_space_time_dataset( if start_time_in_file: count = 1 assign_valid_time_to_map( - ttype=map.get_temporal_type(), - map=map, + ttype=map_object.get_temporal_type(), + map_object=map_object, start=start, end=end, unit=unit, @@ -417,17 +403,17 @@ def register_maps_in_space_time_dataset( # semantic label defined in input file # -> update raster metadata # -> write band identifier to GRASS data base - map.set_semantic_label(semantic_label) + map_object.set_semantic_label(semantic_label) else: # Try to read semantic label from GRASS data base if defined - map.read_semantic_label_from_grass() + map_object.read_semantic_label_from_grass() if is_in_db: # Gather the SQL update statement - statement += map.update_all(dbif=dbif, execute=False) + statement += map_object.update_all(dbif=dbif, execute=False) else: # Gather the SQL insert statement - statement += map.insert(dbif=dbif, execute=False) + statement += map_object.insert(dbif=dbif, execute=False) # Sqlite3 performance is better for huge datasets when committing in # small chunks @@ -439,7 +425,7 @@ def register_maps_in_space_time_dataset( # Store the maps in a list to register in a space time dataset if name: - map_object_list.append(map) + map_object_list.append(map_object) msgr.percent(num_maps, num_maps, 1) @@ -448,13 +434,11 @@ def register_maps_in_space_time_dataset( # Finally Register the maps in the space time dataset if name and map_object_list: - count = 0 num_maps = len(map_object_list) - for map in map_object_list: + for count, map_object in enumerate(map_object_list): if count % 50 == 0: msgr.percent(count, num_maps, 1) - sp.register_map(map=map, dbif=dbif) - count += 1 + sp.register_map(map=map_object, dbif=dbif) # Update the space time tables if name and map_object_list: @@ -465,11 +449,11 @@ def register_maps_in_space_time_dataset( # Update affected datasets if datatsets_to_modify: for dataset in datatsets_to_modify: - if type == "rast" or type == "raster": + if type in ["rast", "raster"]: ds = dataset_factory("strds", dataset) - elif type == "raster_3d" or type == "rast3d" or type == "raster3d": + elif type in ["raster_3d", "rast3d", "raster3d"]: ds = dataset_factory("str3ds", dataset) - elif type == "vect" or type == "vector": + elif type in ["vect", "vector"]: ds = dataset_factory("stvds", dataset) ds.select(dbif) ds.update_from_registered_maps(dbif) @@ -484,7 +468,7 @@ def register_maps_in_space_time_dataset( def assign_valid_time_to_map( - ttype, map, start, end, unit, increment=None, mult=1, interval=False + ttype, map_object, start, end, unit, increment=None, mult=1, interval=False ): """Assign the valid time to a map dataset @@ -513,7 +497,7 @@ def assign_valid_time_to_map( start_time = string_to_datetime(start) if start_time is None: msgr.fatal( - _('Unable to convert string "%s"into a ' "datetime object") % (start) + _('Unable to convert string "{}" into a datetime object').format(start) ) end_time = None @@ -521,7 +505,9 @@ def assign_valid_time_to_map( end_time = string_to_datetime(end) if end_time is None: msgr.fatal( - _('Unable to convert string "%s"into a ' "datetime object") % (end) + _('Unable to convert string "{}" into a datetime object').format( + end + ) ) # Add the increment @@ -534,28 +520,32 @@ def assign_valid_time_to_map( if end_time is None: msgr.fatal(_("Error occurred in increment computation")) - if map.get_layer(): + if map_object.get_layer(): msgr.debug( 1, _( - "Set absolute valid time for map <%(id)s> with " - "layer %(layer)s to %(start)s - %(end)s" - ) - % { - "id": map.get_map_id(), - "layer": map.get_layer(), - "start": str(start_time), - "end": str(end_time), - }, + "Set absolute valid time for map <{id}> with " + "layer {layer} to {start} - {end}" + ).format( + id=map_object.get_map_id(), + layer=map_object.get_layer(), + start=str(start_time), + end=str(end_time), + ), ) else: msgr.debug( 1, - _("Set absolute valid time for map <%s> to %s - %s") - % (map.get_map_id(), str(start_time), str(end_time)), + _( + "Set absolute valid time for map <{mid}> to {start_time} - {end_time}" + ).format( + mid=map_object.get_map_id(), + start_time=str(start_time), + end_time=str(end_time), + ), ) - map.set_absolute_time(start_time, end_time) + map_object.set_absolute_time(start_time, end_time) else: start_time = int(start) end_time = None @@ -568,23 +558,34 @@ def assign_valid_time_to_map( if interval: end_time = start_time + int(increment) - if map.get_layer(): + if map_object.get_layer(): msgr.debug( 1, _( - "Set relative valid time for map <%s> with layer" - " %s to %i - %s with unit %s" - ) - % (map.get_map_id(), map.get_layer(), start_time, str(end_time), unit), + "Set relative valid time for map <{mid}> with layer" + " {layer} to {start} - {end} with unit {unit}" + ).format( + mid=map_object.get_map_id(), + layer=map_object.get_layer(), + start=start_time, + end=str(end_time), + unit=unit, + ), ) else: msgr.debug( 1, - _("Set relative valid time for map <%s> to %i - %s " "with unit %s") - % (map.get_map_id(), start_time, str(end_time), unit), + _( + "Set relative valid time for map <{mid}> to {start} - {end} with unit {unit}" + ).format( + mid=map_object.get_map_id(), + start=start_time, + end=str(end_time), + unit=unit, + ), ) - map.set_relative_time(start_time, end_time, unit) + map_object.set_relative_time(start_time, end_time, unit) ############################################################################## @@ -607,37 +608,34 @@ def register_map_object_list( import grass.pygrass.modules as pymod import copy - dbif, connection_state_changed = init_dbif(dbif) + dbif, connection_state_changed = init_dbif(None) - filename = gscript.tempfile(True) - file = open(filename, "w") - - empty_maps = [] - for map_layer in map_list: - # Read the map data - map_layer.load() - # In case of a empty map continue, do not register empty maps - - if delete_empty: - if type in ["raster", "raster_3d", "rast", "rast3d"]: - if ( - map_layer.metadata.get_min() is None - and map_layer.metadata.get_max() is None - ): - empty_maps.append(map_layer) - continue - if type == "vector": - if map_layer.metadata.get_number_of_primitives() == 0: - empty_maps.append(map_layer) - continue - - start, end = map_layer.get_temporal_extent_as_tuple() - id = map_layer.get_id() - if not end: - end = start - string = "%s|%s|%s\n" % (id, str(start), str(end)) - file.write(string) - file.close() + filename = gs.tempfile(True) + with open(filename, "w") as register_file: + empty_maps = [] + for map_layer in map_list: + # Read the map data + map_layer.load() + # In case of a empty map continue, do not register empty maps + if delete_empty: + if type in ["raster", "raster_3d", "rast", "rast3d"]: + if ( + map_layer.metadata.get_min() is None + and map_layer.metadata.get_max() is None + ): + empty_maps.append(map_layer) + continue + if type == "vector": + if map_layer.metadata.get_number_of_primitives() == 0: + empty_maps.append(map_layer) + continue + + start, end = map_layer.get_temporal_extent_as_tuple() + id = map_layer.get_id() + if not end: + end = start + string = f"{id}|{start}|{end}\n" + register_file.write(string) if output_stds: output_stds_id = output_stds.get_id() @@ -648,22 +646,21 @@ def register_map_object_list( type, output_stds_id, unit=unit, file=filename, dbif=dbif ) - g_remove = pymod.Module("g.remove", flags="f", quiet=True, run_=False, finish_=True) - # Remove empty maps and unregister them from the temporal database + g_remove = pymod.Module("g.remove", flags="f", quiet=True, run_=False, finish_=True) if len(empty_maps) > 0: - for map in empty_maps: + for map_object in empty_maps: mod = copy.deepcopy(g_remove) - if map.get_name(): - if map.get_type() == "raster": - mod(type="raster", name=map.get_name()) - if map.get_type() == "raster3d": - mod(type="raster_3d", name=map.get_name()) - if map.get_type() == "vector": - mod(type="vector", name=map.get_name()) + if map_object.get_name(): + if map_object.get_type() == "raster": + mod(type="raster", name=map_object.get_name()) + if map_object.get_type() == "raster3d": + mod(type="raster_3d", name=map_object.get_name()) + if map_object.get_type() == "vector": + mod(type="vector", name=map_object.get_name()) mod.run() - if map.is_in_db(dbif): - map.delete(dbif) + if map_object.is_in_db(dbif): + map_object.delete(dbif) if connection_state_changed: dbif.close() diff --git a/python/grass/temporal/sampling.py b/python/grass/temporal/sampling.py index 38fdd1aed53..afe84ce57b3 100644 --- a/python/grass/temporal/sampling.py +++ b/python/grass/temporal/sampling.py @@ -16,6 +16,7 @@ :authors: Soeren Gebbert """ + from .core import ( get_current_mapset, get_tgis_message_interface, diff --git a/python/grass/temporal/space_time_datasets.py b/python/grass/temporal/space_time_datasets.py index b91222e4c49..a32cb2d2fac 100644 --- a/python/grass/temporal/space_time_datasets.py +++ b/python/grass/temporal/space_time_datasets.py @@ -10,6 +10,7 @@ """ import getpass +from datetime import datetime from .abstract_map_dataset import AbstractMapDataset from .abstract_space_time_dataset import AbstractSpaceTimeDataset from .base import ( @@ -56,6 +57,8 @@ import grass.script.array as garray +GRASS_TIMESTAMP_FMT = "%a %b %d %H:%M:%S %Y" + ############################################################################### @@ -417,7 +420,16 @@ def load(self): return False # Fill base information - self.base.set_creator(str(getpass.getuser())) + kvp = self.ciface.read_raster_history(self.get_name(), self.get_mapset()) + + if kvp: + self.base.set_creator(kvp["creator"]) + self.base.set_ctime( + datetime.strptime(kvp["creation_time"], GRASS_TIMESTAMP_FMT) + ) + else: + self.base.set_creator(str(getpass.getuser())) + self.base.set_ctime() kvp = self.ciface.read_raster_info(self.get_name(), self.get_mapset()) @@ -820,7 +832,16 @@ def load(self): return False # Fill base information - self.base.set_creator(str(getpass.getuser())) + kvp = self.ciface.read_raster3d_history(self.get_name(), self.get_mapset()) + + if kvp: + self.base.set_creator(kvp["creator"]) + self.base.set_ctime( + datetime.strptime(kvp["creation_time"], GRASS_TIMESTAMP_FMT) + ) + else: + self.base.set_creator(str(getpass.getuser())) + self.base.set_ctime() # Fill spatial extent kvp = self.ciface.read_raster3d_info(self.get_name(), self.get_mapset()) @@ -1157,10 +1178,18 @@ def load(self): return False # Fill base information - self.base.set_creator(str(getpass.getuser())) + kvp = self.ciface.read_vector_history(self.get_name(), self.get_mapset()) - # Get the data from an existing vector map + if kvp: + self.base.set_creator(kvp["creator"]) + self.base.set_ctime( + datetime.strptime(kvp["creation_time"], GRASS_TIMESTAMP_FMT) + ) + else: + self.base.set_creator(str(getpass.getuser())) + self.base.set_ctime() + # Get the data from an existing vector map kvp = self.ciface.read_vector_info(self.get_name(), self.get_mapset()) if kvp: diff --git a/python/grass/temporal/spatial_topology_dataset_connector.py b/python/grass/temporal/spatial_topology_dataset_connector.py index 1427ccd790c..93fe3270c43 100644 --- a/python/grass/temporal/spatial_topology_dataset_connector.py +++ b/python/grass/temporal/spatial_topology_dataset_connector.py @@ -13,6 +13,7 @@ :authors: Soeren Gebbert """ + import copy @@ -67,7 +68,8 @@ class SpatialTopologyDatasetConnector: meet=a@P >>> rlist = tmr.get_spatial_relations() >>> if "COVER" in rlist.keys(): - ... print(rlist["COVER"][0].get_id()) + ... print(rlist["COVER"][0].get_id()) + ... a@P """ @@ -318,8 +320,7 @@ def print_spatial_topology_info(self): """Print information about this class in human readable style""" print( - " +-------------------- Spatial Topology ----------------------------------\ - ----+" + " +-------------------- Spatial Topology --------------------------------------+" # noqa: E501 ) # 0123456789012345678901234567890 if self.equivalent is not None: diff --git a/python/grass/temporal/temporal_algebra.py b/python/grass/temporal/temporal_algebra.py index d5c2dc2a707..acd0b8adba1 100644 --- a/python/grass/temporal/temporal_algebra.py +++ b/python/grass/temporal/temporal_algebra.py @@ -525,9 +525,8 @@ class TemporalAlgebraLexer: # and in relative units in case of relative time. # The end_time() will be represented by null() in case of a time instance. "start_doy": "START_DOY", # Day of year (doy) from the start time [1 - 366] - # Day of week (dow) from the start time [1 - 7], the start of the week is - # Monday == 1 - "start_dow": "START_DOW", + "start_dow": "START_DOW", # Day of week (dow) from the start time [1 - 7], + # the start of the week is Monday == 1 "start_year": "START_YEAR", # The year of the start time [0 - 9999] "start_month": "START_MONTH", # The month of the start time [1 - 12] "start_week": "START_WEEK", # Week of year of the start time [1 - 54] @@ -536,9 +535,8 @@ class TemporalAlgebraLexer: "start_minute": "START_MINUTE", # The minute of the start time [0 - 59] "start_second": "START_SECOND", # The second of the start time [0 - 59] "end_doy": "END_DOY", # Day of year (doy) from the end time [1 - 366] - # Day of week (dow) from the end time [1 - 7], the start of the week is - # Monday == 1 - "end_dow": "END_DOW", + "end_dow": "END_DOW", # Day of week (dow) from the end time [1 - 7], the + # start of the week is Monday == 1 "end_year": "END_YEAR", # The year of the end time [0 - 9999] "end_month": "END_MONTH", # The month of the end time [1 - 12] "end_week": "END_WEEK", # Week of year of the end time [1 - 54] @@ -795,9 +793,10 @@ def __init__( self.dry_run = ( dry_run # Compute the processes and output but Do not start the processes ) - # This dictionary stores all processes, as well as the maps to register and + self.process_chain_dict = ( + {} + ) # This dictionary stores all processes, as well as the maps to register and # remove - self.process_chain_dict = {} self.process_chain_dict["processes"] = ( [] ) # The mapcalc and v.patch module calls @@ -925,8 +924,8 @@ def setup_common_granularity(self, expression, stdstype="strds", lexer=None): if stds.check_temporal_topology() is False: self.msgr.error( _( - "All input space time datasets must have a valid temporal \ - topology." + "All input space time datasets must have a valid temporal " + "topology." ) ) return False @@ -1163,8 +1162,8 @@ def set_temporal_extent_list(self, maplist, topolist=["EQUAL"], temporal="l"): break # Append map to result map list. elif returncode == 1: - # print(map_new.get_id() + " " + str( - # map_new.get_temporal_extent_as_tuple())) + # print(map_new.get_id() + " " + + # str(map_new.get_temporal_extent_as_tuple())) # print(map_new.condition_value) # print(map_new.cmd_list) # resultlist.append(map_new) @@ -1172,8 +1171,7 @@ def set_temporal_extent_list(self, maplist, topolist=["EQUAL"], temporal="l"): # Create r.mapcalc expression string for the operation. # cmdstring = self.build_command_string( - # s_expr_a = map_new, s_expr_b = map_j, - # operator = function) + # s_expr_a = map_new, s_expr_b = map_j, operator = function) # Conditional append of module command. # map_new.cmd_list = cmdstring if returncode == 0: @@ -1440,10 +1438,9 @@ def build_spatio_temporal_topology_list( :return: List of maps from maplistA that fulfil the topological relationships to maplistB specified in topolist. - # Example with two lists of maps - .. code-block:: python + >>> # Example with two lists of maps >>> import grass.temporal as tgis >>> tgis.init(True) >>> l = tgis.TemporalAlgebraParser() @@ -1987,6 +1984,8 @@ def set_granularity(self, maplistA, maplistB, toperator="l", topolist=["EQUAL"]) if newextent is not None: start = newextent.get_start_time() end = newextent.get_end_time() + # print(map_i.get_id() + " - start: " + str(start) + # + " end: " + str(end)) # Track changes in temporal extents of maps. if map_start != start or map_end != end: unchanged = False @@ -2217,9 +2216,9 @@ def build_condition_list(self, tvarexpr, thenlist, topolist=["EQUAL"]): """This function evaluates temporal variable expressions of a conditional expression in two steps. At first it combines stepwise the single conditions by their relations with - LALR. In this process sub condition map lists will be created which will include - information of the underlying single conditions. Important: The temporal - relations between conditions are evaluated by implicit aggregation. + LALR. In this process sub condition map lists will be created which will + include information of the underlying single conditions. Important: The + temporal relations between conditions are evaluated by implicit aggregation. In the second step the aggregated condition map list will be compared with the map list of conclusion statements by the given temporal relation. @@ -3434,8 +3433,7 @@ def p_error(self, t): if t: raise SyntaxError( "syntax error on line %d, position %i token %s near '%s' expression " - "'%s'" - % (t.lineno, t.lexpos, t.type, t.value, self.expression) + "'%s'" % (t.lineno, t.lexpos, t.type, t.value, self.expression) ) else: raise SyntaxError("Unexpected syntax error") diff --git a/python/grass/temporal/temporal_raster3d_algebra.py b/python/grass/temporal/temporal_raster3d_algebra.py index 536f035130c..88d44768b6a 100644 --- a/python/grass/temporal/temporal_raster3d_algebra.py +++ b/python/grass/temporal/temporal_raster3d_algebra.py @@ -10,6 +10,7 @@ :authors: Thomas Leppelt and Soeren Gebbert """ + try: import ply.yacc as yacc except ImportError: diff --git a/python/grass/temporal/testsuite/test_register_function.py b/python/grass/temporal/testsuite/test_register_function.py index fcf1ad756bf..923e4034027 100644 --- a/python/grass/temporal/testsuite/test_register_function.py +++ b/python/grass/temporal/testsuite/test_register_function.py @@ -24,7 +24,7 @@ def setUpClass(cls): os.putenv("GRASS_OVERWRITE", "1") # Use always the current mapset as temporal database cls.runModule("g.gisenv", set="TGIS_USE_CURRENT_MAPSET=1") - tgis.init() + cls.dbif = tgis.init() cls.use_temp_region() cls.runModule("g.region", n=80.0, s=0.0, e=120.0, w=0.0, t=1.0, b=0.0, res=10.0) @@ -72,7 +72,7 @@ def tearDown(self): self.runModule( "t.unregister", type="raster", - maps="register_map_1,register_map_2", + maps="register_map_1,register_map_2,elevation", quiet=True, ) self.runModule( @@ -118,7 +118,8 @@ def test_absolute_time_strds_1(self): def test_absolute_time_strds_2(self): """Test the registration of maps with absolute time in a space time raster dataset. - The timestamps are set using the C-Interface beforehand, so that the register function needs + The timestamps are set using the C-Interface beforehand, + so that the register function needs to read the timetsamp from the map metadata. """ @@ -155,9 +156,10 @@ def test_absolute_time_strds_2(self): def test_absolute_time_strds_3(self): """Test the registration of maps with absolute time in a - space time raster dataset. The timestamps are set via method arguments and with the - c-interface. The timestamps of the method arguments should overwrite the - time stamps set via the C-interface. + space time raster dataset. The timestamps are set via method + arguments and with the c-interface. The timestamps of the + method arguments should overwrite the time stamps set via the + C-interface. """ ciface = tgis.get_tgis_c_library_interface() @@ -187,9 +189,10 @@ def test_absolute_time_strds_3(self): def test_absolute_time_strds_4(self): """Test the registration of maps with absolute time in a - space time raster dataset. The timestamps are set via method arguments and with the - c-interface. The timestamps of the method arguments should overwrite the - time stamps set via the C-interface. The C-interface sets relative time stamps. + space time raster dataset. The timestamps are set via method + arguments and with the c-interface. The timestamps of the method + arguments should overwrite the time stamps set via the C-interface. + The C-interface sets relative time stamps. """ ciface = tgis.get_tgis_c_library_interface() @@ -258,7 +261,8 @@ def test_absolute_time_1(self): def test_absolute_time_2(self): """Test the registration of maps with absolute time - using register_maps_in_space_time_dataset() and register_map_object_list() with empty map deletion + using register_maps_in_space_time_dataset() and + register_map_object_list() with empty map deletion """ tgis.register_maps_in_space_time_dataset( type="raster", @@ -300,9 +304,58 @@ def test_absolute_time_2(self): map_3 = tgis.VectorDataset("register_map_null@" + tgis.get_current_mapset()) self.assertEqual(map_3.map_exists(), False) + def test_history_raster(self): + """Test that raster maps are registered with the history + (creator and creation time) of the raster map itself (and from a + different mapset (PERMANENT) + """ + tgis.register_maps_in_space_time_dataset( + type="raster", + name=None, + maps="elevation@PERMANENT", + start="2001-01-01 10:30:01", + increment="1 year", + interval=True, + dbif=self.dbif, + ) + + map_1 = tgis.RasterDataset("elevation@PERMANENT") + map_1.select(self.dbif, tgis.get_current_mapset()) + # Test that creation time of the map is used + self.assertEqual( + map_1.base.get_ctime(), datetime.datetime(2006, 11, 7, 1, 9, 51) + ) + # Test that registered creator of the map is not the current user + self.assertEqual(map_1.base.get_creator(), "helena") + + def test_history_vector(self): + """Test that vector maps are registered with the history (creator + and creation time) of the vector map itself (and from a + different mapset (PERMANENT) + """ + tgis.register_maps_in_space_time_dataset( + type="vector", + name=None, + maps="lakes@PERMANENT", + start="2001-01-01 10:30:01", + increment="1 year", + interval=True, + dbif=self.dbif, + ) + + map_1 = tgis.VectorDataset("lakes@PERMANENT") + map_1.select(self.dbif, tgis.get_current_mapset()) + # Test that creation time of the map is used + self.assertEqual( + map_1.base.get_ctime(), datetime.datetime(2006, 11, 7, 19, 48, 8) + ) + # Test that registered creator of the map is not the current user + self.assertTrue(map_1.base.get_creator(), "helena") + def test_absolute_time_3(self): """Test the registration of maps with absolute time. - The timestamps are set using the C-Interface beforehand, so that the register function needs + The timestamps are set using the C-Interface beforehand, + so that the register function needs to read the timetsamp from the map metadata. """ @@ -365,8 +418,8 @@ def test_relative_time_strds_1(self): def test_relative_time_strds_2(self): """Test the registration of maps with relative time in a - space time raster dataset. The timetsamps are set for the maps using the - C-interface before registration. + space time raster dataset. The timestamps are set for the maps + using the C-interface before registration. """ ciface = tgis.get_tgis_c_library_interface() ciface.write_raster_timestamp( @@ -459,8 +512,8 @@ def test_relative_time_2(self): self.assertEqual(unit, "seconds") def test_relative_time_3(self): - """Test the registration of maps with relative time. The timetsamps are set beforehand using - the C-interface. + """Test the registration of maps with relative time. The + timestamps are set beforehand using the C-interface. """ ciface = tgis.get_tgis_c_library_interface() ciface.write_raster_timestamp( @@ -619,7 +672,8 @@ def test_absolute_time_stvds_1(self): def test_absolute_time_stvds_2(self): """Test the registration of maps with absolute time in a space time raster dataset. - The timestamps are set using the C-Interface beforehand, so that the register function needs + The timestamps are set using the C-Interface beforehand, + so that the register function needs to read the timetsamp from the map metadata. """ @@ -656,9 +710,9 @@ def test_absolute_time_stvds_2(self): def test_absolute_time_stvds_3(self): """Test the registration of maps with absolute time in a - space time raster dataset. The timestamps are set via method arguments and with the - c-interface. The timestamps of the method arguments should overwrite the - time stamps set via the C-interface. + space time raster dataset. The timestamps are set via method + arguments and with the C-interface. The timestamps of the method + arguments should overwrite the time stamps set via the C-interface. """ ciface = tgis.get_tgis_c_library_interface() @@ -687,7 +741,8 @@ def test_absolute_time_stvds_3(self): self.assertEqual(end, datetime.datetime(2001, 2, 2)) def test_absolute_time_1(self): - """Register vector maps in the temporal database and in addition in a stvds using the object method + """Register vector maps in the temporal database and in addition + in a stvds using the object method :return: """ diff --git a/python/grass/temporal/testsuite/unittests_temporal_raster3d_algebra.py b/python/grass/temporal/testsuite/unittests_temporal_raster3d_algebra.py index fd011a55d53..1ed80cd1316 100644 --- a/python/grass/temporal/testsuite/unittests_temporal_raster3d_algebra.py +++ b/python/grass/temporal/testsuite/unittests_temporal_raster3d_algebra.py @@ -7,7 +7,6 @@ :authors: Soeren Gebbert and Thomas Leppelt """ - import grass.script import grass.temporal as tgis from grass.gunittest.case import TestCase diff --git a/python/grass/temporal/unit_tests.py b/python/grass/temporal/unit_tests.py index fd412ee6cf2..8083680ef3f 100644 --- a/python/grass/temporal/unit_tests.py +++ b/python/grass/temporal/unit_tests.py @@ -8,6 +8,7 @@ :authors: Soeren Gebbert """ + import copy from datetime import datetime import grass.script.core as core diff --git a/raster/r.basins.fill/testsuite/testrbf.py b/raster/r.basins.fill/testsuite/testrbf.py index fdbcd4b548f..b9937b05689 100644 --- a/raster/r.basins.fill/testsuite/testrbf.py +++ b/raster/r.basins.fill/testsuite/testrbf.py @@ -8,6 +8,7 @@ License (>=v2). Read the file COPYING that comes with GRASS for details. """ + import unittest from grass.gunittest.case import TestCase diff --git a/raster/r.external/main.c b/raster/r.external/main.c index c3ee9b8daf2..baf5b7c8ea0 100644 --- a/raster/r.external/main.c +++ b/raster/r.external/main.c @@ -98,10 +98,9 @@ int main(int argc, char *argv[]) flag.o = G_define_flag(); flag.o->key = 'o'; - flag.o->label = - _("Override projection check (use current location's projection)"); - flag.o->description = _( - "Assume that the dataset has same projection as the current location"); + flag.o->label = _("Override projection check (use current project's CRS)"); + flag.o->description = _("Assume that the dataset has the same coordinate " + "reference system as the current project"); flag.j = G_define_flag(); flag.j->key = 'j'; diff --git a/raster/r.external/proj.c b/raster/r.external/proj.c index 0e38578fe06..7b9b8043133 100644 --- a/raster/r.external/proj.c +++ b/raster/r.external/proj.c @@ -130,15 +130,16 @@ void check_projection(struct Cell_head *cellhd, GDALDatasetH hDS, char *outloc, if (outloc != NULL) { /* do not create a xy location if an existing SRS was unreadable */ if (proj_trouble == 2) { - G_fatal_error(_("Unable to convert input map projection to GRASS " - "format; cannot create new location.")); + G_fatal_error( + _("Unable to convert input map coordinate reference " + "system to GRASS format; cannot create new project.")); } else { if (0 != G_make_location_crs(outloc, cellhd, proj_info, proj_units, srid, wkt)) { - G_fatal_error(_("Unable to create new location <%s>"), outloc); + G_fatal_error(_("Unable to create new project <%s>"), outloc); } - G_message(_("Location <%s> created"), outloc); + G_message(_("Project <%s> created"), outloc); G_unset_window(); /* new location, projection, and window */ G_get_window(cellhd); @@ -195,14 +196,15 @@ void check_projection(struct Cell_head *cellhd, GDALDatasetH hDS, char *outloc, proj_info, proj_units)) != 1) { int i_value; - strcpy(error_msg, _("Projection of dataset does not" - " appear to match current location.\n\n")); + strcpy(error_msg, + _("Coordinate reference system of dataset does not" + " appear to match current project.\n\n")); /* TODO: output this info sorted by key: */ if (loc_wind.proj != cellhd->proj || err != -2) { /* error in proj_info */ if (loc_proj_info != NULL) { - strcat(error_msg, _("Location PROJ_INFO is:\n")); + strcat(error_msg, _("Project PROJ_INFO is:\n")); for (i_value = 0; i_value < loc_proj_info->nitems; i_value++) sprintf(error_msg + strlen(error_msg), "%s: %s\n", @@ -211,22 +213,22 @@ void check_projection(struct Cell_head *cellhd, GDALDatasetH hDS, char *outloc, strcat(error_msg, "\n"); } else { - strcat(error_msg, _("Location PROJ_INFO is:\n")); + strcat(error_msg, _("Project PROJ_INFO is:\n")); if (loc_wind.proj == PROJECTION_XY) sprintf(error_msg + strlen(error_msg), - "Location proj = %d (unreferenced/unknown)\n", + "Project proj = %d (unreferenced/unknown)\n", loc_wind.proj); else if (loc_wind.proj == PROJECTION_LL) sprintf(error_msg + strlen(error_msg), - "Location proj = %d (lat/long)\n", + "Project proj = %d (lat/long)\n", loc_wind.proj); else if (loc_wind.proj == PROJECTION_UTM) sprintf(error_msg + strlen(error_msg), - "Location proj = %d (UTM), zone = %d\n", + "Project proj = %d (UTM), zone = %d\n", loc_wind.proj, cellhd->zone); else sprintf(error_msg + strlen(error_msg), - "Location proj = %d (unknown), zone = %d\n", + "Project proj = %d (unknown), zone = %d\n", loc_wind.proj, cellhd->zone); } @@ -300,7 +302,7 @@ void check_projection(struct Cell_head *cellhd, GDALDatasetH hDS, char *outloc, else { /* error in proj_units */ if (loc_proj_units != NULL) { - strcat(error_msg, "Location PROJ_UNITS is:\n"); + strcat(error_msg, "Project PROJ_UNITS is:\n"); for (i_value = 0; i_value < loc_proj_units->nitems; i_value++) sprintf(error_msg + strlen(error_msg), "%s: %s\n", @@ -318,13 +320,14 @@ void check_projection(struct Cell_head *cellhd, GDALDatasetH hDS, char *outloc, } } if (!check_only) { - strcat(error_msg, _("\nIn case of no significant differences " - "in the projection definitions," - " use the -o flag to ignore them and use" - " current location definition.\n")); - strcat(error_msg, _("Consider generating a new location from " + strcat(error_msg, + _("\nIn case of no significant differences " + "in the coordinate reference system definitions," + " use the -o flag to ignore them and use" + " current project definition.\n")); + strcat(error_msg, _("Consider generating a new project from " "the input dataset using " - "the 'location' parameter.\n")); + "the 'project' parameter.\n")); } if (check_only) @@ -342,8 +345,8 @@ void check_projection(struct Cell_head *cellhd, GDALDatasetH hDS, char *outloc, msg_fn = G_message; else msg_fn = G_verbose_message; - msg_fn(_("Projection of input dataset and current location " - "appear to match")); + msg_fn(_("Coordinate reference system of input dataset and current " + "project appear to match")); if (check_only) { GDALClose(hDS); exit(EXIT_SUCCESS); diff --git a/raster/r.external/testsuite/test_r_external.py b/raster/r.external/testsuite/test_r_external.py index 839a724a5b7..a22498eb773 100644 --- a/raster/r.external/testsuite/test_r_external.py +++ b/raster/r.external/testsuite/test_r_external.py @@ -2,6 +2,7 @@ @author Markus Neteler """ + from grass.gunittest.case import TestCase diff --git a/raster/r.gwflow/testsuite/validation_7x7_grid.py b/raster/r.gwflow/testsuite/validation_7x7_grid.py index f4595eaf421..6d632092300 100644 --- a/raster/r.gwflow/testsuite/validation_7x7_grid.py +++ b/raster/r.gwflow/testsuite/validation_7x7_grid.py @@ -7,6 +7,7 @@ @author Soeren Gebbert """ + from grass.gunittest.case import TestCase diff --git a/raster/r.horizon/testsuite/test_r_horizon.py b/raster/r.horizon/testsuite/test_r_horizon.py index 4118c1cb203..846988401dc 100644 --- a/raster/r.horizon/testsuite/test_r_horizon.py +++ b/raster/r.horizon/testsuite/test_r_horizon.py @@ -11,6 +11,7 @@ License (>=v2). Read the file COPYING that comes with GRASS for details. """ + import json from grass.gunittest.case import TestCase diff --git a/raster/r.in.ascii/testsuite/test_r_in_ascii.py b/raster/r.in.ascii/testsuite/test_r_in_ascii.py index 151b6ff7978..20e0c2a8f9e 100644 --- a/raster/r.in.ascii/testsuite/test_r_in_ascii.py +++ b/raster/r.in.ascii/testsuite/test_r_in_ascii.py @@ -8,6 +8,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 from grass.script.core import read_command diff --git a/raster/r.in.gdal/testsuite/test_r_in_gdal.py b/raster/r.in.gdal/testsuite/test_r_in_gdal.py index 3e3f6bcd554..2b8bee0982a 100644 --- a/raster/r.in.gdal/testsuite/test_r_in_gdal.py +++ b/raster/r.in.gdal/testsuite/test_r_in_gdal.py @@ -2,6 +2,7 @@ @author Soeren Gebbert """ + from grass.gunittest.case import TestCase diff --git a/raster/r.in.lidar/main.c b/raster/r.in.lidar/main.c index 9c7d4cdad8f..68d3a0fcd1b 100644 --- a/raster/r.in.lidar/main.c +++ b/raster/r.in.lidar/main.c @@ -300,9 +300,10 @@ int main(int argc, char *argv[]) over_flag = G_define_flag(); over_flag->key = 'o'; over_flag->label = - _("Override projection check (use current location's projection)"); - over_flag->description = _( - "Assume that the dataset has same projection as the current location"); + _("Override projection check (use current project's CRS)"); + over_flag->description = + _("Assume that the dataset has the same coordinate reference system as " + "the current project"); scan_flag = G_define_flag(); scan_flag->key = 's'; diff --git a/raster/r.in.lidar/projection.c b/raster/r.in.lidar/projection.c index faf123db11b..a3c4ff057b2 100644 --- a/raster/r.in.lidar/projection.c +++ b/raster/r.in.lidar/projection.c @@ -29,8 +29,8 @@ void projection_mismatch_report(struct Cell_head cellhd, int i_value; char error_msg[8192]; - strcpy(error_msg, _("Projection of dataset does not" - " appear to match current location.\n\n")); + strcpy(error_msg, _("Coordinate reference system of dataset does not" + " appear to match current project.\n\n")); /* TODO: output this info sorted by key: */ if (loc_wind.proj != cellhd.proj || err != -2) { @@ -86,12 +86,12 @@ void projection_mismatch_report(struct Cell_head cellhd, } } sprintf(error_msg + strlen(error_msg), - _("\nIn case of no significant differences in the projection " - "definitions," + _("\nIn case of no significant differences" + " in the coordinate reference system definitions," " use the -o flag to ignore them and use" - " current location definition.\n")); + " current project definition.\n")); strcat(error_msg, - _("Consider generating a new location with 'location' parameter" + _("Consider generating a new project with 'project' parameter" " from input data set.\n")); G_fatal_error("%s", error_msg); } @@ -132,7 +132,8 @@ void projection_check_wkt(struct Cell_head cellhd, struct Cell_head loc_wind, loc_proj_units, proj_info, proj_units, err); } else if (verbose) { - G_message(_("Projection of input dataset and current location " - "appear to match")); + G_message(_( + "Coordinate reference system of input dataset and current project " + "appear to match")); } } diff --git a/raster/r.in.lidar/r.in.lidar.html b/raster/r.in.lidar/r.in.lidar.html index ff32c8291c2..6cff187c089 100644 --- a/raster/r.in.lidar/r.in.lidar.html +++ b/raster/r.in.lidar/r.in.lidar.html @@ -464,7 +464,7 @@

Serpent Mound dataset

# 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" location=Serpent_Mound +v.in.lidar -i input="Serpent Mound Model LAS Data.las" project=Serpent_Mound # quit and restart GRASS in the newly created project "Serpent_Mound" diff --git a/raster/r.in.pdal/main.cpp b/raster/r.in.pdal/main.cpp index 63ab5c19f03..093b17258e1 100644 --- a/raster/r.in.pdal/main.cpp +++ b/raster/r.in.pdal/main.cpp @@ -248,10 +248,10 @@ int main(int argc, char *argv[]) reproject_flag->key = 'w'; reproject_flag->label = - _("Reproject to location's coordinate system if needed"); + _("Reproject to project's coordinate system if needed"); reproject_flag->description = _("Reprojects input dataset to the coordinate system of" - " the GRASS location (by default only datasets with the" + " the GRASS project (by default only datasets with" " matching coordinate system can be imported"); reproject_flag->guisection = _("Projection"); @@ -393,9 +393,10 @@ int main(int argc, char *argv[]) over_flag->key = 'o'; over_flag->label = - _("Override projection check (use current location's projection)"); - over_flag->description = _( - "Assume that the dataset has same projection as the current location"); + _("Override projection check (use current project's CRS)"); + over_flag->description = + _("Assume that the dataset has the same coordinate reference system as " + "the current project"); over_flag->guisection = _("Projection"); Flag *base_rast_res_flag = G_define_flag(); @@ -731,7 +732,7 @@ int main(int argc, char *argv[]) // we reproject when requested regardless of the input projection if (reproject_flag->answer) { - G_message(_("Reprojecting the input to the location projection")); + G_message(_("Reprojecting the input to the project's CRS")); char *proj_wkt = location_projection_as_wkt(false); pdal::Options o4; @@ -781,8 +782,8 @@ int main(int argc, char *argv[]) // getting projection is possible only after prepare if (over_flag->answer) { G_important_message(_("Overriding projection check and assuming" - " that the projection of input matches" - " the location projection")); + " that the CRS of input matches" + " the project's CRS")); } else if (!reproject_flag->answer) { pdal::SpatialReference spatial_reference = diff --git a/raster/r.in.pdal/projection.c b/raster/r.in.pdal/projection.c index 244ee323afb..25e6da8e25e 100644 --- a/raster/r.in.pdal/projection.c +++ b/raster/r.in.pdal/projection.c @@ -27,8 +27,8 @@ void projection_mismatch_report(struct Cell_head cellhd, int i_value; char error_msg[8192]; - strcpy(error_msg, _("Projection of dataset does not" - " appear to match current location.\n\n")); + strcpy(error_msg, _("Coordinate reference system of dataset does not" + " appear to match current project.\n\n")); /* TODO: output this info sorted by key: */ if (loc_wind.proj != cellhd.proj || err != -2) { @@ -84,12 +84,12 @@ void projection_mismatch_report(struct Cell_head cellhd, } } sprintf(error_msg + strlen(error_msg), - _("\nIn case of no significant differences in the projection " - "definitions," + _("\nIn case of no significant differences" + " in the coordinate reference system definitions," " use the -o flag to ignore them and use" - " current location definition.\n")); + " current project definition.\n")); strcat(error_msg, - _("Consider generating a new location with 'location' parameter" + _("Consider generating a new project with 'project' parameter" " from input data set.\n")); G_fatal_error("%s", error_msg); } @@ -131,8 +131,8 @@ void projection_check_wkt(struct Cell_head cellhd, struct Cell_head loc_wind, } else { if (verbose) { - G_message(_("Projection of input dataset and current location " - "appear to match")); + G_message(_("Coordinate reference system of input dataset and " + "current project appear to match")); } } } diff --git a/raster/r.in.pdal/r.in.pdal.html b/raster/r.in.pdal/r.in.pdal.html index 3dd9d068ac7..92cd25205e8 100644 --- a/raster/r.in.pdal/r.in.pdal.html +++ b/raster/r.in.pdal/r.in.pdal.html @@ -522,7 +522,7 @@

Serpent Mound dataset

# 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" location=Serpent_Mound +v.in.lidar -i input="Serpent Mound Model LAS Data.las" project=Serpent_Mound # quit and restart GRASS in the newly created project "Serpent_Mound" diff --git a/raster/r.info/testsuite/test_r_info.py b/raster/r.info/testsuite/test_r_info.py index 05953ae56b4..a4d554232dd 100644 --- a/raster/r.info/testsuite/test_r_info.py +++ b/raster/r.info/testsuite/test_r_info.py @@ -8,6 +8,7 @@ License (>=v2). Read the file COPYING that comes with GRASS for details. """ + from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule diff --git a/raster/r.random.cells/testsuite/test_random_cells.py b/raster/r.random.cells/testsuite/test_random_cells.py index 9faeb3b0135..0d4ca4379e5 100644 --- a/raster/r.random.cells/testsuite/test_random_cells.py +++ b/raster/r.random.cells/testsuite/test_random_cells.py @@ -12,7 +12,6 @@ for details. """ - from grass.gunittest.case import TestCase from grass.gunittest.main import test diff --git a/raster/r.random/testsuite/testrandom.py b/raster/r.random/testsuite/testrandom.py index 791aebfa669..e2bc2e413a3 100644 --- a/raster/r.random/testsuite/testrandom.py +++ b/raster/r.random/testsuite/testrandom.py @@ -8,6 +8,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 from grass.gunittest.gmodules import SimpleModule diff --git a/raster/r.reclass/testsuite/test_r_reclass.py b/raster/r.reclass/testsuite/test_r_reclass.py index 71d63d1c4bb..3f4ecfdf6a3 100644 --- a/raster/r.reclass/testsuite/test_r_reclass.py +++ b/raster/r.reclass/testsuite/test_r_reclass.py @@ -8,6 +8,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 from grass.gunittest.gmodules import SimpleModule diff --git a/raster/r.series.interp/testsuite/interp_test.py b/raster/r.series.interp/testsuite/interp_test.py index 017912172be..e4cadd251cd 100644 --- a/raster/r.series.interp/testsuite/interp_test.py +++ b/raster/r.series.interp/testsuite/interp_test.py @@ -1,6 +1,7 @@ """Test of r.series.interp @author Soeren Gebbert """ + from grass.gunittest.case import TestCase diff --git a/raster/r.support/testsuite/test_r_support.py b/raster/r.support/testsuite/test_r_support.py index f7b7dd9e917..9a024c4a723 100644 --- a/raster/r.support/testsuite/test_r_support.py +++ b/raster/r.support/testsuite/test_r_support.py @@ -8,6 +8,7 @@ Read the file COPYING that comes with GRASS for details """ + import random import string diff --git a/raster/r.texture/testsuite/test_texture.py b/raster/r.texture/testsuite/test_texture.py index af24229e9ba..378eaaeabb5 100644 --- a/raster/r.texture/testsuite/test_texture.py +++ b/raster/r.texture/testsuite/test_texture.py @@ -8,6 +8,7 @@ License (>=v2). Read the file COPYING that comes with GRASS for details. """ + from grass.gunittest.case import TestCase diff --git a/raster/r.tile/testsuite/testrt.py b/raster/r.tile/testsuite/testrt.py index c1da1d10cdf..6cb5a91c296 100644 --- a/raster/r.tile/testsuite/testrt.py +++ b/raster/r.tile/testsuite/testrt.py @@ -8,6 +8,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/raster/r.to.vect/testsuite/test_r_to_vect.py b/raster/r.to.vect/testsuite/test_r_to_vect.py index 57de45e4b77..7ba284d0261 100644 --- a/raster/r.to.vect/testsuite/test_r_to_vect.py +++ b/raster/r.to.vect/testsuite/test_r_to_vect.py @@ -8,6 +8,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/raster/r.univar/testsuite/test_r_univar.py b/raster/r.univar/testsuite/test_r_univar.py index 368ff3faae0..c03aea5dcfa 100644 --- a/raster/r.univar/testsuite/test_r_univar.py +++ b/raster/r.univar/testsuite/test_r_univar.py @@ -2,6 +2,7 @@ @author Soeren Gebbert """ + from grass.gunittest.case import TestCase diff --git a/raster/r.what/testsuite/testrw.py b/raster/r.what/testsuite/testrw.py index f63ae5dfe30..8184f534720 100644 --- a/raster/r.what/testsuite/testrw.py +++ b/raster/r.what/testsuite/testrw.py @@ -8,6 +8,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 from grass.gunittest.gmodules import SimpleModule diff --git a/raster3d/r3.flow/testsuite/r3flow_test.py b/raster3d/r3.flow/testsuite/r3flow_test.py index 8385f10559e..f7d013f38bd 100644 --- a/raster3d/r3.flow/testsuite/r3flow_test.py +++ b/raster3d/r3.flow/testsuite/r3flow_test.py @@ -3,6 +3,7 @@ @author Anna Petrasova """ + import os from grass.gunittest.case import TestCase from grass.gunittest.main import test diff --git a/raster3d/r3.gradient/testsuite/r3gradient_test.py b/raster3d/r3.gradient/testsuite/r3gradient_test.py index 9d0814dac78..d41e0a74baa 100644 --- a/raster3d/r3.gradient/testsuite/r3gradient_test.py +++ b/raster3d/r3.gradient/testsuite/r3gradient_test.py @@ -3,6 +3,7 @@ @author Anna Petrasova """ + from grass.gunittest.case import TestCase from grass.gunittest.main import test diff --git a/raster3d/r3.in.lidar/main.c b/raster3d/r3.in.lidar/main.c index cd622a6098f..6170a3f50ba 100644 --- a/raster3d/r3.in.lidar/main.c +++ b/raster3d/r3.in.lidar/main.c @@ -287,9 +287,10 @@ int main(int argc, char *argv[]) over_flag = G_define_flag(); over_flag->key = 'o'; over_flag->label = - _("Override projection check (use current location's projection)"); - over_flag->description = _( - "Assume that the dataset has same projection as the current location"); + _("Override projection check (use current projects's CRS)"); + over_flag->description = + _("Assume that the dataset has the same coordinate " + "reference system as the current project"); print_flag = G_define_flag(); print_flag->key = 'p'; diff --git a/raster3d/r3.in.lidar/projection.c b/raster3d/r3.in.lidar/projection.c index 40eb4b6e289..a3ebea5d7f4 100644 --- a/raster3d/r3.in.lidar/projection.c +++ b/raster3d/r3.in.lidar/projection.c @@ -30,8 +30,8 @@ void projection_mismatch_report(struct Cell_head cellhd, int i_value; char error_msg[8192]; - strcpy(error_msg, _("Projection of dataset does not" - " appear to match current location.\n\n")); + strcpy(error_msg, _("Coordinate reference system of dataset does not" + " appear to match current project.\n\n")); /* TODO: output this info sorted by key: */ if (loc_wind.proj != cellhd.proj || err != -2) { @@ -87,12 +87,12 @@ void projection_mismatch_report(struct Cell_head cellhd, } } sprintf(error_msg + strlen(error_msg), - _("\nIn case of no significant differences in the projection " - "definitions," + _("\nIn case of no significant differences in the" + " coordinate reference system definitions," " use the -o flag to ignore them and use" - " current location definition.\n")); + " current project definition.\n")); strcat(error_msg, - _("Consider generating a new location with 'location' parameter" + _("Consider generating a new project with 'project' parameter" " from input data set.\n")); G_fatal_error("%s", error_msg); } @@ -133,7 +133,8 @@ void projection_check_wkt(struct Cell_head cellhd, struct Cell_head loc_wind, loc_proj_units, proj_info, proj_units, err); } else if (verbose) { - G_message(_("Projection of input dataset and current location " - "appear to match")); + G_message(_( + "Coordinate reference system of input dataset and current project " + "appear to match")); } } diff --git a/scripts/r.fillnulls/testsuite/test_r_fillnulls.py b/scripts/r.fillnulls/testsuite/test_r_fillnulls.py index 8cfed138f4e..975fd5330f2 100644 --- a/scripts/r.fillnulls/testsuite/test_r_fillnulls.py +++ b/scripts/r.fillnulls/testsuite/test_r_fillnulls.py @@ -3,6 +3,7 @@ @author: Sanjeet Bhatti """ + import os from grass.gunittest.case import TestCase diff --git a/scripts/r.mapcalc.simple/testsuite/test_rmapcalcsimple.py b/scripts/r.mapcalc.simple/testsuite/test_rmapcalcsimple.py index 70da5f40b11..d400ccc7980 100644 --- a/scripts/r.mapcalc.simple/testsuite/test_rmapcalcsimple.py +++ b/scripts/r.mapcalc.simple/testsuite/test_rmapcalcsimple.py @@ -8,6 +8,7 @@ License (>=v2). Read the file COPYING that comes with GRASS for details. """ + from grass.gunittest.case import TestCase diff --git a/scripts/r.reclass.area/testsuite/testrra.py b/scripts/r.reclass.area/testsuite/testrra.py index 6c7822c0f6c..e6e2300875b 100644 --- a/scripts/r.reclass.area/testsuite/testrra.py +++ b/scripts/r.reclass.area/testsuite/testrra.py @@ -8,6 +8,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/scripts/r.unpack/r.unpack.py b/scripts/r.unpack/r.unpack.py index f195ebc151f..a19123b28f0 100644 --- a/scripts/r.unpack/r.unpack.py +++ b/scripts/r.unpack/r.unpack.py @@ -30,8 +30,8 @@ # %end # %flag # % key: o -# % label: Override projection check (use current location's projection) -# % description: Assume that the dataset has same projection as the current location +# % label: Override projection check (use current projects's CRS) +# % description: Assume that the dataset has same coordinate reference system as the current project # % guisection: Output settings # %end # %flag @@ -140,9 +140,7 @@ def main(): # check projection compatibility in a rather crappy way if flags["o"]: - grass.warning( - _("Overriding projection check (using current location's projection).") - ) + grass.warning(_("Overriding projection check (using current project's CRS).")) else: diff_result_1 = diff_result_2 = None @@ -156,7 +154,7 @@ def main(): grass.fatal( _( "PROJ_INFO file is missing, unpack raster map in XY " - "(unprojected) location." + "(unprojected) project." ) ) skip_projection_check = True # XY location @@ -180,22 +178,23 @@ def main(): grass.warning( _( "Difference between PROJ_INFO file of packed map " - "and of current location:\n{diff}" + "and of current project:\n{diff}" ).format(diff="".join(diff_result_1)) ) if diff_result_2: grass.warning( _( "Difference between PROJ_UNITS file of packed map " - "and of current location:\n{diff}" + "and of current project:\n{diff}" ).format(diff="".join(diff_result_2)) ) grass.fatal( _( - "Projection of dataset does not appear to match current " - "location. In case of no significant differences in the " - "projection definitions, use the -o flag to ignore them and " - "use current location definition." + "Coordinate reference system of dataset does" + " not appear to match current project." + " In case of no significant differences in the CRS definitions," + " use the -o flag to ignore them and use" + " current project definition." ) ) diff --git a/scripts/v.db.addcolumn/v.db.addcolumn.py b/scripts/v.db.addcolumn/v.db.addcolumn.py index 2ce1a3eedc2..8d8e322118c 100755 --- a/scripts/v.db.addcolumn/v.db.addcolumn.py +++ b/scripts/v.db.addcolumn/v.db.addcolumn.py @@ -42,6 +42,8 @@ import atexit import os +import re + from grass.exceptions import CalledModuleError import grass.script as grass @@ -98,17 +100,26 @@ def main(): column_existing = grass.vector_columns(map, int(layer)).keys() add_str = "BEGIN TRANSACTION\n" + pattern = re.compile(r"\s+") for col in columns: if not col: grass.fatal(_("There is an empty column. Did you leave a trailing comma?")) - col_name = col.split(" ")[0].strip() + whitespace = re.search(pattern, col) + if not whitespace: + grass.fatal( + _( + "Incorrect new column(s) format, use" + " <'name type [,name type, ...]'> format, please." + ) + ) + col_name, col_type = col.split(whitespace.group(0), 1) if col_name in column_existing: grass.error( _("Column <{}> is already in the table. Skipping.").format(col_name) ) continue grass.verbose(_("Adding column <{}> to the table").format(col_name)) - add_str += f"ALTER TABLE {table} ADD COLUMN {col};\n" + add_str += f'ALTER TABLE {table} ADD COLUMN "{col_name}" {col_type};\n' add_str += "END TRANSACTION" sql_file = grass.tempfile() rm_files.append(sql_file) diff --git a/scripts/v.db.dropcolumn/v.db.dropcolumn.py b/scripts/v.db.dropcolumn/v.db.dropcolumn.py index 937f573006c..b408c83395a 100755 --- a/scripts/v.db.dropcolumn/v.db.dropcolumn.py +++ b/scripts/v.db.dropcolumn/v.db.dropcolumn.py @@ -96,11 +96,11 @@ def main(): # see db_sqltype_name() for type names if f[1] == "CHARACTER": # preserve field length for sql type "CHARACTER" - coltypes.append("%s %s(%s)" % (f[0], f[1], f[2])) + coltypes.append(f'"{f[0]}" {f[1]}({f[2]})') else: - coltypes.append("%s %s" % (f[0], f[1])) + coltypes.append(f'"{f[0]}" {f[1]}') - colnames = ", ".join(colnames) + colnames = ", ".join([f'"{col}"' for col in colnames]) coltypes = ", ".join(coltypes) cmds = [ @@ -119,7 +119,7 @@ def main(): table=table, coldef=coltypes, colnames=colnames, keycol=keycol ) else: - sql = "ALTER TABLE %s DROP COLUMN %s" % (table, column) + sql = f'ALTER TABLE {table} DROP COLUMN "{column}"' try: grass.write_command( diff --git a/scripts/v.db.renamecolumn/v.db.renamecolumn.py b/scripts/v.db.renamecolumn/v.db.renamecolumn.py index 0309cff406a..87f1850a57c 100755 --- a/scripts/v.db.renamecolumn/v.db.renamecolumn.py +++ b/scripts/v.db.renamecolumn/v.db.renamecolumn.py @@ -103,12 +103,12 @@ def main(): # some tricks if driver in ["sqlite", "dbf"]: if oldcoltype.upper() == "CHARACTER": - colspec = "%s varchar(%s)" % (newcol, oldcollength) + colspec = f"{newcol} varchar({oldcollength})" else: - colspec = "%s %s" % (newcol, oldcoltype) + colspec = f"{newcol} {oldcoltype}" grass.run_command("v.db.addcolumn", map=map, layer=layer, column=colspec) - sql = "UPDATE %s SET %s=%s" % (table, newcol, oldcol) + sql = f'UPDATE {table} SET "{newcol}"="{oldcol}"' grass.write_command( "db.execute", input="-", database=database, driver=driver, stdin=sql ) @@ -119,12 +119,12 @@ def main(): else: newcoltype = oldcoltype - sql = "ALTER TABLE %s CHANGE %s %s %s" % (table, oldcol, newcol, newcoltype) + sql = f'ALTER TABLE {table} CHANGE "{oldcol}" "{newcol}" {newcoltype}' grass.write_command( "db.execute", input="-", database=database, driver=driver, stdin=sql ) else: - sql = "ALTER TABLE %s RENAME %s TO %s" % (table, oldcol, newcol) + sql = f'ALTER TABLE {table} RENAME "{oldcol}" TO "{newcol}"' grass.write_command( "db.execute", input="-", database=database, driver=driver, stdin=sql ) diff --git a/scripts/v.import/v.import.html b/scripts/v.import/v.import.html index 072b92e5529..a530f5f3662 100644 --- a/scripts/v.import/v.import.html +++ b/scripts/v.import/v.import.html @@ -64,46 +64,6 @@

EXAMPLE

v.import input=research_area.shp output=research_area extent=input -

ERROR MESSAGES

- -

SQL syntax errors

- -Depending on the currently selected SQL driver, error messages such as follows may arise: - -
-DBMI-SQLite driver error:
-Error in sqlite3_prepare():
-near "ORDER": syntax error
-
- -Or: - -
-DBMI-DBF driver error:
-SQL parser error:
-syntax error, unexpected DESC, expecting NAME processing 'DESC
-
- -This indicates that a column name in the input dataset corresponds to a reserved -SQL word (here: 'ORDER' and 'DESC' respectively). A different column name has to be -used in this case. The columns parameter can be used to assign different -column names on the fly in order to avoid using reserved SQL words. - -For a list of SQL reserved words for SQLite (the default driver), -see here. - -

Projection errors

- -
-Projection of dataset does not appear to match the current project.
-
- -Here you need to create or use a project whose CRS matches that -of the vector data you wish to import. Try using location parameter to -create a new project based upon the CRS information in the file. If -desired, you can then re-project it to another project -with v.proj. -

SEE ALSO

diff --git a/scripts/v.rast.stats/testsuite/test_v_rast_stats.py b/scripts/v.rast.stats/testsuite/test_v_rast_stats.py index 4b411bf6666..ca3111d14d8 100644 --- a/scripts/v.rast.stats/testsuite/test_v_rast_stats.py +++ b/scripts/v.rast.stats/testsuite/test_v_rast_stats.py @@ -2,6 +2,7 @@ @author Soeren Gebbert """ + from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule from grass.pygrass.vector import VectorTopo diff --git a/scripts/v.unpack/v.unpack.py b/scripts/v.unpack/v.unpack.py index 6fed249ccf7..bd350676444 100644 --- a/scripts/v.unpack/v.unpack.py +++ b/scripts/v.unpack/v.unpack.py @@ -32,8 +32,8 @@ # %end # %flag # % key: o -# % label: Override projection check (use current location's projection) -# % description: Assume that the dataset has same projection as the current location +# % label: Override projection check (use current projects's CRS) +# % description: Assume that the dataset has same coordinate reference system as the current project # % guisection: Output settings # %end # %flag @@ -155,7 +155,7 @@ def main(): grass.fatal( _( "PROJ_INFO file is missing, unpack vector map in XY (unprojected) " - "location." + "project." ) ) skip_projection_check = True # XY location @@ -186,22 +186,23 @@ def main(): grass.warning( _( "Difference between PROJ_INFO file of packed map " - "and of current location:\n{diff}" + "and of current project:\n{diff}" ).format(diff="".join(diff_result_1)) ) if diff_result_2: grass.warning( _( "Difference between PROJ_UNITS file of packed map " - "and of current location:\n{diff}" + "and of current project:\n{diff}" ).format(diff="".join(diff_result_2)) ) grass.fatal( _( - "Projection of dataset does not appear to match current " - "location. In case of no significant differences in the " - "projection definitions, use the -o flag to ignore them and " - "use current location definition." + "Coordinate reference system of dataset does not" + " appear to match current project." + " In case of no significant differences in the CRS definitions," + " use the -o flag to ignore them and use" + " current project definition." ) ) diff --git a/temporal/t.rast.aggregate/testsuite/test_aggregation_absolute.py b/temporal/t.rast.aggregate/testsuite/test_aggregation_absolute.py index 2056a654f7c..c5eb4618d92 100644 --- a/temporal/t.rast.aggregate/testsuite/test_aggregation_absolute.py +++ b/temporal/t.rast.aggregate/testsuite/test_aggregation_absolute.py @@ -7,6 +7,7 @@ :authors: Soeren Gebbert """ + import os import grass.pygrass.modules as pymod import grass.temporal as tgis diff --git a/temporal/t.rast.aggregate/testsuite/test_aggregation_absolute_parallel.py b/temporal/t.rast.aggregate/testsuite/test_aggregation_absolute_parallel.py index f70c150c7c0..ac6471398ad 100644 --- a/temporal/t.rast.aggregate/testsuite/test_aggregation_absolute_parallel.py +++ b/temporal/t.rast.aggregate/testsuite/test_aggregation_absolute_parallel.py @@ -7,6 +7,7 @@ :authors: Soeren Gebbert """ + import os import grass.pygrass.modules as pymod import grass.temporal as tgis diff --git a/temporal/t.rast.aggregate/testsuite/test_aggregation_relative.py b/temporal/t.rast.aggregate/testsuite/test_aggregation_relative.py index 265d4f82465..fe53844feea 100644 --- a/temporal/t.rast.aggregate/testsuite/test_aggregation_relative.py +++ b/temporal/t.rast.aggregate/testsuite/test_aggregation_relative.py @@ -7,6 +7,7 @@ :authors: Soeren Gebbert """ + import os import grass.pygrass.modules as pymod import grass.temporal as tgis diff --git a/temporal/t.rast.series/testsuite/test_series.py b/temporal/t.rast.series/testsuite/test_series.py index 4eb41f80f1a..7b9df7921b7 100644 --- a/temporal/t.rast.series/testsuite/test_series.py +++ b/temporal/t.rast.series/testsuite/test_series.py @@ -7,6 +7,7 @@ :authors: Soeren Gebbert """ + import os import grass.pygrass.modules as pymod import grass.temporal as tgis diff --git a/temporal/t.rast3d.algebra/testsuite/test_raster3d_algebra.py b/temporal/t.rast3d.algebra/testsuite/test_raster3d_algebra.py index 59d47a1dacd..980ec553119 100644 --- a/temporal/t.rast3d.algebra/testsuite/test_raster3d_algebra.py +++ b/temporal/t.rast3d.algebra/testsuite/test_raster3d_algebra.py @@ -7,7 +7,6 @@ :authors: Soeren Gebbert and Thomas Leppelt """ - import grass.script import grass.temporal as tgis from grass.gunittest.case import TestCase diff --git a/temporal/t.shift/testsuite/test_shift.py b/temporal/t.shift/testsuite/test_shift.py index 1c1f29f03d9..eff9a5f5702 100644 --- a/temporal/t.shift/testsuite/test_shift.py +++ b/temporal/t.shift/testsuite/test_shift.py @@ -7,6 +7,7 @@ :authors: Soeren Gebbert """ + import os import grass.pygrass.modules as pymod import grass.temporal as tgis diff --git a/temporal/t.snap/testsuite/test_snap.py b/temporal/t.snap/testsuite/test_snap.py index e16acc52a57..5d8c0bbe166 100644 --- a/temporal/t.snap/testsuite/test_snap.py +++ b/temporal/t.snap/testsuite/test_snap.py @@ -7,6 +7,7 @@ :authors: Soeren Gebbert """ + import os import grass.pygrass.modules as pymod import grass.temporal as tgis diff --git a/temporal/t.support/testsuite/test_support_str3ds.py b/temporal/t.support/testsuite/test_support_str3ds.py index 4e2a1d30123..613b5e9bbc3 100644 --- a/temporal/t.support/testsuite/test_support_str3ds.py +++ b/temporal/t.support/testsuite/test_support_str3ds.py @@ -7,6 +7,7 @@ :authors: Soeren Gebbert """ + import os import grass.pygrass.modules as pymod import grass.temporal as tgis diff --git a/temporal/t.support/testsuite/test_support_strds.py b/temporal/t.support/testsuite/test_support_strds.py index a618f94bc5b..40692693c34 100644 --- a/temporal/t.support/testsuite/test_support_strds.py +++ b/temporal/t.support/testsuite/test_support_strds.py @@ -7,6 +7,7 @@ :authors: Soeren Gebbert """ + import os import grass.pygrass.modules as pymod import grass.temporal as tgis diff --git a/temporal/t.support/testsuite/test_support_stvds.py b/temporal/t.support/testsuite/test_support_stvds.py index 256e3a9b98f..2cd973c2119 100644 --- a/temporal/t.support/testsuite/test_support_stvds.py +++ b/temporal/t.support/testsuite/test_support_stvds.py @@ -7,6 +7,7 @@ :authors: Soeren Gebbert """ + import os import grass.pygrass.modules as pymod import grass.temporal as tgis diff --git a/temporal/t.unregister/testsuite/test_unregister.py b/temporal/t.unregister/testsuite/test_unregister.py index 6cf54e482e2..fa011b9edaa 100644 --- a/temporal/t.unregister/testsuite/test_unregister.py +++ b/temporal/t.unregister/testsuite/test_unregister.py @@ -7,6 +7,7 @@ :authors: Soeren Gebbert """ + import os import grass.pygrass.modules as pymod import grass.temporal as tgis diff --git a/vector/v.external/args.c b/vector/v.external/args.c index 798982f2c15..7f482f836e6 100644 --- a/vector/v.external/args.c +++ b/vector/v.external/args.c @@ -46,9 +46,10 @@ void parse_args(int argc, char **argv, struct _options *options, flags->override = G_define_flag(); flags->override->key = 'o'; flags->override->label = - _("Override projection check (use current location's projection)"); - flags->override->description = _("Assume that the dataset has the same " - "projection as the current location"); + _("Override projection check (use current project's CRS)"); + flags->override->description = + _("Assume that the dataset has the same " + "coordinate reference system as the current project"); flags->proj = G_define_flag(); flags->proj->key = 'j'; diff --git a/vector/v.external/proj.c b/vector/v.external/proj.c index b1eae407c35..625482fee5e 100644 --- a/vector/v.external/proj.c +++ b/vector/v.external/proj.c @@ -163,15 +163,15 @@ void check_projection(struct Cell_head *cellhd, GDALDatasetH hDS, int layer, /* do not create a xy location because this can mean that the * real SRS has not been recognized or is missing */ if (proj_trouble) { - G_fatal_error(_("Unable to convert input map projection to GRASS " - "format; cannot create new location.")); + G_fatal_error(_("Unable to convert input map CRS to GRASS " + "format; cannot create new project.")); } else { if (0 != G_make_location_crs(outloc, cellhd, proj_info, proj_units, srid, wkt)) { - G_fatal_error(_("Unable to create new location <%s>"), outloc); + G_fatal_error(_("Unable to create new project <%s>"), outloc); } - G_message(_("Location <%s> created"), outloc); + G_message(_("Project <%s> created"), outloc); G_unset_window(); /* new location, projection, and window */ G_get_window(cellhd); @@ -232,14 +232,15 @@ void check_projection(struct Cell_head *cellhd, GDALDatasetH hDS, int layer, proj_info, proj_units)) != 1) { int i_value; - strcpy(error_msg, _("Projection of dataset does not" - " appear to match current location.\n\n")); + strcpy(error_msg, + _("Coordinate reference system of dataset does not" + " appear to match current project.\n\n")); /* TODO: output this info sorted by key: */ if (loc_wind.proj != cellhd->proj || err != -2) { /* error in proj_info */ if (loc_proj_info != NULL) { - strcat(error_msg, _("Location PROJ_INFO is:\n")); + strcat(error_msg, _("Project PROJ_INFO is:\n")); for (i_value = 0; i_value < loc_proj_info->nitems; i_value++) sprintf(error_msg + strlen(error_msg), "%s: %s\n", @@ -248,22 +249,22 @@ void check_projection(struct Cell_head *cellhd, GDALDatasetH hDS, int layer, strcat(error_msg, "\n"); } else { - strcat(error_msg, _("Location PROJ_INFO is:\n")); + strcat(error_msg, _("Project PROJ_INFO is:\n")); if (loc_wind.proj == PROJECTION_XY) sprintf(error_msg + strlen(error_msg), - "Location proj = %d (unreferenced/unknown)\n", + "Project proj = %d (unreferenced/unknown)\n", loc_wind.proj); else if (loc_wind.proj == PROJECTION_LL) sprintf(error_msg + strlen(error_msg), - "Location proj = %d (lat/long)\n", + "Project proj = %d (lat/long)\n", loc_wind.proj); else if (loc_wind.proj == PROJECTION_UTM) sprintf(error_msg + strlen(error_msg), - "Location proj = %d (UTM), zone = %d\n", + "Project proj = %d (UTM), zone = %d\n", loc_wind.proj, cellhd->zone); else sprintf(error_msg + strlen(error_msg), - "Location proj = %d (unknown), zone = %d\n", + "Project proj = %d (unknown), zone = %d\n", loc_wind.proj, cellhd->zone); } @@ -337,7 +338,7 @@ void check_projection(struct Cell_head *cellhd, GDALDatasetH hDS, int layer, else { /* error in proj_units */ if (loc_proj_units != NULL) { - strcat(error_msg, "Location PROJ_UNITS is:\n"); + strcat(error_msg, "Project PROJ_UNITS is:\n"); for (i_value = 0; i_value < loc_proj_units->nitems; i_value++) sprintf(error_msg + strlen(error_msg), "%s: %s\n", @@ -355,13 +356,14 @@ void check_projection(struct Cell_head *cellhd, GDALDatasetH hDS, int layer, } } if (!check_only) { - strcat(error_msg, _("\nIn case of no significant differences " - "in the projection definitions," - " use the -o flag to ignore them and use" - " current location definition.\n")); - strcat(error_msg, _("Consider generating a new location from " + strcat(error_msg, + _("\nIn case of no significant differences " + "in the coordinate reference system definitions," + " use the -o flag to ignore them and use" + " current project definition.\n")); + strcat(error_msg, _("Consider generating a new project from " "the input dataset using " - "the 'location' parameter.\n")); + "the 'project' parameter.\n")); } if (check_only) @@ -379,8 +381,8 @@ void check_projection(struct Cell_head *cellhd, GDALDatasetH hDS, int layer, msg_fn = G_message; else msg_fn = G_verbose_message; - msg_fn(_("Projection of input dataset and current location " - "appear to match")); + msg_fn(_("Coordinate reference system of input dataset and current " + "project appear to match")); if (check_only) { GDALClose(hDS); exit(EXIT_SUCCESS); diff --git a/vector/v.extract/testsuite/test_v_extract.py b/vector/v.extract/testsuite/test_v_extract.py index ff0c8b590a3..d266e8ad649 100644 --- a/vector/v.extract/testsuite/test_v_extract.py +++ b/vector/v.extract/testsuite/test_v_extract.py @@ -8,6 +8,7 @@ License (>=v2). Read the file COPYING that comes with GRASS for details. """ + import os from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule diff --git a/vector/v.fill.holes/examples.ipynb b/vector/v.fill.holes/examples.ipynb index 28d86332b0a..fcc006501e4 100644 --- a/vector/v.fill.holes/examples.ipynb +++ b/vector/v.fill.holes/examples.ipynb @@ -82,7 +82,7 @@ "outputs": [], "source": [ "!g.region vector=data grow=3 res=1\n", - "text_position = (75,5)" + "text_position = (75, 5)" ] }, { @@ -144,7 +144,7 @@ "source": [ "!g.region vector=dissolve_data grow=2 res=1\n", "\n", - "text_position = (75,5)\n", + "text_position = (75, 5)\n", "\n", "plot = gj.Map(use_region=True, width=700)\n", "plot.d_background(color=\"white\")\n", @@ -209,7 +209,7 @@ "plot = gj.Map(use_region=True, width=700)\n", "plot.d_background(color=\"white\")\n", "plot.d_vect(map=\"lakes_only\", legend_label=\"Original\")\n", - "plot.d_legend_vect(flags=\"b\", at=(60,10))\n", + "plot.d_legend_vect(flags=\"b\", at=(60, 10))\n", "plot.show()" ] }, @@ -231,7 +231,7 @@ "plot = gj.Map(use_region=True, width=700)\n", "plot.d_background(color=\"white\")\n", "plot.d_vect(map=\"lakes_filled\", legend_label=\"Filled\")\n", - "plot.d_legend_vect(flags=\"b\", at=(60,10))\n", + "plot.d_legend_vect(flags=\"b\", at=(60, 10))\n", "plot.show()" ] }, @@ -253,7 +253,7 @@ "plot = gj.Map(use_region=True, width=700)\n", "plot.d_background(color=\"white\")\n", "plot.d_vect(map=\"lakes_dissolved\", legend_label=\"Dissolved\")\n", - "plot.d_legend_vect(flags=\"b\", at=(60,10))\n", + "plot.d_legend_vect(flags=\"b\", at=(60, 10))\n", "plot.show()" ] }, @@ -265,9 +265,17 @@ "source": [ "plot = gj.Map(use_region=True, width=1024)\n", "plot.d_background(color=\"#E28A2B\")\n", - "plot.d_vect(map=\"lakes_filled\", color=\"none\", fill_color=\"#384C6B\", legend_label=\"Filled\")\n", - "plot.d_vect(map=\"lakes_only\", color=\"#859BBA\", fill_color=\"none\", width=2, legend_label=\"Original\")\n", - "plot.d_legend_vect(flags=\"b\", at=(80,85), fontsize=22, symbol_size=35)\n", + "plot.d_vect(\n", + " map=\"lakes_filled\", color=\"none\", fill_color=\"#384C6B\", legend_label=\"Filled\"\n", + ")\n", + "plot.d_vect(\n", + " map=\"lakes_only\",\n", + " color=\"#859BBA\",\n", + " fill_color=\"none\",\n", + " width=2,\n", + " legend_label=\"Original\",\n", + ")\n", + "plot.d_legend_vect(flags=\"b\", at=(80, 85), fontsize=22, symbol_size=35)\n", "filename = \"v_fill_holes.png\"\n", "plot.save(filename)\n", "!mogrify -trim {filename}\n", diff --git a/vector/v.in.lidar/main.c b/vector/v.in.lidar/main.c index 76bc0ef540e..8db7d1e3ee6 100644 --- a/vector/v.in.lidar/main.c +++ b/vector/v.in.lidar/main.c @@ -270,10 +270,10 @@ int main(int argc, char *argv[]) limit_opt->guisection = _("Decimation"); outloc_opt = G_define_option(); - outloc_opt->key = "location"; + outloc_opt->key = "project"; outloc_opt->type = TYPE_STRING; outloc_opt->required = NO; - outloc_opt->description = _("Name for new location to create"); + outloc_opt->description = _("Name for new project (location) to create"); outloc_opt->key_desc = "name"; print_flag = G_define_flag(); @@ -319,15 +319,16 @@ int main(int argc, char *argv[]) over_flag = G_define_flag(); over_flag->key = 'o'; over_flag->label = - _("Override projection check (use current location's projection)"); - over_flag->description = _( - "Assume that the dataset has same projection as the current location"); + _("Override projection check (use current project's CRS)"); + over_flag->description = + _("Assume that the dataset has the same coordinate reference system as " + "the current project"); no_import_flag = G_define_flag(); no_import_flag->key = 'i'; - no_import_flag->description = _( - "Create the location specified by the \"location\" parameter and exit." - " Do not import the vector data."); + no_import_flag->description = + _("Create the project specified by the \"project\" parameter and exit." + " Do not import the vector data."); no_import_flag->suppress_required = YES; G_option_exclusive(skip_opt, preserve_opt, NULL); @@ -512,16 +513,16 @@ int main(int argc, char *argv[]) * assume the user has a terminal open */ if (GPJ_wkt_to_grass(&cellhd, &proj_info, &proj_units, projstr, 0) < 0) { - G_fatal_error(_("Unable to convert input map projection to GRASS " - "format; cannot create new location.")); + G_fatal_error(_("Unable to convert input map CRS to GRASS " + "format; cannot create new project.")); } else { if (0 != G_make_location(outloc_opt->answer, &cellhd, proj_info, proj_units)) { - G_fatal_error(_("Unable to create new location <%s>"), + G_fatal_error(_("Unable to create new project <%s>"), outloc_opt->answer); } - G_message(_("Location <%s> created"), outloc_opt->answer); + G_message(_("Project <%s> created"), outloc_opt->answer); } /* If the i flag is set, clean up? and exit here */ @@ -529,7 +530,7 @@ int main(int argc, char *argv[]) exit(EXIT_SUCCESS); /* TODO: */ - G_warning("Import into new location not yet implemented"); + G_warning("Import into new project not yet implemented"); /* at this point the module should be using G_create_alt_env() to change context to the newly created location; once done it should switch back with G_switch_env(). See r.in.gdal */ diff --git a/vector/v.in.lidar/projection.c b/vector/v.in.lidar/projection.c index 44dc7c680d3..aba51081b91 100644 --- a/vector/v.in.lidar/projection.c +++ b/vector/v.in.lidar/projection.c @@ -27,8 +27,8 @@ void projection_mismatch_report(struct Cell_head cellhd, int i_value; char error_msg[8192]; - strcpy(error_msg, _("Projection of dataset does not" - " appear to match current location.\n\n")); + strcpy(error_msg, _("Coordinate reference system of dataset does not" + " appear to match current project.\n\n")); /* TODO: output this info sorted by key: */ if (loc_wind.proj != cellhd.proj || err != -2) { @@ -84,12 +84,12 @@ void projection_mismatch_report(struct Cell_head cellhd, } } sprintf(error_msg + strlen(error_msg), - _("\nIn case of no significant differences in the projection " - "definitions," + _("\nIn case of no significant differences" + " in the coordinate reference system definitions," " use the -o flag to ignore them and use" - " current location definition.\n")); + " current project definition.\n")); strcat(error_msg, - _("Consider generating a new location with 'location' parameter" + _("Consider generating a new project with 'project' parameter" " from input data set.\n")); G_fatal_error("%s", error_msg); } @@ -130,7 +130,8 @@ void projection_check_wkt(struct Cell_head cellhd, struct Cell_head loc_wind, loc_proj_units, proj_info, proj_units, err); } else if (verbose) { - G_message(_("Projection of input dataset and current location " - "appear to match")); + G_message(_( + "Coordinate reference system of input dataset and current project " + "appear to match")); } } diff --git a/vector/v.in.lidar/v.in.lidar.html b/vector/v.in.lidar/v.in.lidar.html index 04c76c0e281..72177b9f5ed 100644 --- a/vector/v.in.lidar/v.in.lidar.html +++ b/vector/v.in.lidar/v.in.lidar.html @@ -91,7 +91,7 @@

Project Creation

If the user wishes to import the data with the full CRS definition, it is possible to have v.in.lidar automatically create a new project based on the CRS and extents of the file being read. This is accomplished -by passing the name to be used for the new project via the location +by passing the name to be used for the new project via the project parameter. Upon completion of the command, a new project will have been created (with only a PERMANENT mapset), and the vector map will have been imported with the indicated output name into the PERMANENT mapset. @@ -116,7 +116,7 @@

EXAMPLE

v.in.lidar -p input="Serpent Mound Model LAS Data.las" # create a project with CRS information of the LAS data - v.in.lidar -i input="Serpent Mound Model LAS Data.las" location=Serpent_Mound + v.in.lidar -i input="Serpent Mound Model LAS Data.las" 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 diff --git a/vector/v.in.ogr/testsuite/test_v_in_ogr.py b/vector/v.in.ogr/testsuite/test_v_in_ogr.py index 7257c44b0ee..167cd8747e8 100644 --- a/vector/v.in.ogr/testsuite/test_v_in_ogr.py +++ b/vector/v.in.ogr/testsuite/test_v_in_ogr.py @@ -2,6 +2,7 @@ @author Markus Neteler """ + from grass.gunittest.case import TestCase from grass.gunittest.gmodules import SimpleModule diff --git a/vector/v.in.pdal/main.cpp b/vector/v.in.pdal/main.cpp index 9348463ecc3..0f48e1d5fea 100644 --- a/vector/v.in.pdal/main.cpp +++ b/vector/v.in.pdal/main.cpp @@ -212,19 +212,20 @@ int main(int argc, char *argv[]) Flag *reproject_flag = G_define_flag(); reproject_flag->key = 'w'; reproject_flag->label = - _("Reproject to location's coordinate system if needed"); + _("Reproject to projects's coordinate system if needed"); reproject_flag->description = _("Reprojects input dataset to the coordinate system of" - " the GRASS location (by default only datasets with the" + " the GRASS project (by default only datasets with the" " matching coordinate system can be imported"); reproject_flag->guisection = _("Projection"); Flag *over_flag = G_define_flag(); over_flag->key = 'o'; over_flag->label = - _("Override projection check (use current location's projection)"); - over_flag->description = _( - "Assume that the dataset has same projection as the current location"); + _("Override projection check (use current project's CRS)"); + over_flag->description = + _("Assume that the dataset has the same coordinate reference system as " + "the current project"); over_flag->guisection = _("Projection"); // TODO: from the API it seems that also prj file path and proj string will @@ -347,7 +348,7 @@ int main(int argc, char *argv[]) // we reproject when requested regardless the input projection if (reproject_flag->answer) { - G_message(_("Reprojecting the input to the location projection")); + G_message(_("Reprojecting the input to the project's CRS")); char *proj_wkt = location_projection_as_wkt(false); pdal::Options o4; // TODO: try catch for user input error @@ -370,8 +371,8 @@ int main(int argc, char *argv[]) // getting projection is possible only after prepare if (over_flag->answer) { G_important_message(_("Overriding projection check and assuming" - " that the projection of input matches" - " the location projection")); + " that the CRS of input matches" + " the project's CRS")); } else if (!reproject_flag->answer) { pdal::SpatialReference spatial_reference = diff --git a/vector/v.in.pdal/projection.c b/vector/v.in.pdal/projection.c index 244ee323afb..aa811a0a409 100644 --- a/vector/v.in.pdal/projection.c +++ b/vector/v.in.pdal/projection.c @@ -27,8 +27,8 @@ void projection_mismatch_report(struct Cell_head cellhd, int i_value; char error_msg[8192]; - strcpy(error_msg, _("Projection of dataset does not" - " appear to match current location.\n\n")); + strcpy(error_msg, _("Coordinate reference system of the dataset does not" + " appear to match current project.\n\n")); /* TODO: output this info sorted by key: */ if (loc_wind.proj != cellhd.proj || err != -2) { @@ -84,12 +84,12 @@ void projection_mismatch_report(struct Cell_head cellhd, } } sprintf(error_msg + strlen(error_msg), - _("\nIn case of no significant differences in the projection " - "definitions," + _("\nIn case of no significant differences" + " in the coordinate reference system definitions," " use the -o flag to ignore them and use" - " current location definition.\n")); + " current project definition.\n")); strcat(error_msg, - _("Consider generating a new location with 'location' parameter" + _("Consider generating a new project with 'project' parameter" " from input data set.\n")); G_fatal_error("%s", error_msg); } @@ -131,8 +131,8 @@ void projection_check_wkt(struct Cell_head cellhd, struct Cell_head loc_wind, } else { if (verbose) { - G_message(_("Projection of input dataset and current location " - "appear to match")); + G_message(_("Coordinate reference system of the input dataset and " + "current project appear to match")); } } } diff --git a/vector/v.to.rast/testsuite/test_v_to_rast.py b/vector/v.to.rast/testsuite/test_v_to_rast.py index 273c77983d8..352b8c578f3 100644 --- a/vector/v.to.rast/testsuite/test_v_to_rast.py +++ b/vector/v.to.rast/testsuite/test_v_to_rast.py @@ -11,6 +11,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.univar/testsuite/v_univar_test.py b/vector/v.univar/testsuite/v_univar_test.py index 3b9c75b7d70..33fd0425c1d 100644 --- a/vector/v.univar/testsuite/v_univar_test.py +++ b/vector/v.univar/testsuite/v_univar_test.py @@ -8,6 +8,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 from grass.gunittest.gmodules import SimpleModule diff --git a/vector/v.vect.stats/testsuite/test_vect_stats.py b/vector/v.vect.stats/testsuite/test_vect_stats.py index 8d7cf0e6c18..fe982ceff33 100644 --- a/vector/v.vect.stats/testsuite/test_vect_stats.py +++ b/vector/v.vect.stats/testsuite/test_vect_stats.py @@ -8,6 +8,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 from grass.gunittest.gmodules import SimpleModule