From beda5b3d8db782ce412bb3ac30a72353f7d87836 Mon Sep 17 00:00:00 2001 From: Ivan Mincik Date: Thu, 20 Jun 2024 08:17:11 +0000 Subject: [PATCH 001/293] packaging: Add Nix files for creating development environment and the package (#3889) --- .gitignore | 1 + flake.lock | 57 ++++++++++++++++++++ flake.nix | 80 ++++++++++++++++++++++++++++ package.nix | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 288 insertions(+) create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 package.nix diff --git a/.gitignore b/.gitignore index 2cc7f3da721..0db3adb55f5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ /emacs.desktop *~ *.lock +!flake.lock *.pyc OBJ.* locale/scriptstrings/* diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000000..ff511760ebb --- /dev/null +++ b/flake.lock @@ -0,0 +1,57 @@ +{ + "nodes": { + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1717285511, + "narHash": "sha256-iKzJcpdXih14qYVcZ9QC9XuZYnPc6T8YImb6dX166kw=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "2a55567fcf15b1b1c7ed712a2c6fadaec7412ea8", + "type": "github" + }, + "original": { + "id": "flake-parts", + "type": "indirect" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1718428119, + "narHash": "sha256-WdWDpNaq6u1IPtxtYHHWpl5BmabtpmLnMAx0RdJ/vo8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e6cea36f83499eb4e9cd184c8a8e823296b50ad5", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1717284937, + "narHash": "sha256-lIbdfCsf8LMFloheeE6N31+BMIeixqyQWbSr2vk79EQ=", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/eb9ceca17df2ea50a250b6b27f7bf6ab0186f198.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/eb9ceca17df2ea50a250b6b27f7bf6ab0186f198.tar.gz" + } + }, + "root": { + "inputs": { + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000000..d3d2b47e995 --- /dev/null +++ b/flake.nix @@ -0,0 +1,80 @@ +{ + description = "GRASS GIS"; + + nixConfig = { + bash-prompt = "\\[\\033[1m\\][grass-dev]\\[\\033\[m\\]\\040\\w >\\040"; + }; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + }; + + outputs = inputs@{ flake-parts, ... }: + flake-parts.lib.mkFlake { inherit inputs; } { + + systems = [ "x86_64-linux" ]; + + perSystem = { config, self', inputs', pkgs, system, ... }: { + + packages.grass = pkgs.callPackage ./package.nix { }; + + devShells.default = pkgs.mkShell { + buildInputs = + # from package nativeBuildInputs + with pkgs; [ + bison + flex + gdal # for `gdal-config` + geos # for `geos-config` + netcdf # for `nc-config` + pkg-config + ] ++ (with python3Packages; [ pytest python-dateutil numpy wxPython_4_2 ]) + + # from package buildInputs + ++ [ + blas + cairo + ffmpeg + fftw + freetype + gdal + geos + lapack + libGLU + libpng + libsvm + libtiff + libxml2 + netcdf + pdal + postgresql + proj + readline + sqlite + wxGTK32 + zlib + zstd + ] ++ lib.optionals stdenv.isDarwin [ libiconv ]; + + shellHook = '' + echo -e "\nWelcome to a GRASS development environment !" + echo "Build GRASS using following commands:" + echo + echo " 1. ./configure --prefix=\$(pwd)/app" + echo " 2. make -j$(nproc)" + echo " 3. make install" + echo + echo "Run tests:" + echo + echo " 1. export PYTHONPATH=\$(app/bin/grass --config python_path):\$PYTHONPATH" + echo " 2. export LD_LIBRARY_PATH=\$(app/bin/grass --config path)/lib:\$LD_LIBRARY_PATH" + echo " 3. pytest" + echo + echo "Note: run 'nix flake update' from time to time to update dependencies." + ''; + }; + }; + + flake = { }; + }; +} diff --git a/package.nix b/package.nix new file mode 100644 index 00000000000..2a5def372c2 --- /dev/null +++ b/package.nix @@ -0,0 +1,150 @@ +{ lib +, stdenv +, makeWrapper +, wrapGAppsHook + +, withOpenGL ? true + +, bison +, blas +, cairo +, ffmpeg +, fftw +, flex +, freetype +, gdal +, geos +, lapack +, libGLU +, libiconv +, libpng +, libsvm +, libtiff +, libxml2 +, netcdf +, pdal +, pkg-config +, postgresql +, proj +, python3Packages +, readline +, sqlite +, wxGTK32 +, zlib +, zstd + +}: + +stdenv.mkDerivation (finalAttrs: { + pname = "grass"; + version = "dev"; + + src = lib.cleanSourceWith { + src = ./.; + filter = path: type: + ! (lib.hasSuffix ".nix" path); + }; + + nativeBuildInputs = [ + makeWrapper + wrapGAppsHook + + bison + flex + gdal # for `gdal-config` + geos # for `geos-config` + netcdf # for `nc-config` + pkg-config + ] ++ (with python3Packages; [ python-dateutil numpy wxPython_4_2 ]); + + buildInputs = [ + blas + cairo + ffmpeg + fftw + freetype + gdal + geos + lapack + libpng + libsvm + libtiff + libxml2 + netcdf + pdal + postgresql + proj + readline + sqlite + wxGTK32 + zlib + zstd + ] ++ lib.optionals withOpenGL [ libGLU ] + ++ lib.optionals stdenv.isDarwin [ libiconv ]; + + strictDeps = true; + + configureFlags = [ + "--with-blas" + "--with-cairo-ldflags=-lfontconfig" + "--with-cxx" + "--with-fftw" + "--with-freetype" + "--with-geos" + "--with-gdal" + "--with-lapack" + "--with-libsvm" + "--with-nls" + "--with-openmp" + "--with-pdal" + "--with-postgres" + "--with-postgres-libs=${postgresql.lib}/lib/" + "--with-proj-includes=${proj.dev}/include" + "--with-proj-libs=${proj}/lib" + "--with-proj-share=${proj}/share/proj" + "--with-sqlite" + "--with-zstd" + "--without-bzlib" + "--without-mysql" + "--without-odbc" + ] ++ lib.optionals (! withOpenGL) [ + "--without-opengl" + ] ++ lib.optionals stdenv.isDarwin [ + "--without-cairo" + "--without-freetype" + "--without-x" + ]; + + # Otherwise a very confusing "Can't load GDAL library" error + makeFlags = lib.optional stdenv.isDarwin "GDAL_DYNAMIC="; + + # Ensures that the python scripts running at build time are actually + # executable; otherwise, patchShebangs ignores them. + postConfigure = '' + for f in $(find . -name '*.py'); do + chmod +x $f + done + + patchShebangs */ + ''; + + postInstall = '' + wrapProgram $out/bin/grass \ + --set PYTHONPATH $PYTHONPATH \ + --set GRASS_PYTHON ${python3Packages.python.interpreter} \ + --suffix LD_LIBRARY_PATH ':' '${gdal}/lib' + ln -s $out/grass*/lib $out/lib + ln -s $out/grass*/include $out/include + ''; + + enableParallelBuilding = true; + + meta = with lib; { + description = "GIS software suite used for geospatial data management and analysis, image processing, graphics and maps production, spatial modeling, and visualization"; + homepage = "https://grass.osgeo.org/"; + license = licenses.gpl2Plus; + maintainers = with maintainers; teams.geospatial.members; + platforms = platforms.all; + mainProgram = "grass"; + }; +}) From 7ad3e34c069bb2387451ed3d0ab4a17f01a776be Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 21 Jun 2024 09:52:36 +0000 Subject: [PATCH 002/293] CI(deps): Update docker/build-push-action action to v6.1.0 (#3913) --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 9c278b6d10b..43209646de3 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -76,7 +76,7 @@ jobs: password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push id: docker_build - uses: docker/build-push-action@94f8f8c2eec4bc3f1d78c1755580779804cb87b2 # v6.0.1 + uses: docker/build-push-action@31159d49c0d4756269a0940a750801a1ea5d7003 # v6.1.0 with: push: true pull: true From 004b916c4f66a12b5e2b713f4925570666fb73e2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 21 Jun 2024 11:55:11 +0200 Subject: [PATCH 003/293] CI(deps): Update alpine:3.20 Docker digest to b89d9c9 (#3914) --- docker/alpine/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/alpine/Dockerfile b/docker/alpine/Dockerfile index ab704a78fb6..ad2a878bfae 100644 --- a/docker/alpine/Dockerfile +++ b/docker/alpine/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.20@sha256:77726ef6b57ddf65bb551896826ec38bc3e53f75cdde31354fbffb4f25238ebd as common +FROM alpine:3.20@sha256:b89d9c93e9ed3597455c90a0b88a8bbb5cb7188438f70953fede212a0c4394e0 as common # Based on: # https://github.com/mundialis/docker-grass-gis/blob/master/Dockerfile From e18a39994cbab1940755855a16b0bf7cfaa731e4 Mon Sep 17 00:00:00 2001 From: Ondrej Pesek Date: Fri, 21 Jun 2024 21:54:47 +0200 Subject: [PATCH 004/293] doc: help_loc_structure -> help_project_structure (#3910) * location is renamed to project in 8.4 --- doc/help_loc_structure.odg | Bin 34698 -> 0 bytes doc/help_project_structure.odg | Bin 0 -> 30200 bytes grasslib.dox | 4 ++-- lib/init/Makefile | 2 +- lib/init/help_loc_struct.png | Bin 79007 -> 0 bytes lib/init/help_project_structure.png | Bin 0 -> 117039 bytes lib/loc_struct.png | Bin 54377 -> 0 bytes 7 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 doc/help_loc_structure.odg create mode 100644 doc/help_project_structure.odg delete mode 100644 lib/init/help_loc_struct.png create mode 100644 lib/init/help_project_structure.png delete mode 100644 lib/loc_struct.png diff --git a/doc/help_loc_structure.odg b/doc/help_loc_structure.odg deleted file mode 100644 index b4a3dcc6fa455c5b0022df225aa003bb5eae0c43..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34698 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 diff --git a/doc/help_project_structure.odg b/doc/help_project_structure.odg new file mode 100644 index 0000000000000000000000000000000000000000..d1941ee27cce4ab45a5ee220c55033fd3ff82788 GIT binary patch literal 30200 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 M;jBox=bumi2Tj*Rz5oCK literal 0 HcmV?d00001 diff --git a/grasslib.dox b/grasslib.dox index cdc314a6b1d..dd5de826cfe 100644 --- a/grasslib.dox +++ b/grasslib.dox @@ -203,9 +203,9 @@ as follows: -\image html "loc_struct.png" "Diagram of GRASS file structure" +\image html "help_project_structure.png" "Diagram of GRASS file structure" \section Compiling_and_Installing_GRASS_Modules Compiling and Installing GRASS Modules diff --git a/lib/init/Makefile b/lib/init/Makefile index 645b9ac0164..f6983faada3 100644 --- a/lib/init/Makefile +++ b/lib/init/Makefile @@ -25,7 +25,7 @@ FILES = \ $(ETC)/echo$(EXE) \ $(ETC)/license \ $(ETC)/VERSIONNUMBER \ - $(HTMLDIR)/help_loc_struct.png \ + $(HTMLDIR)/help_project_structure.png \ $(HTMLDIR)/grassdb.png \ $(HTMLDIR)/location.png \ $(HTMLDIR)/location-add.png \ diff --git a/lib/init/help_loc_struct.png b/lib/init/help_loc_struct.png deleted file mode 100644 index c4684ce5c7593237184bbefed8616e69928462fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 79007 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 diff --git a/lib/init/help_project_structure.png b/lib/init/help_project_structure.png new file mode 100644 index 0000000000000000000000000000000000000000..d1f15ee6696b31e378f87e29eb3302e098543213 GIT binary patch literal 117039 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�>?{mN)l0>^kOX9zzF;z~ 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 literal 0 HcmV?d00001 diff --git a/lib/loc_struct.png b/lib/loc_struct.png deleted file mode 100644 index 3c4e331a8c683cbe3bf84ec0a598b78a6aa50ded..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54377 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 From 1efff48a456acbb068cb5e31ea0e0f93cf0d03ac Mon Sep 17 00:00:00 2001 From: Ondrej Pesek Date: Sat, 22 Jun 2024 00:44:50 +0200 Subject: [PATCH 005/293] v.external: delete duplicated layer requirement definition (#3902) --- vector/v.external/args.c | 1 - 1 file changed, 1 deletion(-) diff --git a/vector/v.external/args.c b/vector/v.external/args.c index 7f482f836e6..90b73738402 100644 --- a/vector/v.external/args.c +++ b/vector/v.external/args.c @@ -32,7 +32,6 @@ void parse_args(int argc, char **argv, struct _options *options, "\t\tESRI Shapefile: shapefile name\n" "\t\tMapInfo File: mapinfo file name\n" "\t\tPostGIS database: table name"); - options->layer->required = NO; options->layer->key_desc = "name"; options->layer->gisprompt = "old,datasource_layer,datasource_layer"; From acee505efeb8a8b1ba7923bfbb754da6320ce712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sat, 22 Jun 2024 10:35:18 +0200 Subject: [PATCH 006/293] CI: Cancel in progress jobs for other workflows (#3904) --- .github/workflows/clang-format-check.yml | 4 ++-- .github/workflows/create_release_draft.yml | 5 +++++ .github/workflows/label.yml | 4 ++++ .github/workflows/titles.yml | 4 ++++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/workflows/clang-format-check.yml b/.github/workflows/clang-format-check.yml index c78f5ae50b0..da54d1cd178 100644 --- a/.github/workflows/clang-format-check.yml +++ b/.github/workflows/clang-format-check.yml @@ -8,8 +8,8 @@ on: pull_request: workflow_dispatch: concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ github.ref_protected != true }} + group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} + cancel-in-progress: true permissions: {} jobs: formatting-check: diff --git a/.github/workflows/create_release_draft.yml b/.github/workflows/create_release_draft.yml index 5a29ea7582c..11f806e2f55 100644 --- a/.github/workflows/create_release_draft.yml +++ b/.github/workflows/create_release_draft.yml @@ -13,6 +13,11 @@ on: - .github/** - utils/** +concurrency: + group: >- + ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} + cancel-in-progress: true + env: OUT_DIR: ${{ github.workspace }}/../grass_outdir GRASS: grass-${{ github.ref_name }} diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml index b8bcd2cd771..f1f9bb9130a 100644 --- a/.github/workflows/label.yml +++ b/.github/workflows/label.yml @@ -13,6 +13,10 @@ on: permissions: {} +concurrency: + group: ${{ github.workflow }}-${{ github.event_name == 'pull_request_target' && github.head_ref || github.sha }} + cancel-in-progress: true + jobs: labeler: permissions: diff --git a/.github/workflows/titles.yml b/.github/workflows/titles.yml index e27797771f2..b8e33c2fb37 100644 --- a/.github/workflows/titles.yml +++ b/.github/workflows/titles.yml @@ -12,6 +12,10 @@ on: permissions: {} +concurrency: + group: ${{ github.workflow }}-${{ github.event_name == 'pull_request_target' && github.head_ref || github.sha }} + cancel-in-progress: true + jobs: validate-titles: runs-on: ubuntu-latest From af6e7da9da7ce81fe2cc16c36b97bf37c977bb7a Mon Sep 17 00:00:00 2001 From: Nicklas Larsson Date: Sun, 23 Jun 2024 01:01:18 +0200 Subject: [PATCH 007/293] libvector/neta: fix memory leaks (#3618) --- lib/vector/neta/articulation_point.c | 1 + lib/vector/neta/timetables.c | 10 +++++++++- lib/vector/neta/utils.c | 18 +++++++++++++----- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/lib/vector/neta/articulation_point.c b/lib/vector/neta/articulation_point.c index 653fe859bc7..17e511e1fda 100644 --- a/lib/vector/neta/articulation_point.c +++ b/lib/vector/neta/articulation_point.c @@ -153,5 +153,6 @@ int NetA_articulation_points(dglGraph_s *graph, struct ilist *articulation_list) G_free(parent); G_free(stack); G_free(current_edge); + G_free(mark); return points; } diff --git a/lib/vector/neta/timetables.c b/lib/vector/neta/timetables.c index 5acc569009e..8911f04f8ed 100644 --- a/lib/vector/neta/timetables.c +++ b/lib/vector/neta/timetables.c @@ -69,7 +69,10 @@ int NetA_init_distinct(dbDriver *driver, dbString *sql, int **lengths, G_warning(_("Out of memory")); return -1; } - db_open_select_cursor(driver, sql, &cursor, DB_SEQUENTIAL); + if (db_open_select_cursor(driver, sql, &cursor, DB_SEQUENTIAL) != DB_OK) { + G_warning(_("Unable to open select cursor: %s"), db_get_string(sql)); + return -1; + } count = index = 0; /*calculate the lengths of the routes */ table = db_get_cursor_table(&cursor); @@ -128,6 +131,10 @@ int NetA_init_timetable_from_db(struct Map_info *In, int route_layer, struct field_info *Fi; Fi = Vect_get_field(In, route_layer); + if (Fi == NULL) + G_fatal_error(_("Database connection not defined for layer %d"), + route_layer); + driver = db_start_driver_open_database(Fi->driver, Fi->database); if (driver == NULL) G_fatal_error(_("Unable to open database <%s> by driver <%s>"), @@ -308,6 +315,7 @@ int NetA_init_timetable_from_db(struct Map_info *In, int route_layer, } db_close_cursor(&cursor); } + Vect_destroy_field_info(Fi); db_close_database_shutdown_driver(driver); return 0; diff --git a/lib/vector/neta/utils.c b/lib/vector/neta/utils.c index b5fe70f83ca..118e57b98e1 100644 --- a/lib/vector/neta/utils.c +++ b/lib/vector/neta/utils.c @@ -115,6 +115,9 @@ int NetA_get_node_costs(struct Map_info *In, int layer, char *column, struct field_info *Fi; Fi = Vect_get_field(In, layer); + if (Fi == NULL) + G_fatal_error(_("Database connection not defined for layer %d"), layer); + driver = db_start_driver_open_database(Fi->driver, Fi->database); if (driver == NULL) G_fatal_error(_("Unable to open database <%s> by driver <%s>"), @@ -122,16 +125,21 @@ int NetA_get_node_costs(struct Map_info *In, int layer, char *column, nlines = Vect_get_num_lines(In); nnodes = Vect_get_num_nodes(In); - Cats = Vect_new_cats_struct(); - Points = Vect_new_line_struct(); for (i = 1; i <= nnodes; i++) node_costs[i] = 0; db_CatValArray_init(&vals); + int nvals = + db_select_CatValArray(driver, Fi->table, Fi->key, column, NULL, &vals); + + db_close_database_shutdown_driver(driver); + Vect_destroy_field_info(Fi); - if (db_select_CatValArray(driver, Fi->table, Fi->key, column, NULL, - &vals) == -1) + if (nvals == -1) return 0; + + Cats = Vect_new_cats_struct(); + Points = Vect_new_line_struct(); for (i = 1; i <= nlines; i++) { int type = Vect_read_line(In, Points, Cats, i); @@ -152,8 +160,8 @@ int NetA_get_node_costs(struct Map_info *In, int layer, char *column, } Vect_destroy_cats_struct(Cats); + Vect_destroy_line_struct(Points); db_CatValArray_free(&vals); - db_close_database_shutdown_driver(driver); return 1; } From a24714ec1fcca3699d1b1d810c48ed5dac9f0bee Mon Sep 17 00:00:00 2001 From: Chung-Yuan Liang <77927944+cyliang368@users.noreply.github.com> Date: Sat, 22 Jun 2024 19:08:27 -0400 Subject: [PATCH 008/293] r.texture: support parallel computing by OpenMP (#3857) * refactor to use local vars instead of global vars * refactor to seperate execute part for parallelization * remove unused variable * divide h_mearsure.h into paired header files * refactor parameters and flags in main.c * remove unused variable 'threads' in main.c * add execute_parallel.c * parallelize execute, add tests and benchmarks * rename test and benchmark files, integrate parallel part in execute.c * Run benchmark on all methods * get thread id only if OpenMP is defined * replace omp firstprivate with omp private * remove tid, and only use trow to aviod no OpenMP support situation * add keyword "parallel" in main.c * replace the list of functions with the -a flag in benchmark * add benchmark for window sizes, plot speedup and efficiency * remove basename list in benchmark, add benchmark results to html * replace 'x' with '×' for window sizes in html * breakdown long lines in html * remove known issue in r.texture.html * modify the formats and paths of figures --- raster/r.texture/Makefile | 4 +- .../r.texture/benchmark/benchmark_rtexture.py | 60 +++++ .../benchmark/benchmark_rtexture_window.py | 62 +++++ raster/r.texture/execute.c | 146 ++++++---- raster/r.texture/execute.h | 2 +- raster/r.texture/main.c | 20 +- raster/r.texture/r.texture.html | 49 +++- .../r_texture_mapsize_efficiency.png | Bin 0 -> 53108 bytes raster/r.texture/r_texture_mapsize_time.png | Bin 0 -> 44780 bytes .../r.texture/r_texture_window_efficiency.png | Bin 0 -> 48672 bytes raster/r.texture/r_texture_window_time.png | Bin 0 -> 43656 bytes .../{test_texture.py => test_rtexture.py} | 0 .../testsuite/test_rtexture_parallel.py | 255 ++++++++++++++++++ 13 files changed, 533 insertions(+), 65 deletions(-) create mode 100644 raster/r.texture/benchmark/benchmark_rtexture.py create mode 100644 raster/r.texture/benchmark/benchmark_rtexture_window.py create mode 100644 raster/r.texture/r_texture_mapsize_efficiency.png create mode 100644 raster/r.texture/r_texture_mapsize_time.png create mode 100644 raster/r.texture/r_texture_window_efficiency.png create mode 100644 raster/r.texture/r_texture_window_time.png rename raster/r.texture/testsuite/{test_texture.py => test_rtexture.py} (100%) create mode 100644 raster/r.texture/testsuite/test_rtexture_parallel.py diff --git a/raster/r.texture/Makefile b/raster/r.texture/Makefile index a3ceebba2b7..94c584e9ff4 100644 --- a/raster/r.texture/Makefile +++ b/raster/r.texture/Makefile @@ -2,8 +2,10 @@ MODULE_TOPDIR = ../.. PGM = r.texture -LIBES = $(RASTERLIB) $(GISLIB) $(MATHLIB) +LIBES = $(RASTERLIB) $(GISLIB) $(MATHLIB) $(OPENMP_LIBPATH) $(OPENMP_LIB) DEPENDENCIES = $(RASTERDEP) $(GISDEP) +EXTRA_CFLAGS = $(OPENMP_CFLAGS) +EXTRA_INC = $(OPENMP_INCPATH) include $(MODULE_TOPDIR)/include/Make/Module.make diff --git a/raster/r.texture/benchmark/benchmark_rtexture.py b/raster/r.texture/benchmark/benchmark_rtexture.py new file mode 100644 index 00000000000..8015781e70b --- /dev/null +++ b/raster/r.texture/benchmark/benchmark_rtexture.py @@ -0,0 +1,60 @@ +"""Benchmarking of r.texture + +@author Aaron Saw Min Sern + Chung-Yuan Liang +""" + +from grass.exceptions import CalledModuleError +from grass.pygrass.modules import Module +from subprocess import DEVNULL + +import grass.benchmark as bm + + +def main(): + results = [] + mapsizes = [1e6, 2e6, 4e6, 8e6] + metrics = ["time", "speedup", "efficiency"] + + for mapsize in mapsizes: + benchmark(int(mapsize**0.5), f"r.texture_{int(mapsize/1e6)}M", results) + + for metric in metrics: + bm.nprocs_plot( + results, + title=f"r.texture -a {metric}", + metric=metric, + ) + + +def benchmark(size, label, results): + reference = "r_texture_reference_map" + output = "benchmark_r_texture" + generate_map(rows=size, cols=size, fname=reference) + module = Module( + "r.texture", + input=reference, + output=output, + a=True, + run_=False, + stdout_=DEVNULL, + overwrite=True, + ) + results.append(bm.benchmark_nprocs(module, label=label, max_nprocs=8, repeat=3)) + Module("g.remove", quiet=True, flags="f", type="raster", name=reference) + Module("g.remove", quiet=True, flags="f", type="raster", pattern=f"{output}_*") + + +def generate_map(rows, cols, fname): + Module("g.region", flags="p", rows=rows, cols=cols, res=1) + # Generate using r.random.surface if r.surf.fractal fails + try: + print("Generating reference map using r.surf.fractal...") + Module("r.surf.fractal", output=fname, overwrite=True) + except CalledModuleError: + print("r.surf.fractal fails, using r.random.surface instead...") + Module("r.random.surface", output=fname, overwrite=True) + + +if __name__ == "__main__": + main() diff --git a/raster/r.texture/benchmark/benchmark_rtexture_window.py b/raster/r.texture/benchmark/benchmark_rtexture_window.py new file mode 100644 index 00000000000..4f1830d08d3 --- /dev/null +++ b/raster/r.texture/benchmark/benchmark_rtexture_window.py @@ -0,0 +1,62 @@ +"""Benchmarking of r.texture + +@author Aaron Saw Min Sern + Chung-Yuan Liang +""" + +from grass.exceptions import CalledModuleError +from grass.pygrass.modules import Module +from subprocess import DEVNULL + +import grass.benchmark as bm + + +def main(): + results = [] + mapsize = 1e6 + window_sizes = [3, 9, 15, 27] + metrics = ["time", "speedup", "efficiency"] + + for window in window_sizes: + benchmark(int(mapsize**0.5), window, f"r.texture_{window}x{window}", results) + + for metric in metrics: + bm.nprocs_plot( + results, + title=f"r.texture -a {metric}", + metric=metric, + ) + + +def benchmark(size, window, label, results): + reference = "r_texture_reference_map" + output = "benchmark_r_texture" + generate_map(rows=size, cols=size, fname=reference) + module = Module( + "r.texture", + input=reference, + output=output, + size=window, + a=True, + run_=False, + stdout_=DEVNULL, + overwrite=True, + ) + results.append(bm.benchmark_nprocs(module, label=label, max_nprocs=8, repeat=3)) + Module("g.remove", quiet=True, flags="f", type="raster", name=reference) + Module("g.remove", quiet=True, flags="f", type="raster", pattern=f"{output}_*") + + +def generate_map(rows, cols, fname): + Module("g.region", flags="p", rows=rows, cols=cols, res=1) + # Generate using r.random.surface if r.surf.fractal fails + try: + print("Generating reference map using r.surf.fractal...") + Module("r.surf.fractal", output=fname, overwrite=True) + except CalledModuleError: + print("r.surf.fractal fails, using r.random.surface instead...") + Module("r.random.surface", output=fname, overwrite=True) + + +if __name__ == "__main__": + main() diff --git a/raster/r.texture/execute.c b/raster/r.texture/execute.c index c8b15a269ef..80514e6af86 100644 --- a/raster/r.texture/execute.c +++ b/raster/r.texture/execute.c @@ -1,3 +1,7 @@ +#if defined(_OPENMP) +#include +#endif + #include #include #include @@ -17,8 +21,7 @@ ***************************************************************************************************/ int execute_texture(CELL **data, struct dimensions *dim, struct menu *measure_menu, int *measure_idx, - struct output_setting *out_set) - + struct output_setting *out_set, int threads) { int size = dim->size; int dist = dim->dist; @@ -27,23 +30,34 @@ int execute_texture(CELL **data, struct dimensions *dim, int n_outputs = dim->n_outputs; int n_measures = dim->n_measures; int *outfd = out_set->outfd; + RASTER_MAP_TYPE out_data_type = out_set->out_data_type; struct Flag *flag_null = out_set->flag_null; struct Flag *flag_ind = out_set->flag_ind; int offset = size / 2; int i, j, row, col, first_row, first_col, last_row, last_col; + int trow; int have_px, have_py, have_pxpys, have_pxpyd; - FCELL **fbuf; - FCELL measure; /* Containing measure done */ - struct matvec *mv; - fbuf = G_malloc(n_outputs * sizeof(FCELL *)); - for (i = 0; i < n_outputs; i++) - fbuf[i] = Rast_allocate_buf(out_data_type); + FCELL ***fbuf_threads; /* Buffer for each thread */ + FCELL measure; /* Containing measure done */ + struct matvec **mvs; /* matrices and vectors for each thread */ + + /* allocate memory*/ + /* fbuf_threads[0] is used for writing out when program is not in loops*/ + fbuf_threads = (FCELL ***)G_malloc(sizeof(FCELL **) * threads); + for (i = 0; i < threads; i++) { + fbuf_threads[i] = (FCELL **)G_malloc(n_outputs * sizeof(FCELL *)); + for (j = 0; j < n_outputs; j++) + fbuf_threads[i][j] = Rast_allocate_buf(out_data_type); + } - mv = G_malloc(sizeof(struct matvec)); - alloc_vars(size, mv); + mvs = (struct matvec **)G_malloc(sizeof(struct matvec *) * threads); + for (i = 0; i < threads; i++) { + mvs[i] = G_malloc(sizeof(struct matvec)); + alloc_vars(size, mvs[i]); + } /* variables needed */ if (measure_menu[2].useme || measure_menu[11].useme || @@ -76,11 +90,11 @@ int execute_texture(CELL **data, struct dimensions *dim, last_col = ncols; } - Rast_set_f_null_value(fbuf[0], ncols); + Rast_set_f_null_value(fbuf_threads[0][0], ncols); for (row = 0; row < first_row; row++) { for (i = 0; i < n_outputs; i++) { - Rast_put_row(outfd[i], fbuf[0], out_data_type); + Rast_put_row(outfd[i], fbuf_threads[0][0], out_data_type); } } if (n_measures > 1) @@ -90,62 +104,80 @@ int execute_texture(CELL **data, struct dimensions *dim, else G_message(_("Calculating %s..."), measure_menu[measure_idx[0]].desc); - for (row = first_row; row < last_row; row++) { - G_percent(row, nrows, 2); - for (i = 0; i < n_outputs; i++) - Rast_set_f_null_value(fbuf[i], ncols); - - /*process the data */ - for (col = first_col; col < last_col; col++) { - if (!set_vars(mv, data, row, col, size, offset, dist, - flag_null->answer)) { - for (i = 0; i < n_outputs; i++) - Rast_set_f_null_value(&(fbuf[i][col]), 1); - continue; - } - /* for all angles (0, 45, 90, 135) */ - for (i = 0; i < 4; i++) { - set_angle_vars(mv, i, have_px, have_py, have_pxpys, have_pxpyd); - /* for all requested textural measures */ - for (j = 0; j < n_measures; j++) { - - measure = - (FCELL)h_measure(measure_menu[measure_idx[j]].idx, mv); - - if (flag_ind->answer) { - /* output for each angle separately */ - fbuf[j * 4 + i][col] = measure; - } - else { - /* use average over all angles for each measure */ - if (i == 0) - fbuf[j][col] = measure; - else if (i < 3) - fbuf[j][col] += measure; - else - fbuf[j][col] = (fbuf[j][col] + measure) / 4.0; +#pragma omp parallel private(row, col, i, j, measure, trow) default(shared) + { +#pragma omp for schedule(static, 1) ordered + for (row = first_row; row < last_row; row++) { + trow = row % threads; /* Obtain thread row id */ + G_percent(row, nrows, 2); + + /* initialize the output row */ + for (i = 0; i < n_outputs; i++) + Rast_set_f_null_value(fbuf_threads[trow][i], ncols); + + /*process the data */ + for (col = first_col; col < last_col; col++) { + if (!set_vars(mvs[trow], data, row, col, size, offset, dist, + flag_null->answer)) { + for (i = 0; i < n_outputs; i++) + Rast_set_f_null_value(&(fbuf_threads[trow][i][col]), 1); + continue; + } + /* for all angles (0, 45, 90, 135) */ + for (i = 0; i < 4; i++) { + set_angle_vars(mvs[trow], i, have_px, have_py, have_pxpys, + have_pxpyd); + /* for all requested textural measures */ + for (j = 0; j < n_measures; j++) { + measure = (FCELL)h_measure( + measure_menu[measure_idx[j]].idx, mvs[trow]); + if (flag_ind->answer) { + /* output for each angle separately */ + fbuf_threads[trow][j * 4 + i][col] = measure; + } + else { + /* use average over all angles for each measure */ + if (i == 0) + fbuf_threads[trow][j][col] = measure; + else if (i < 3) + fbuf_threads[trow][j][col] += measure; + else + fbuf_threads[trow][j][col] = + (fbuf_threads[trow][j][col] + measure) / + 4.0; + } } } } +#pragma omp ordered + { + for (i = 0; i < n_outputs; i++) + Rast_put_row(outfd[i], fbuf_threads[trow][i], + out_data_type); + } } + } /* end of parallel section */ - for (i = 0; i < n_outputs; i++) - Rast_put_row(outfd[i], fbuf[i], out_data_type); - } - - Rast_set_f_null_value(fbuf[0], ncols); + Rast_set_f_null_value(fbuf_threads[0][0], ncols); for (row = last_row; row < nrows; row++) { for (i = 0; i < n_outputs; i++) { - Rast_put_row(outfd[i], fbuf[0], out_data_type); + Rast_put_row(outfd[i], fbuf_threads[0][0], out_data_type); } } G_percent(nrows, nrows, 1); - for (i = 0; i < n_outputs; i++) - G_free(fbuf[i]); - G_free(fbuf); - dealloc_vars(mv); - G_free(mv); + for (i = 0; i < threads; i++) { + for (j = 0; j < n_outputs; j++) + G_free(fbuf_threads[i][j]); + G_free(fbuf_threads[i]); + } + G_free(fbuf_threads); + + for (i = 0; i < threads; i++) { + dealloc_vars(mvs[i]); + G_free(mvs[i]); + } + G_free(mvs); return 0; } diff --git a/raster/r.texture/execute.h b/raster/r.texture/execute.h index a8f07594fa2..cff9615cce7 100644 --- a/raster/r.texture/execute.h +++ b/raster/r.texture/execute.h @@ -20,4 +20,4 @@ typedef struct output_setting { int execute_texture(CELL **data, struct dimensions *dim, struct menu *measure_menu, int *measure_idx, - struct output_setting *out_set); + struct output_setting *out_set, int threads); diff --git a/raster/r.texture/main.c b/raster/r.texture/main.c index c21bcc1cac3..7ff1c7d9865 100644 --- a/raster/r.texture/main.c +++ b/raster/r.texture/main.c @@ -20,6 +20,9 @@ * software is provided "as is" without express or implied warranty. * *****************************************************************************/ +#if defined(_OPENMP) +#include +#endif #include #include @@ -86,6 +89,7 @@ int main(int argc, char *argv[]) struct dimensions dim; struct output_setting out_set; char p[1024]; + int threads; G_gisinit(argv[0]); @@ -94,6 +98,7 @@ int main(int argc, char *argv[]) G_add_keyword(_("algebra")); G_add_keyword(_("statistics")); G_add_keyword(_("texture")); + G_add_keyword(_("parallel")); module->description = _("Generate images with textural features from a raster map."); @@ -307,7 +312,20 @@ int main(int argc, char *argv[]) out_set.flag_null = flag.null; out_set.flag_ind = flag.ind; - execute_texture(data, &dim, measure_menu, measure_idx, &out_set); + threads = atoi(parm.nproc->answer); +#if defined(_OPENMP) + /* Set the number of threads */ + omp_set_num_threads(threads); + if (threads > 1) + G_message(_("Using %d threads for parallel computing."), threads); +#else + if (threads > 1) { + G_warning(_("GRASS GIS is not compiled with OpenMP support, parallel " + "computation is disabled.")); + threads = 1; + } +#endif + execute_texture(data, &dim, measure_menu, measure_idx, &out_set, threads); for (i = 0; i < dim.n_outputs; i++) { Rast_close(outfd[i]); diff --git a/raster/r.texture/r.texture.html b/raster/r.texture/r.texture.html index a91f2175b8d..93b089a86fe 100644 --- a/raster/r.texture/r.texture.html +++ b/raster/r.texture/r.texture.html @@ -187,6 +187,50 @@

Second-order statistics in the spatial domain

Importantly, the input raster map cannot have more than 255 categories. +

Performance

+To enable parallel processing, the user can specify the number of threads +to be used with the nprocs parameter (default 1). +Figures below show benchmark results running on +Intel® Core™ i9-10940X CPU @ 3.30GHz. See benchmark scripts +in the source code for more details. + +
+ time benchmark for r.texture with different map sizes +
+ Figure 1: Benchmark shows execution time for different + number of cells (1M, 2M, 4M, and 8M) and + the fixed size of window (3×3). +
+
+ efficiency benchmark for r.texture with different map sizes +
+ Figure 2: Benchmark shows efficiency for different + numbers of cells (1M, 2M, 4M, and 8M) and + the fixed size of window (3×3). +
+
+ time benchmark for r.texture with different window sizes +
+ Figure 3: Benchmark shows execution time for different + sizes of windows + (3×3, 9×9, + 15×15, and 27×27) + and the fixed number of cells (1M). +
+
+ efficiency benchmark for r.texture with different window sizes +
+ Figure 4: Benchmark shows efficiency for different + sizes of windows (3×3, 9×9, + 15×15, and 27×27) + and the fixed number of cells (1M). +
+ +

EXAMPLE

Calculation of Angular Second Moment of B/W orthophoto (North Carolina data set): @@ -264,11 +308,6 @@

EXAMPLE

IDM textures according to direction -

KNOWN ISSUES

- -The program can run incredibly slow for large raster maps and large -moving windows (size option). -

REFERENCES

The algorithm was implemented after Haralick et al., 1973 and 1979. diff --git a/raster/r.texture/r_texture_mapsize_efficiency.png b/raster/r.texture/r_texture_mapsize_efficiency.png new file mode 100644 index 0000000000000000000000000000000000000000..d6cc513df26729b534e1cf69779263bed535a87b GIT binary patch literal 53108 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 literal 0 HcmV?d00001 diff --git a/raster/r.texture/r_texture_mapsize_time.png b/raster/r.texture/r_texture_mapsize_time.png new file mode 100644 index 0000000000000000000000000000000000000000..3ec01b5a5acd5f7e6bab3316f169e67b74ae1a2d GIT binary patch literal 44780 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 z z`CrwzlwzcO(NHu7mDDxVEG$3vArh9kA2WiIw=vy-5A_D9vuu{;+z)kWMGXxSL|X$s zU2Z#!2LiPWBtwKQZ*4i|8#mDItqcbNDEi%>9(O-eX%4UyDgZGL07u9|{@`<6(LyyN zI5-5K3fVAQ`}?6`VO{ukXOKwlH~IMjyq_P|HBkgOH~^aNB&0Qgnc)Fkd(d~Avc_V3fyj7U@4mO2u%SG->9dnH2BOHL~ji(hK3j;l^n}QkK}oh+j@HmnimRi@bK~XD)~JIjkUBey}iAS z-pXY(FZLvx+1Nxt>5$_|&ID2d{J*%Y?1#8XhDP((uXw-xFD?v7FaZk4vc3G{7SRtO z1Zogq3_>8UKx&pdA8;m;M0H%7{@{JM#mr;UNMK-KaP!I7R*0Y`XChu4gCyq&PofIU z7vYo#OG0)#{o1gt5R{5NsVMfy$jJEDuW=A1yF$t=ep14V9&(fdH#Z7F2Ajs;X|An!Y!F8xnGfi;FAr-9H8d3f|zSlU(;)xRW(Vm8KpCSqP1YSw5bUF)q4W8Bqqj z3IQ`FRa5o=14~FqP|D8sGH=dBf8xE@dRAldfo}tI=+B?{+G^^$t1K+JAAk3~!%tMv z&OiH7ppi>Wpr;+6#}g)Ti~1{IsTyBw!`8NFBM?2bHI0ppJ9(ZrJCN$o#AYy~0`jvt z0%la1;2A^I#kn|nBL&RQ+{4&JX*{WTsO#uJF7P74%FmU%8n_;Sh6*6y(L~;dTuTxb z&m5qDzUXlB-gl)053r3^&1<#h@ye%H5HZKWffyEccV!qqnb%}Q9CD5@m=51hzV!46 zYIayX`u!U##s3D5$~7mylW)jIB^?li+r_;1x#Z;KpY1N+PrdaFT_A-3tQG~x57Yc+ zW@d*gc||VLyyJV7rOyaF>&GMo1-^DC!X~feHmEcOy#;IC`SftRBTex!m#C=d6N z$*HNm@!N3>gxJ{Fuy6E>&Y&hcy19jdNQ_7iN&44P)SBOjyYo!@U-;g9I!+FzPCMZ9 zXD9pX=4E=2pA`W%yGUP-Xji7V5Eq`Rm9=~t(^~c@L*<_Ezk+`Q$`yDT!jIEI32p`9 zonU;aX%8gL=FUzm*acqefICJ;M!fp1Fau2djxHr7C4n13@z6TaN&sy&-F~_r_rILC zQgN}>MtnFMM7#w#KsHB+S|l3B=I#*>kv?wTMB|m7(Nvb!aU?RryEEa z8H>DmVHEJ49RSYwVe_lK6Vn#4b6)&Pp_U?W2?L9u`PY}132)xu%|7Ex;kUp#KiyF` zlBVJ{ZjYuBj89Gu+iW}`0_>rh#MLsC^MI6M@9HRJA2rC2t91tljKmuzd=d$VPDSbKCjLmvBdHg0IRjOfTEc@5b}ygPFXgGW*ymkwiO-%rd;anz z+3DHY+~y`wo#4Ym4@9p3MrP3&{apEKU{h1m#`8W(N=oWmj!Xa<5g7?8FJvCbQkgbC zK7p2tgbzFdF%?kFftTV}1#)ugtnnB)lEjj>W1(Gq)6)K>#kY782p^(zi5kMk` zImJRm%9%zvcy%4-ps@yR@x{_&up>`}iG=FPhE;`JZLW6-3c1% z!Cz@t;81@Ts9ooVWT;pxMq1=0;FjE*MS&WvTVo~EX{m{(3eLje)|@hEyicBR z{c<`IjH@UvhIuQV*4p?1-!i+fkm>EaH7gr5dLpofMbzBpK*hu=@B%F=eA&bna9~Po zTwGk3T!xH?$1RAK_gb7l@d=@(p`qH%`C%;r05-~UCbEE}Yrfqx!v!}r@EGC-20-wD zOph=ZAON7ov;mKaDiA;a45qUwAX;T*WskYnV4pd=^ZTz~a^?>fI@lpw z7eC|fG&78P*2%Mwl6FA6{Q@-Da=TF?px|EX>gbeP9x*z8eo`=e1_=)oVtDUgzZ}6N zv4>Osf??>1l~vYB850iBo#mk%vNs|Qg?7aF_(6aIoV)bFy9?+NbVtRfrNsiha#R@c!{gW=(|gb(3pOy6SXQw5|r2OG8!>4@!EI= zKG^;PcYEwX=R_I+O06hFT%Rgy-oCAeiS=Q&o7A$az1eJe^t|R;%(Q**e@e0Z$FW z2?GTh?ymDHc~l2@0iq02K8L+xY1g>8)HYK=&YJ_<6f}k(1-Rbh;iRl2khQk924~!| zy42WAp*EN`2cX^ZKd(3TmXSe$42CFKh)@fpW0+e&0_5R0!cT2KMw+BqDC2A}hz9m% zb3kGKki2`el#b|1)zAundJmjJug2*b!WjV)An21szaO@J1}zk7skcaP=;Wie4&4VQYn%r@t25rT8zGx;(w3rl8)Yue&oTxcf6sZq4v+`2#}K{J*n7R=QQCoJ%Q+e3IO+4l%xST* z&o$N6Ov1vIT#T$|-z?EbAStDt`1K@l1L3+aJFFE=+wzXNvMOw)U-~{EpOoZeL;=Ya z=!~VuS65e`S_LlW~vUu3AbM{huBibG5<8yg!(XJ>inEJ1z)iqTZnBzQM*MUBUJz-<&k+AsFp zv2KVT225_=V=M;fxw`rm(*FU(Bm{J6{VMwygxCEM1mBxT+ zSpe<-;nM=p)THf_?5XrSX>X7 zDKnMQ8&Lth-O8*~un1ZWa5YKScM_Saa(x?9B$1KPh^LU4l#ppEOm%NcK`myRNlckA`gIQX;|lWx5y}Ocz(j6sRIgPi{w|6XtbW-*P{jaf+i+7 zqN3fB9^a9{YTH12#3iY--3LMYKf#T=di5%Gd4DM_lt8JIHNl}gU1Bg8kuZk$e)jeU z9TNIsuz;auyH7*9-1_r5-5fMeL^LvTOKc0UGN-%nGkt{RKz5l_QWtz0kbQv0#nOoe zf>sb66ZAVmk}ZA{(aJWX%Z12i>^-BK~n|t19S#x8aeCeP(s`z&V$+CUteBJ zdJ7|xEByF}eiW4DC)4#09c~{&BM6cV{)t9vu}?RRNgnF|1M%u-9iaaCf{xbq@y<)6 zxh9%Xn&Gp0`ba~S^U}R16P1LBvtH}A9tnJ2E=v>mq1jOO{l`$j*}#JbE8-8sMm{72 zAZQH_i=fuNhwKNEmk0P%pd0boPu@b3`;6ZSH|jl^wRa!^NE~1*OzZcG#igaT>1pH5YLGQDJ$HVO zfu@CGVrR!57#IL~TH&G8ylsql&0FdcLn2Q{zE^ebtj;Gy)xlvjKvr@)|MsORp4aT~ zN`064g?0`dR;5E->0+*Ux`l>dK*bn)KYq+HKR=Ivlf)akE8|Oj@1lZ(F+oH|8pEKd zB3>ZSW2isRLH}W8VnP+w|Hb>Ld0nT_Fakcl1!R@CA`YRT9UHK@|~LTphq@kZz_7NJ>_@bz&CH0U2x~Dm%LcxJQS}nQ_L)V!33HQ2sB!h*yf?FC{lZ549iYJbzANprbO$Y>K)O1x^IJxkCW~cMX z(N4cKzvo{|*zNB@5k&>SS0I%gX{rH+?9v4S0pbrM9TO1Rp@{nlX&tL3Z6yG$hde@| zn4S-nqMFQNp?Vd}W}2AE8Ahjz(vY;st6qHsP0Cj}E9swyg<*#hGsc2DA+=KvcrN44 zOEwSS{@|em@irdL*ZAt|>#uOLsq>nhuBZOS>dRNz*jho80CqZD zQ2{m)B3i)xwxfjw?x)}rlft^-lCjCKy-c*pUx|&Hy!%SNn?#1?`QNIU_rA9LtRTBR zIoO!;nxFtEIR`t!YX*-F8cZm;^a9%36^snPS^*~ng@Sf7IW;u`Vf)E&a2B~9mSTdc zRBK^3k{^Ls%rO&?8P}#8uAmUv7RcB}NCgq?O{BBz5_C4XJ_Vs3@leyf)1Xra^hLj; zMJgzCdEr;&@&oOC?| ztBDk3Z)o&7JUVLm@q-L-zt{7;t3VCAK)gjL1vK1b-g_s8be!@woodcMN&AngO;^$8 zxG11kS#T!;^TF^qrOL?dj*V4X@&6=4NrLK+xq=k1l9E-IVuwF6#Sj;(Ecsb>VMT!v$np_&)mI4i~YRwB3TGv9} zzi922Zi`AxqyT_#JTD4eWc~G6s;a7xzNL1~9+dz~LK-K0Hvg$jmy5z8Ayt{Nq!8## z)v~&;>R8%O5RFDK{u7coAAyX?y{(f08a;^G(Hi&np#5MTao+X1h8 zHp0WzenNBc9z(;9f{g>?3o|~`lv_Q&{o=hmW#7HVS2(om^do0N(gT z73&vYxrIV8qV6gvEG~U<|D9G4{Do5r20`0u29jpg;thGiv^W)^=LcCuH%9g! zpLFuT0s^kpMw8zJ3L9B0Gjp}~FC2l)57fos*@ofZfaE;DhJpezrEJOV*7!e{qdK7V zD737stVA{ECWu@SwC{qLO3Q*)<`v1=*yMgMTEU8RV?t~4&|I3gf+1kU@Q<29%TxHM z_@lVbMP$&bZU*mSy(b@rj&gEa3*H)PQ;mRr_F{M%=|8tfIQai^ixk+R(DL4tFbYtk z;=f3i0Al+}h_ta>;(k&3lG8=4&hHG2`Cn(-*f;SMLtvB5_KTDwkQyEiPYiGsAceKm8RM~~b@-2%uD1>=n1zlSLtP}{>Xy2E4 zKl)|&_l6s#>iQs2XdRJ;!~fFZ{|Pu{tzZ)UBg?r zC-W}N*lYp5GDQrYWNz5=EI#t%SXwo`JL6PAJb#Ci=r15Yk8gHQHfG9oBAO-<*m)89 zBNgAP{7kYh2_?NV(Lp}pw1Hhmi<}AP-&7=Aw`k5)=sbxFs~Mvk5XnqPeQU^Upalka5+v}( zlmq>G>=cF2*9QF6DZXWLoh~>u=*m|$&iAjBU%Esgzy_a+ywoz4>mA8FJ$k|Wju+0V zoR(^WA|lWaM+OU^lz{O-zbS9o?QC_9@<2+}ejb?Tr?sv@-Q##6 zkNV^i4f*lckf})=8oNaORbBcF6U@S?RjBZaW?{})EFoP zyL@?Z?Qnm>Ax3*&Fhq!0{#S)EUA;F+vLH`piN%5w{Xc!woA{UgRAledi8-x2vnG-vex_wKzW5j00_1%P_%8qpcoG=_Ugj8oJ-D z6Zw$nKh$udr+d7S{=4lBOEu8IA6NRs$Y-$y1O1t6eksL2LzDwr9N32Q9?EyN5B z4PEY+cAQ0lFIM19Pem05-CxNL0~cF5WmQ{EH!j)|yl=;EF`@bO`7Mg2SvgB9H`Kqs zrxyHo=(^sSIeD77(7-YIz!nP(gAT z-_OXY%}&tmob95YNL)s7KB4Tfm+?pWpl*Vg-osZzlMl3G9)#o$Xf=ui%(gr}H93iN zJ;#3Q?G4V$%bU(0Ap;RbHeJfsr3grev7e%J*W;Oi@aoIY$qv+KC(oQ8FOKILc9k*S z#361n!CX9_$@uamr2UgpO)QMqGE&VJ>ju(|!+x(Fc%cja&vc~To13#h@pXL)&|Mzj z1ZQopK!WNb^{^#5%5?i@$D&l)&;Tjr^XD>V%#g#m54IuisU<+jb$2UAGf2_`D3S%e z)3s_pjmv2h#4AH1qit|eWJwSj01cvq)S^xw6K?pIh`eb;^G=cMle!i4!{)PzPpj(S zdISZ9_J##;;uIE;tw5lVfkF*VA^}(cQ~hAJVnMF8L~LLHV>nb)R5$G>spUfnS_~W- ztbTnp>D9E2;Q!W%LN!Y7_}0YfW;&B7xVdQ<)rWuh_|b_GiLD{T>3|L_Oj(Zoon5h9 zXd{~go^i`SWY3hLYeEb#gnKKRKRAxpGjev-bhhBIykmkx(UCpynHROOb`NTxK3H6@^saWEaL+(Hwqz4IZN}+- zt`h+vngYu&h1>ERoSaq9kV&`%lQ2?XEaEZYwO~JOv{qY^H`&QwWI$!z*hu2!6z{#P zQ4<4_h$Sb~XFgH|>KR)J)NB{Nv|N^U;)I?$1L|E#h_^WeQv(h>YV>EO;F~cKS$$*L zN|Om=`-QRnjZ6P3&is47t@o3i;2#TI0%>OU3)jfs;6;wXSN*it|MB2QIJATURf#6? z(6svBCBx%It^EVUe~$iy*X#RLZKX}J^+rab2mkd|BZiu)-5FrTBDHmaLKU-^UBVl> zv?vg&{Z=@>x=#LOYV;`_@S+KV$sz~n6<#Sc@geJhswKI#>5s&_i-O@$YR=ElP{bk* z*VFlti9lf_;ii!D;i8HK%&xuSw5o}oBcc62m1a@!ff7vXFz7<@I)w$_$Fqb=wSR_J zli%mDcwJK?`+Ej2P%|Rc_vEV;0W>S+#-jDK`%rH&E0CD2o7inD?`0cydX$16_u}7N zB?iO4ABvrPcxNE~E8D5~eE!&prtOn~NO*pMhR1I%tuJ2|1+f0-8w1mN%?p}J8AKID zu{KQ*v&;LR+2tG7ast{&?h19WnubMJr$@y|o<{B~J?Ce4VetQ@=t{!xwmtt1DH5$m z!FTLfK3viK)ai(d1CJbQbEdS0@#EgA?As(PfTTz(S$H-lz~B3TW|+DqagN^jvpJi7-}MyoE0 z&p)Ahc)3J8E)c=H+<};!lEr(|IOsqfCR7_Hw3--QDgbcN$Q6Txv}F6Ob9(s;*b|&z zXMF)KYHMp+5S@ej^y}9vzbk0ad9Qr+g;Rd_uE)@pOp#XuiLW3nJie>9IR$IINlD}X zNkvy*dwbEA`yfli)^uenx6_`Z@Sa@^V5KaSD~rkj3z z&p-vFzNzkTR z4+zGqdMB=EGBQ49lv3Cj$i!NIi>!cvZ8NCzRETJ;NiPm&CD<#;&WfLUz5)1cz>;Hq zytQz|NI2aDR2wB@eU+*X-inNsEF~qSyZi8>;SHkoIW!(&`JW$sCe0Jz-mLH;7KkA5 zwqI{4STn#{pC~kuZtf-U3?q`wmLURRG)2$$2#u&c)gX2I$RHGF;_Dj!D*HGl~jQW22*ZC@3u;t$5-9%H(9FSp2 zkRieR&pM3rf9UPy#lp?~t$R3y2#T3pz5G`z%AVsy-TzC?{oaUR`WGr>#mLDYhiM5e z{c;@WqQ}9*Lq|dL1{rj>C>8dV*Z(-B*Sk~T$hXK;Xi3Y5^Dk`@|Cct04PXXLeD^3I zM4}MIa`9^-a9xo0n=a19q5sNs?XOzoyI!KUE5uV)E7gk{m-zDR(%PeVzi+FTzdAEWQis}nh!4lbzSo*$fd+&C8v+OwOeqf!8=GCC^g zxCJ9N83+Fdhqy8;)BSppE7M+OQU{Qx09`+vv`D{HT&&lb7>1YQ~w)A{U*y zgT&=)yot%h-I|Z4+zAJHkwj%SM8gHW^EV%VM?niA@97_Cfr56|n1_XTyfXh>DNg>m z;Bst}TI1hodL~tKH+weI8A6>_V1+;j9QuJecL$3kjrE5r+}mztRVA})=sy0(-cQ!b z2AIzNmp`~~FiA_HC4D0-3r|C~N%qZ&oAKKOb`k`Alzrj-;aw3!WTAhwt_D86I7bCU za~lx+0GIGSMN5EU{2fc`1#v9Y)>+CX;YgAEV;rW0*@2=aIRg#TVn2u+PH`+QbX2e~ zUPxZnjdRV@5Z3Fr2?jr>yuD%xXDt7*h;-Rwga^T~795l*o3Q+A6Ka&{z)wXIChCtS zVNFzZkCdF$p*1B}ic`Cplamusvp^jR20aCMda9&1B}gnozR;h;eEm8uAk~g(RTUM^ z{kCta7c)%hvf0;*BJv*UKHKV(4cfJtD7d09qQfEKj7S|pyTD(xermr7NCX`b6jd`g z+l2^&rKP1zlu@LVl%cG*jeUAJ!J7qf$I;O-0JwtR#o2**7or(K=0ciF&R)5(AFap$ zSULN>iQyp;l|^AJ&4dG7Zz1ah%5N?n?BO>8R(M#1w83CMfzNq+bQ(@I z0_|7}SrUhq9mLdt?rc*tv+dpmB1S2sV{a-BTJ&UBL@3T0PTJbWG46=o32&gcihs}X71UG02;2@8rN;9c*dy3eA0L*#%1@5N( zgD?#J=G>xH?M!8QN#D+x3QUN{%Wrb1r~Camhr-QS2V35DI5(udz1@8M^SyV#l9F3G z|9}b$XJ{k9TuS!1vK>mcQzdmJ@BD8s0MC#?UWHeMS=tX>3MjhKPx&cP#k;MVkAnyOzsx>d!zM((dYQxG zK>qF9lfg4UP}%j(_v%{2r>~5b6|o*G-Nf=B_Q*N^7#mxRL!i)- zz*^m8dENHj8K%dI4j;mxb8ZUip%5!%vdN$EU~}d+a_$v6VCwhB?G}ZNp>qh1y&2n| z=~}Ub^ED28fUsJkr9Z5C@aJzIwG5}?NUdx2&RiHtb)7gM-E0+#;xstqh}adfCt93| zUPaJL4~;PxkRgI%EmXw3U6d^9I+c4=+xG1yOlqa`DxcnWpVALsU(Yz*6S)eFCa#-l zXq?v|77dYl;(H?Ob+AEbXJN9AHMF zU^f;Zl<<>TzUb78UB*Or&NaTeEfF#V{{ zQ6MZjSd9dSDX>frn~vnZEGbe-k1Ypv`+T5oMdF&=J4+Yb=M77_>M8rudon^)i{vGYgIr-VY zJY5mho<4(p*$bW=?Zo=~p4&#v(qA~Zj#)=A5cY%Y-Xon}t~W;C@_&|DZ{5r1XrJ+9 zk7zoffBmhz$oTHhr&V+AV%VFsayWpUqhB-RhcS4CUsFvg_C0Y!j+3=G7MDYd&OXsz z#B8-)pV8#*$X$tq15DLVTrJkcgyS{q$=uDaUZ{06orzB|`l@XYT75h_bD+ky@g!mn zCNZy$c^yyz2Q}bqsDhQ%H8`&&`ovyKQ}YF8KGR!z>9?XK&WH8e&S?{8bT_OEsfTt9 z9`#+dn7Z+5<{V@4=!l$GU+rsV9EsDoO^_Eo^1xfsubEr18GYixVt)btLmAlga5`XW zdK5aM0*%s88?}STicg(GdsI}?ELvm&TAZ9_t8?h=z1`T96>0l;Fq&ORbDGNu;SY&V)gZv zS)mf$w}#KtJzYhQzYx*OpbOI(uw~v}G#0zwuTZK*3)Lw%RTKNfj1aPyOn>Y6;L|3&MChaa|njC^YerjThxdAZ}4Ysr{p zn9tDJLe(w(#(gIJMEqyA56l`WMQ=n!>yDVG31XYL3R$Tp&q3$Eo*fC?dn{<4WJ8kq zgPwinxh#Kir6adc-y67tlC_(Qq&BDsC$LKXFf!S8jX_lBZ-O=trp+%O;T9-FIaMqN&>^}Y#o zNIfa;A-}yH^iaAvo7S^nS14C3H&9c4sMPA?>6rsZMutn{5!x!T3*qmj zX9_v+n$Ff8;=FpVe@+!p8xD71Wxo1cktWKcWJ~e+hjllaWJWR5XPwK*XDE?_V-p44}pp#%vR^;_r#=;CCnqVJ=!U{bE9Xci#<)4 z6_i3ZUzK-VsI+s7WFG8l_>LM4NgXmRdugm*mc!xqgVjk30n({WEXiR&p!=R{=Lq*QG3N-UET5Dy*zL)cw$J$*`+c}{&1FW z35G9y3H_$}S<|I2uQ{Q@^}Ptm(v3rCY3?kEZ)!Mw6Y{XN3o=TAY2&{aiqrA_c)E9! z)Evi5b(nI$a*fbU6g6hu&vw>9#rN^^!cSf0FE8wtr)vbqtYB@a+pd~M$WoZa&emQZ z&B)wYri#O=OEvC%MJ{0MhdWWTH1A;hVJ&pAyrEX=zZ)`<8{B(#4RC~l|H^*D{w4JL z65Nsdt+TyJN@=L+3^i$Q>wo;C`Imc>^jH@14h!TKDqFn8(9|p62aa}<{fXQ}!yb^& zZ`QwT4`V=o8Bv+2e&1{2>EXLRQ(ZR5vjh-vq3z!U?K|yDckWEFuRR_kU2}>xZF+)c z(=QQ2OQG0*oAE*Yn!d#Fa0`zrYzS&*G*trJw=JD9q9W*9pLx&8f7u>ezY~2K?OJ5o z`=!M|OJRFHZ+Wtk`@he?fH5(bNw|w9{8p|Md9t)~+#h6gFP&g}+cMuX?YkjaR>5Rb{l|FQQ?ih(o6ThF+m8ojVm6DM46-N({2POV|1&0J zo+bxVv)T<7eh!yL5$8z}xQTXIe*40_u$auZc&|-YTk(sK>NCH;HpM+iXhteGUzYF7 zrhUk7afxKPV{jnIaG-?7+~ST&!<+9R_ygi%+4?1cpFXD(xjlsNSHi{BGuJar^raxn zCmWlnOimQnjg5@Ydad^HbVJtsVwHWl_TIw9$qX!ak;s3`JqJ^3W~M4m+x zQ-+MLapL=i$oh-*a+B z)!3l5@RLys&-&}$hYv05ODT6!MGtfHt{c?8isd#4{I_dJm0_9OD1$;6-yO52rCbo$ znAwn5P(5qwyyEeALcZFk&@|ayOJ4!=Wty?#VQJQ~k&)KaGxiw&6Yu}t-U@RnD=6s3 zwpwV|^^SciBCz#`ciQhr-9l4?_3mk6je(mlHk#58PyBIZezXxkc-G&rT$n&l1SNrh z5m%2Tn;`c&g`n=%d@EH7t!Uy(p>5+q-@#$W?WXrXe$cG_jpelcDWG}2e@o>t&O3+3 zJ74}@*%lVT{%_g>VA@zL6|GCW=4{WM`~1yEdh>4>cUct~b|yt8NOoP{T`RlezerIY zLH2BO^3L{$4wp^Zqn$5E{pN(Ww;wC6ii-R( zgm<2vE~Go3OjJFX(Pmuxd;H1f>gi!u`;5QzV_NRGv&6`{&CZ!0WNTlM4-kX_C`46s z{#~xW?YW#9R{VC&$MAqZxu`Cc3g4KnWMW-`+dySSI!ytW`18)XJ4fvk=5H6L?B{(6 zk~SugHfCW1DTPF_8})YP2|HOG|NnS<%cv})u8$K0>5}e{5b5qt>2B$65s>a~rKD3r z8Ug8U>2B%n2B|ss^FHs)tXcD6K8=gD1ZCaVzRuZapW6HXOLFmOz8jfct$K{vru@>J z`E^Q>()4%;A6yfHk<<8`E6NF~1FoZYG#jJ;iSvkaoG9{kl!@1f*Ix zvq(nxwOf2ym5$tBwV$AW@sMUI{PDH%Swl7Z>D_OS%bcRHo89ap_vZp^W__uLbjQFk zydyS5+?N-&j+u>JMKdKlr( z4G!D`8lmq^O}5j7Bp$2fC^1NY>ZXi+ zPfx$uh0NSz$CVHt-(~oG@ts`VGyJzKt>7<3C%fD}UI|G_6-~`PAl)$P2R`T*NS<56Uf%3zKVZAE ztQ2FO$Il?_^Ve~zao_%<)xdbm1(o&pl0&PqJuOp#Kj@>*3wBNFKyTE5=jQd_b@ATa zeCKW{S*G?pw>#zU>*Ani>iR$l&HYw_2iTM}-2U~N;5ZJy6$Z(;pf0VG?Bz}x6BAP# zC~JU2U%@!R8*Pk|*9_|{K8TTHubG*o$GmuXT%(~aMutghPK`zaIprf##H^*?4{*XPe4w|0j;v^g_f#|Z>hx^6N3q6@~zb2OzfB9Q}^ zZs2?pq<>xc9s}9B9gw_?psR53TDafs{3Y+shqkh;qy6b;&I}u_d_-1P+R)smT^TnM zWfaI@#;N?+S;UzQ*IaAL*3-+nUeTQLxDHPx^avk4Rk#jj=2bY zKfW~HfT&$pS0^DYO?FcUq;Wt`j6Z1GKcM^F!GP@ceV=k5M)l!fa`X=R`L!RE$jT@5 zwzy{wsyPB?4oTDAVVtt&&m`~qv*h*NNLKxBA0D?1g>zRpFW$w|(_r0z5c2VX&6>Gg zVjxfsOn#8Yuly)6krZX6cT3kaM|7kL9GWUQnn1DUq{oive?5VJ_{TH~ofR z7Rq`cUWg>eZKv_*UuF+(^0uEVypmJ;TOUewfNueHj;@uhbDc zy4bc-QR!sT0E3-7I-Rr(Bu^3&5~4lmii>v}i-(5&86C9%RV1yI;DR63+UHL+b=8%Q z%SiOULCU+ocezKVZh_d?6{(+5E15kr8xftYo$AiI{hq#(wOqmwHW9}91itCQz!Hb- zx4ecdFv*@_3RBt7xw)tQ&Gj$1jk?jk#PRPvACww>2fLgNj7=J&Oz(@q#P8qLw6*6S z>c9;;7?>I4nyx(N7v2re<}UfD$CvNUE81bC)?TIMD`YUn_A_}#uZ}#Ce96y@vk@jd zUpl^$WF@Q~%U0?`w*BCBPc4SW45v|GJ4N%SqBS;;9&h581!~g}4x|T6mZwg@dGyATmGkRN3NvJC`o=1m38d$7P2 zGs)$n-fuhTwLoMh{K-@Chzta*v0%Z2;vc2un_QI@d;0~=M$DSOG zQae6w^c@VD?l0&hGJW#s$#aM1pu`3gB1C}7nfTC9ILJoc)z$Ts6>M6Kfn?*d^Ozt+ z0}PVDnBjTwQ?#tf@lOx?db8tNy+q!2W%uo)>)J8m7adcvTFx~1F`uu}V}6O+atzrs zetp}O2BVNI+qb1-;wTEfw1%a2rP-a1v)ub>wX^!)lVD{YxugH#-fubJa#JZ#zYuIE z_#^+$QMzm(K(>KU=mKXQXZp2H%+E#4(yp`Xa)MaLq2D{P*}Uh-HZvUFp*whe)rvBd zl(zJ|a`ENAt^ct!6{Nq2xIZ{nbiS~=w_KI$)-cZF!8S3cFV};km7~odTA_@<`WJSa zl+ety5Z)HtKSX;T^4}f(w8F_=^Z6(H$rZM_gq3m{`%mTAGpCaN1@<7t;1=g@^;RAA ze^yH8UV-|5>n&3Gk`iA${L9dVsdnB8OU1}Dz*2cd8LEJyv43f6;# zG985bZ{OvuT;R5a%ix#Puj}!WHKhgV&!n*N`*Kw>c_y9ic;i)`Y&&}RnGXJ={?B^9 z7Ar3#?2Xsuh`g^O@?hK(HH-KBEH1uKixFwTs-EcXJ4(MdnjiwlP#ML*-B8t@wgxeC z!Iul6uO{FH zemewgTrB}lO^3?zG@e5L!Xxz@a)IiI?rd4-D&^%PSeI9Q=pbYi+Z=cBAu`;st4)6E ztz>{uZr)j8GdvjaXFX)daBVc9E4g_ITFlI~T3|{yx?7M*HCN84P3{ zYZVdUjvQ6o{-Sln@5RK~{*0O>PKxqI4B`A3JV1frOKtk!4sIu=w0xvhVWxncJR>d6 z9ei~daB#HN8^X-S_?eJXpA&Lu)_%8#$0Ilj!V~Ru6^n^OQ^b+NY5YZ;pAqka-PNe# zqE=<$URM-t6g~y|5T1J24#qZAO^+n5bg*KGBQp6Xv z%Uqj>acthvpOJq!)hdivk`8&OPq5QG(;6P0nj5`_R%&l*;hI}S&1K~dNra-?U%ul{ zPM&FKPTsY@^taXQs@~T;eEOaU3t5BU<4_sBmXVJ8j*d)p-}I+5P}pan;^1bJKfppv z?;lDV!CY<=@tzX11hcQ-2OFHP*l9h&J1;@2S=V}9(Z+Ta%fo?MxM!1pzX$3s=BSB_ zN9g{JE{h@haNjRn1&TiZP6b^iY0 za$!Dmo+&Xijw=}IHfm3s<)GaP+iKK3j?JK~-0=1i$w2RlaF;q$I;;o?DBjpQcs+0( z@Ew9tw6-Md&2v5$7k|DZ_12-0px1(*$J>2flH>XK^p5x!S2~86_f*P4)Td(9ULjeU zpg&!yGFJ@(cJz*$nU9`Lo&p(3I8k}DF#HjDITnV&W4q#v4;UAQK`C~L4ex_Ai+D0d ztW16Xd4JCPJa15+>0|$(-R#H+#>LhAKFo_Fccj%9eq+l_$?5Ak(+~0;8U_KCd4UyU zLiyW`yL(KP9c-+q;_B*EayVkqBAZUkVu;!>9O^OR8rVA){oioyH(LXxekavkXn3&W zDav)2;k(1rHYvaAPX$1Vmd0rinSju;VSA23_5=htdOq++|}{rP-(A9S|30zXzeCmh&CCAK&L9I*m;cDO(04b+I7uVn^fsq^s!Y@NyQPimA|oo&Y?|FRf}kO{CtFrx1ohC zK;7#eDfh{4UgT2vMP~QJ8+#@0J?ooZGo&xQV&UZR{?z|CiDM``9kwHNWV|(}C4do? z-YUJrTL`Lob}&L=@d8BIf;e?&jEQ+}=l8DDGR|RT7H?Qsz2DBsx53rVjm#++Uh1q^ z+A%E+U#0B#ESd!A|NT07^rdTOl-lP0bL}0^GGqCjjm;T0&cyi*KZo!&_UM>!SQcqC z7ER6~+~2Ga4AUT3hKb5`8{%9G7_AYR+`PfTQ5*Qf_6myWGFE!d&mFtUScRK$e}Y?y zl!t_Rb`K;*1pE+6aWS-Wu7-cG^D?e;v=u)DPR6Rv;*{qXl!mB#(Y@KJ=CyP8H{mcz z7s@QQO~!CtaZO=kKX3ZzURrvd{C(5=#Yb0DP~o9D{fvXdC_45Vubx;m7G{-1duWXl zzIuq>S<66fis!$ky-+T`;mRxs%{mRtlgLJ^x;5x%qvgG+8mgO=oUD5m^_$(kh zXNUfA>|?~@RH)mZZT#diSOo`z?!jZ{OM_l7aI+E0OxP~+?yP-fD}0b4^H(A$=B&mC zGx_Jc#>hykl`806uhY%|q11krCQIk=j^UVU%KvWzZsO=NWc75SQya zIQUuc>kTbd5pX^?98oc)aT#}H&mCS)WH-L5tATd>_Zq*#2Q7M*qD=d$Xjx@c?lp6D zo4cU>2^W+d91&iDK@aMbgVE>dEpjb<;;cWii(K|y=d()xZY@0JV^A_EL2Qz-}?38qm2*g%d&1nUI)pp`KPfEH8>&rf}a>K zB}2-Yu6L@VBF*dOEOKKaRql_ZzY`tTLjv*cTGlVSJ?kn`y-|_b@k}_Q$1sj;?F%-a z{3e==VFdN+1N*Vu#Wh$hEU~^kdq{vv5MD~kh7cqQI5Vnc@vwB*GH61Xn9YT;KX==< z&FA;V&$aHL5DHgw6Y8Bpy#}0^rXE2XBKw4cH;&x}l~$F8i+Yvss;2>?Z&K( zP@~kMpbO7`9yHHGr`y!vv8@@I-V~0?dEVouwEiOzx}t7+CGvEie&V354=**~%Ow#; z7EkT#s7=*XjWOGh%JZ(jc*Um`@M!xpX`Up!6Pxu~hyK3r2E#&)1fNij@II15o&znl zA?Q*JmUB7Z7J2wq3{E~iQKuslvf3cMIGZ!cDaw${hj|g-JvZW)d-AtcNz|rui>Ut_ z$7P6J%5BwsDS5^dT7f4%n$i7Q?T=yl;cF?1kdis|aXRm{ z3Ce3yzCc5H|`nh|E_U9xK8zILR?u&yqEf1&D zsA<wQ(Wg>Wql1jzggMrU@JN}Jbf0`V@BI6rB|A6$%{TMf0u#H+J>o4{L{e2p&j+B5n#k_B(YVXsbL z!u9F;2)jg=Z$V{yt&W7#DsuE^LSEdnsHAA-=l5+VU#9;k8Q=1SX9i;O3u)n!*<(vm zP&0(w?_W0ZJ=h#c@h6cask>@c*E!IS?Fh^HstJ9Y{Se<3ul#kLMO+;_wRR1Dvd$W8-Zux1{c9C{3O37u_u`u~Jj#h7Q2I79*~la6urBrQydH8P#0*6}bwU zgGF0bbBlG?#xqTg_0qB*hxu{V9{;QtS670Ia-r4i7-;jfOK+1NL z$HN4hJ>l4HQEf~s>DarrGQdOaoe?{U&qZ_E7aCt@SlJ1h#IE|Yu%2A1QNqHKUL)S| zQrE#Ba@z5&u+4*tuF`c^HY7ndbtZ$UVq4qav05MDh7)Se_t(nHd$siBZg0k{eH=LL z9os^R+DIkg85vM!*q*tLa81ai4h63y28cmSJl^atP{>?v`Al!lxY85$8kairoA=Q@ z&$IoDh|i^T&f(q86VmZfb7-zE>+~1yV~de2PCJIB@TF5Ya&Ua!s}t4ec`+lQ`@^!3 zxoJsZ*D`zJ-|Ui0>xy&edC6M)HZTy+iPiZ^s?QAww;s zZ(??P9sXr`w5XtbKAI4nF}1URJ@#qHaP=PZPicM7YShaxzY6oe5f*j&(4g+)wet9S zcdi#5S$dYD?~hP@j{y%KtH z=U;<6md5={i&IW~y!QVLbth%V)t3oW5uW9uz@fY!Oes?A=WO0X(8lcT>ByF2k^FHD<2Td2pJCwC%>MtS|p1Mwj^>3bHoY@8Uj`svUZPjpUQZ ziCUjrKUo`z=hkVp-nQan^?~_=91?A{?TD9#bnl+ov!^DvDfpR! ziyVYvehlObs}z3sK`E^ib>ctYVd$#bl2L}Q(rv*{g0zLkGp=A8DU=CgHNywP4p+5G zaE(dJLw zgdle3XM=hjzdIpv<|mY5@1?&tuM#1Q`KC=yoCffIj^vBCsV&myZvPX zrS#W=mGm6g;+}l7)>8_{MDE4(ve5+> z$9RnwH%J)nvo!(Df?~C6YTi&)J+#FuIbDGS^^**f*VlHpVsGQGD znJt=+gv`f25qURnp*SQr6C2c0aM@!9ClUq$<|W<-Q%d9Jnn+$q8or{-r}eVwc|xL`Y|DTlZy|=rZO&KKgp#$^206rKv(rdk+{F|b z)st@T%o~K9{u~-#ls)fNl72YxtQwrM*imXWWhjT+H~pl1mGmxsC=BctM&ZdX zj=kdq%(vs>kSX+hn&)P^)_JJJewuIj2Pn7lxcq6>OIBd{XTkq;e`td6b}j0UMDsl= zLc-wK=C`TMt;?pd_jd2i42OhdiK!EL|9IlD3nms?{RsZ?)j{bUJzLb#p53pvH6Kuw zlI}kSN!;Fl`ks8FSm=g>xBh-UYIU7wV;&}VC?hF?(d)C3ftFM#L;qX2UiYRjQb-8z z)7!M#cX3{tTDoh=BT1LpR2ov;==zEE??uFosLc{Lcg8n@S%Bbp2Nffg%kDbzTX}A2 zK>Q8nP2Ev`Rl(Pd-zMPN<51u6P&qB{jb#j_pg^WI>(oH(9~w%wAQ!JD26WaBXqj^} znf1tSzRzz;C%eBT>@zOtq}0@?M6+$t$Bwx-hXsO#Em2%d_vYoLA@ixxu@JJT_$$TB1tYOJe`VR-M=VAmfEt5Igzo z)glHXXps6Q6|)5{wZ1LuBP zmEFS!7#gh59to7eL9k6{EmG(KXA^2RNPR!R(SfylJ2b0FbiY?IWThV$v#CsWWV^PS z{Sc4#JuX#dUBF}NXHV$$#7$ekDsgPnBhCjQb9ZE1Q$@ed(jJw-{!`u! zcSHkm@zecIeS_HMJWMS~-iiyPWgHxLI{NdunQO8(bsSC_IB{8|zC^R(pIFsD6qZ*X zk|J@Ll*r@EH_u7wAg*D$c~|&rN6|s2F)4n>VM9zoz-j9Juzc*^gArKomA=IOzVrZR z@Iaa~{yB4lag?d{kZ!zyz}EE+y z`$yKroA!Jn25?c4ScV){Z^Y_Af?&{F-g{)b;C8fTjzYmJ5jdxO!AlHU#z{kcjjpmo zLnWZ71XD@F)u>I&ubwgpnv)65^fPAP7WVMZpDFJOO%xS+6TeZUo5j8QzglW9Zmjas zkLdFLLE4-asf>p{=0%qUr!IT2yiK89^kI*}Wd=b@FoNy)k8y=|c#wcE`gZI^0DPsd zXG?tdzx$|=J585*B1fMm<5ok^Rp8X-j*jku%yaAPFSD~I&FqmKcsSb?PvHN}JG2d+ zN+Pq%M!L7JofR!O;uy2ViyKcvD1G#v$rj;XkV~K)XQ{2!m?Q_=@Wo==PIEE33x<{? zo%EcHeCM8d4a`Z*r`_gmJz7?*-mPRQh6)B{q!1VOE52iAQ`0ObdnHZcSyn7^dT?-C z1D?zVp1jw9qzyz}$X6e~oHp)~%EP$-G8f>A4Y(`Gi@{NH%>Owhu2~k7#GNfAtG>GQ z=}zdG{b0v1@2)j~nU?PFT`FzDZoWb)IwE^$^=AsWzI`XvNzJadS)U_Yv@7={K3VGn zYrYNbAQJ@Gieg}if~C!s`Y*~FR_lwaWo5$QTxq#Q3HkT$fi(kQpt^i~&7d%)U2FAb zBFql)e1I~}h_u6^7`OzAdTV}|UiM;N#Uk1fE==4COL`64!+z?4jVSXyUYnp}4o+*( z246#7?J8l6@RLAo#?UM0y5ZdJD-r~!N4{ke>^dBkX;NSyK{AcVDGr3ET_2^Ma#p3+*?C)-OgNEE@bdDO}m&-1@efvAP?1U6L11m?o>Jj@DS+xYf54kChZOV}z=@y_s-wz45c6 zueE)*w5SC|Ns<~IgS5=dtnqy=f`;Ni1*vqVx`c(b9CoP$Den}zIIJ;4=jYd-XrvKuxjf1uRCb3AW4B@L>z6E4t8KEJJ2WP` zePkuku_pZP+7Om=hH)g z6o)y9lwHQzcES6;M#tEzrV>z}mp7(R3k}vHjMT5ybP0E45~@fM46Ut;DJZ%hNur6t z5ap|Ql{{f(21)~c5uI(05-N|K{;nde)L%ceh9O!wX z{Y0JVAY7w%{P@(T7J()>$_viJLE}3<6BYNSgCm|+$NP~lkJio;zjkuvb)a&cf*wf_ zSZmROD`1q^P8=2$4CF}BZoj3oNT=tgV)BN7X)%SAJ2PQJ@om@g*gt2J`8#5?k0Buq zk&$R#8xwje3#V0^0f@JS(Jl-+3$1brDeUYja4Z_;90~rMeHvO?2|zZu0~Cv<0*zUq zaeLZ${Cg>u=LS%6*7D?ZAv99up%ri!>6e4H1IWP@I|9U6 zrePJSPV5mJ(r~#Z=L${zLG=}AAYSWxA@99s;Nw|uD=ZuvqfHCNBwOWQ_?wm)!s+#S zO{RZqi}jpe;nggUGZ2N_bm8;ZFZdqoapZ1C{H~;KXykaz&eB$gh%3LZTh8*clS5ZT zc0&zf>lq0!FDI!jZsW^M+cvVCgpx3s_)W-HSBjKA$L}rpL~R63UpP+CgpTLcG-FGPF~C0I?=QBitgJR+GY&9?bR~XUXdVvcWyc!Y8Z6Gu z-xbY)xbPhGAyA2vo^u6YHlJbBe$!2S6oc&1vAJWjkzI}A$gb8)w#KIx29yKxrb|Xq`I|*CH6CXKl?KA38$l1U_Eq!Me2hSY#+X0Ai^+$yxD_?q?!a^%RjIih# zC0`_5=_in^in*rfhdh=Wnx2{YEO_y1e`>-t6I9gKxa_L}eWl*IIRmfZvAF&|)|9O0 zhD1Zhf&N|i8gP5$jliBjnZn5$nV?E36NN^QVQ@Cf_UOg6#ylUcS%ld!#i(BDdtX?Mp&&GU2PA#;B2HOMviDO?nZ1n=JrD@IvJ zeTGH6!TPv0wi04+A=D;Fq>Q{Ob!>CwGvpZ2BYe(KoAEjz;sV!0RW4c(^pltF?f8Zb z??!{;f11Ul{MT~F2kx{`;(idumth&8;`+sb+y~VA_wTLJxYovEK`W@Y2xZ38yjzk@ zU(lTFsjz`JXB50^pTIU1iv^JT*5kO&1oxg?F<(vKxLa|hAp@^RoYN=p! zLe{R)11Ms>8x8kQAX(Jud(woOs;ryJo0DH`0^8dGy&6x_Cyj_XeLY4W6i~~KInCK7 zxc~B4QOEqx?D)W(ep`klm4T~iX1IfJI#+!98T{}Ty|l5vYBgq@N)KK1>#;SaGL zc094D9|c$TaI)8j{9s&HrKV$p+Ra>oUK<`P6!Ri07Y&D1iMk`HhO6W>HuJKy%%@0$ zQ?J+Mub&*eSMU;S-VWnSrM!<;Hl(UQQEyC8rBR`)GrfO57YklR(ZO*I8rv8BA8{f^AoNE+9|5hmzvB^ z&OC2s!@C0)0&*r+bB_ zD@Wn2i;yi8xk?x!{Pg>~LhaJkRn>6^!o-a&JfF1+XyTzqAbPA_Z4HK@YEgzJjZ{ z$+`(TZ6t2+X8I_EN0KG-!Pcp6e0~2;)0K453)cdr1r3&?c_FsfsMDkt!#INHZA-7V zsdCch9yDGa86+LA@F&v3xv^P)m=ievglh54j29Q1!`rq$vftY}JV0x!I9bcqD}_Sf zv^DFS=K&2~^h27Ro-;mW+;NzwS~7Qwsq1SqZ56Mp{$C2L?Mr31c9dpAxEX(D3KkWE zOizLyGxXayvG6zjWvLN0y>Be}pO4Fyg}6yhV*h5bhAla1SL%2v@spgOfnITOM=n{hLg7Xj}N#&Ku!-)AQl2{gXiBylSN1X*&GeGM|g{L2+5f&a@hH zNaB>)2qQ(Pjw1`d8N$^1=keL8SxX*g&kPs$SI{HQ1OGgv^XceRc4#-s&wG|Ofc4+7 zvwDKZ5y20s7;FLBwq+bNc0_QolywX2YCMk(<0NhB2g*Kw$j@ANm+C7Uu^aM(Q}$srg8usX0daXUN_8Y`fq`&Rg2qs*yC*Hi`p^$KJ|Q^P$A zQe|n|GSD3BF^N)2F@TK9WuzKn6#doobZBDUi04CDc!f+*IXN}3UA$H+CYcw`jUkq_u^wtmym$uUqHlP;tG#5ny}|!sNzr?NU!{ z_=)k2E@3wW;cZlBV6PFu_;}&aZeDfiH#$q4>&)wE{==U~#XrE$g;_Kue5IaY^tZLg z`?mtQp~|4od$dhHpvlpU;`W2q{}Eh-VH!gLDjYEKkIMPdz%}{?#%hKHdcb&HYgtHn zZ_JiPotpBhM-P>!Ldc*~?r;`6K}E1yZjy{)GK$HGfWc+R$Ghs?s$;GdvcrN7+MuN z3P(qb(@Igcs1q}F*t4+yqbxsqj7a$OQXnj}N?VV?={FC};=7w?FB|O=AgUJ#u@irz zoADzT<+mXVSklake=aw(ZfCBN^oGgYH8qQ()@pe7u8QEGur*VqIS8H@hHj z{PGXyloTJt#elEVT4yCI0kfKz_$GGmgIgW1W^>cU&ZQNRP&oS(B-2@NHa?TH**Ex6 ziG^=|5}+u{Z4T%gl>&wau*}YgC|IH@tSEr7n`QOSOs+Th1ZC)L2{>-kZ z&tGFf179_KPcvO|cj{3wxq*=R_ZVEpcfPb@;aH#FQj`G~wz1>{_SQD&79W=s{4V{y zdU=J@wIQ~V0!H6fl^O7i)q_DLCmuv?F_c%OD*R@cK1M=XUiD#_8pj|On`>zhy>=y0 zuf3_tZwexrArWX7tYY2?;MG^731(w_&(g!gNfA`MOkBGx3D3OYEbi>&cy=+jo6mJD zZp#Q3^8Z>o;@>>nkF4M2*HC;xvUyaWMx1(;a42quWk?~F9}rrlYsq-6e?9Fwt1_j6 zl|uCUWh_Q8rnY%0j-X3bOl{tMrZ4x7s}B`o;G5=|f#ov%9gE&8-;vErwZ&@g>aGg%>mjiMl(9R~!~#Z*pq>l=>ABkL#9&5Oq3(~=@Ep1>Jd9Z6lQlJuWgmkq&Phq+7_hs;iKc(B<5$8jlz!LRHI+0 z-AYmjtRF#L#&nfBNJ~v65pEtcZ9C?Y-XI6<5Dn!c*gF6B*x1mg-#98ix&G!#u~7I7 zb~AvYudG4&*Sla=c22O;eH)I58rGAbjHSf)XtH8I-T>d43uSoBWW^w2Plo_oISN;N zw^!BufMS67E?|OhOBjzr;?n@q^*cr2yOfD5HUcJ2(=g)E@M3e^VfZk%t|TAHuLmgl zl+|rve`E#3d-kojL;XWCKy7|NGbn*Jv=TEi@$p8pd3@$<*(mhyWMdE}18G@4R~KWl0#=O16H^9y&<5*Cl9pouBd)>|V+To0?1Uo||lDGly>4| zvZH<*j4ug!HK}XPp85R?)ye6>zkJfCYSCd8@-HJ4eby1i^0Dae_$e|72H%+VhP=j2 z&{7YV7mtt^Pye;HCix`vY1+Srv?*;oGiHzEdT$i&YE){^PGTQ?Ifx!Rh&s;()HsN& z#(@J$hbYNF? zA}0>#-dnr;s%#5tt%HnY%F3oqA|k`nm&a#{zWu>3g;Y{sNyz%5w-AOypRi0c%Io`) zMHXHTz8CRh^zqr`HlwyS;~45Gtc?lUkE?`jlu0|)<$vJ}7=kYEh52XjwDaletGi1S zi693lH3M`nZiGpUMBO`4Z2|4K|aPGA@PU5;3OL%pn=_1YOGA*h^B&)<4@f4zbqnMa^v_brD|nQ`5y7B;JbBLuE1 zmGkYHlG&O4i1>k!hsrHT}V{_|zADhdis<|+;<&UN; zA;0y$%hdFKZuye9v*nuPRP~snGESbI)R88Pld(mfm^^qDG-T5W3JVi41&Oe<>eSk2 zB@4t=;j8bj{hMB9-nDaMv^1r45~Q&2xN!y6KtGAl9@>B<^ht8ZCvz<<4)m*cZgx%Y zVf81*M{Y#!*uwH`cOZYg4mD&9Fe=pUdS%G1tumZT3#XRzU18=`KjE-Z6cf;dE^DN9T$uG_5%MTxZqs_gh^o}^dc9PmdXsv>YP}e$ z`SPdRuRlXOu%e8y$bAWsJ|*tKom4!v8leW{Z10PnTEk}(t;`^1DY{f5w>As1oUhK1 z&i5>>L~I;GXVZ^&@7(5jW~@2qH}Jy{{@5f&myi&Ot@=XBSNq zUW*ex23IFs;5B=fLQYN&o-$4@a~fW)E1bLBWT`pvtGJLw4h3vDqdI(@ioRp~wwL_D z#n$Zf7tx>R?}SZ$@6q&J))1BSY8jd-`UJpy6#iy=T!c$-e@~FI35_w+LAPnSmPen1 zZ-vA4YW(OZcW^(ix*Ny6eM;Q+`|eA<+pe*K{ytepJEF`7?f9wzx@5YBwLsbis7Q?o z(cIq}pXzb-N@rbl>%Wwd@adPS)v0|Sy_)NtY&xAKQC~*RqqV`26cJIzMf@5sKMBY&3I3%Y+W;!fn}mn~FkFL+;!un6%Z3+Ha#NwFJ|V zY1ZcCt!U$#gob}iOv$*eVK%FX{yg-RdMZe~<;SN|B#+p1)atg&=%{gnUahbou3fbL z!qi7^hjIyPjELLKqQpRo{^=K1=b$2NgFuttx4$BN!KJ^DdSw|w>NBm1W^RL6&_*O( zB5fTfVtVzec4HyoHX-LPIkL=XXn%=;u+};#HX;f$6Hx`{g6fFLPAS;Na)i%Bwga}9 zY#s`i1?q1fYtlE)wGnOO8&;LJ{G~aPW((Aa!e*7f6FgKWt-k(Aihp{cF31w=rm2FE zCDn=AY+O(di_NQm)_gKfgfRpiK`ii--~HxfZHe!*d6gUd57Hw#0#g-+P80l{REo9B zXoaAgS%tKASx1qds!{QO=&-eHR6hO`wFWm&@26=i%DP~Z&k+RRmP+bYXM3O@s114e_-~`zzkk{PGQZgkBClNZ5J!zKaJz}#v~W#cI~8JC3nIZ zQ9D&P3|`h$^+rU;Tz+zzFY%{MJbMf&QW6@DhG(&BsBlJx6D#r^hAyW!b@bR~=V;S8 z_J_Ay;#R%zl>rvKLSuJUg`tJe{xJhcuY@W)_g~SI+6ALC+ZtZlAnV!EBiwnP9eMQS z@g2F-@hB76T_J_Lnpe#xiY2`>NfzIj->U#xBQ$+IX{dhAST z+7^$(M9Z^B+JU_+D3`%Cc*sV~xH-?XX6;H^_G_4xztZSU(bYL+QdNhjEx}8g+c5s& z^rW5kR3}29%Y>eR3zTx~IIXjL{$?@O4kt4*^i=)5HWU{#3s<$+2nMxhyeqaSfJEsv zk-8`fqnWa3*A)Kl=1+=kYw{gy3Q?Rb0aLbjp>V&IyZdqiG}zzS!wR{G>Ft%<<rxNO}0gwjbsmO*4nS7KAnA#oF>`|-8* zxd+YQJ2{DLJHKzx>fq7fD~t4htiTVow)p?!yCdTY|1ahjKwzp| zXc-FuYV1D}z@=KukY9!X5kVRg>fvm84fyZ8U%xO%M@Pr4IFiu{dLg(4fJN&A@V`QKD45@Q3k8cA*HqkAD$-&LvAgbE-dFwlWq#%dEFKyf2%^bH5qY+v(zKWCzWG(#^r1Y{dHC?`GkL z0!vCta^?<=fwI!RvhFajg8chXc51T>K!%i+l>tJoJ^1%vgUT@3f@WFv=9Rx z$(a0Vd`StNx|UXVUtb?+0fe_=IywYEOqhX0HcADM1`%M#hCPNp{%4qjM^6oFV^Kg% zV3BA4uh;y~cT*;SHw9GvjX)>K15qA_)wY;Fe`Fy&I8xZuL7s?IW!AagE?*fgtJjU)nY#cW-0JaR*8s!U&fTvKl$rt73jE0d z(uLr&xG zf_mEp?!2P`U{aSa5!aXnA3}(xO_Bv)fFWrDid`H0UfK%8+0$|8HAQ}^=YfU=vTy@~ zgPqN%ZMOSW!zVz19U}akpPyeKZv6%V7_zp0f9sh>0$8zAMN0iZ>`z%o2Qf)D1dtwe z`50<+8b22dwkgQV!^ZV@gAz%T{qbS3{0Sq7BK5=SODw=Qtpg$kL<}o~*O`H25i5#X z8y7-<0>VSNpfQ0zNfV7%F;EIcocjSR|HO*3>|K$KaYbONHV9mkw_Uft!P?EoX z`)0geJX~5*f;0udX8?4Tl}dqZ#s=|%hPf#QfZT&v3HT}GKo|=%VjK`(Kvw|o;i7r? z0wmP-0BO|Q$A^(5O$PvsMF6{{C;SambR1_?c({m#MPUe~*XL^3C~GHhVYYU84|s8{ z1>&sq;C+Gm6)2rH0xHNU9MRSQ39tEOy2_W$W;_qJB%D@BCfg%qY5Pt6Td%h%t z5EFp>rI@$30QgMP{c%X=n&_%~c<=&F8!FKKh6iY*WVHUWWCoo;P%`@#kkBY7DDb%* zGJsv_ATU5MH820>+94wYyat4R3}nT^AYf8JQrw#@Us6jq!vPHda6v#^P7;*GpYNnQ zZh*T}2!F4nw3Lw~YvFjl8ip?ujZ!+c)`ixhd-t;1_sR2UwVfjuK?k5NV0_fO z@Gy$K{k8_7%R;z?prwd~yzzmQH>d=T0nUnCt&IYe!eaai(z4O`tgk>Ju&AysAwd3E zukQ_?raQKEHaKk>0p{qw@^E2U847dD*{i-dGV~C!fQ$^{y=%{C68_oAT)}D(8EO&g zbYlR783=wtI4Wb)YKs6O6!#h#*9h<>lhJw^Ca0z#Ac~&eUJ4c#48X*oWn_e~e&c|{ z>hQJTQeYrJ+zQAl>VV-35OcxW3{t?g8rgtWEFj0f1ffwnz(>e|E$i9{;C5I z`cw=IVgcvlLxCzpo*hE>1zDfjsfBAhH+EMN<_xj``^(2i2;2gYu(o_rh2;QgaqZ;9 zVV`BVYi88c`Jwk zU?>6`Ab3Y_fXPlq8({z#ARzT^r%D4xW23VrEG(=xE#2impjQ70Fv=l(otc>#pbW*X zPB)|G(|gj5F5CksJQPW?5Jv*k5lo;2E(*|VV9YLna17)l(8qd|UjzCm(0vyL1T%OKz=vw9&C`n2I z^-;ia$<-`{1XOm*(Ht;?CZm=2{zulY$)(x_--7+qng~Q!9>hU^5SRcs9N<+P0eeP= zkE3QEd>~NAKLaB45TrP8Zlv4IGiwcP?eJ>TeiM)t1H9>o3KJ1@Q51x$20lMTo1qU7 z*s~APorhzjCCv88|GRFXDNEr0f19fQKmOaS8f1e1`Sa&BDOag7?bqaFi`lW~_Lm1? zK(JXV%bvgnlf3ZL6&BFL4Ff{6Iv_f|2W|}{?E&_V5w{zFLCtPXXAC=mNWRm57Aq5W zLU(I08Nz;lCygO_FkL_ekAb95jRKGXLEx%#!Iy{84!ll;VE10Zz_414lY@~~@d(xw z0Q1LwX!(wY<}EdKFXZ7MA)*%~(Lj-rkpT*dE_gVY_2!#gO9y=&tN|R45)fCdt3g>j z;A)!8R~h@+kN{*D&>Nk_deA3iGyBlG`57!^0L!#iS)zTR+7up)4Tv$FU|;}cPiaNP z*aC55(k{%hB6&&4*BhIgj3l9w5WfTfo?aj$9|A@oBqW~$F?@g_^Aegr1;p0|@B52v z-F~2b%<6sj32>cDQuAzd09{th!=tgF+h|yxF3AMIy#P_L;D0}VIG$Rr%4`s`c;*1E zh#6@9LMZYIfAaG3ApQ*10n{HcNJt=T^IRaT@L$NgvaW6MvPkWF!(~ z5e5mQx3`tIXRCnol55!RYc*4X10+7H&4+Lu76H1F)oDWlLiMv>tnCD=4x;E*z+nbF zFe%U+6BYXW!x_9-5Rw85VEjOM?yKIHsGv8h!Q(xLUO*bo9?&HPQv@7ndO##H1=Kd0 zB^y9_ns0ET1V9S%guyegX8060WVO=730T2V1nd@IY4CX{F)7IyP#qz0K0lu_Fes?e za2HGl0G34xV1Ouicz6<0QX9ZsmVW_o7T~uEZ++$!5EN7(75rtlz*cL&gbR@I5Fi$4 z-o|4{$YeSJuD1>WoB=%DM2QAH_@&=}h5iClfWqe5)>bf>7S0GEFltG9WJrP)7IlR302&Sv>V9Tqx7_dsh)0M6 z_HPv^(^uEje9Og!3l_hC3@_JO_F3vJJ+1%XZ7In1n{QiXtr( zx|brdM|MThL?j`iuC2|r6dH;~+>%ntzGUCJ$`-z__xJJr6Tbb_4~);emvdg{b!N^J z#POC)px=NTHZnF|hs*iKYysmvr&w#nyMT-A9{z--ZnV zfW%DLfs>|YW^PuxMPP)XlmRX-Vxe$<>W;>~WHIIA6wJp6hu|}$F(oxM%R0)OmVV8l z?4YTbrD-K<=NK9olq&v%gG6w`pjz46+w%zuMgSD$-ioMv7mBb=MHje8=9paoqXckO z%-QJ9Ia=nq2H0)<`6w`m^_FHGQ3qg?7=bv^AmWAFZ`%t7s7K#xe)m?~G>Gf-|EUG@ zztj=je_LZW<^JD)U)J&-rUO19LA{Gyc>RDv|2L%59O&3vN+V-qW>Bg!)VsK|3ARc( zpjufwIEaaAc}1^YxgsJm@;#LIgN%$2hu}r;^*lV^lEl?cbD2mBVC>q2ai+yPUVKf0 znh=e+6deu05H~tH>Vi9p(`SUZF9WiQ77(WV{6Pz2CDIATa+s&8s-}i9NO>$R9%1DW zs+5%c_U-Y7_GJd`<=$D?egS?+1~KlTG&HUO112LaZQ<^|4Wp5pkVoaI)KGA)hAI;prD6dg1t-fAJY1aDd#w4+QjaiS9Whaijg2y8_WO~oa)DHRk0UsU5uv~v#2Fc)e4K8vJKF@j;SegYzVY$>%X4poF%h`5 zG?`*i)7Iw=Di$j*rj9Sy50@!bowL2xppexXIxr?+qd118BGyFQIlC^pi}mr#o+? zrtT{%D|;F+Fxk^rz5GrOf8gEvy@U3mCO zBnsd`?8gCndj=kX_Q;ko@~KFcdKaU$ES;RB5n$qPn98K-1p)D{V+uUj)LbATk3zM+ z$LrrmXm0bn6m3jnCXmBSqgmlF%Wqom-yQLr3y9PBr14&xB8PjZdz%2z1#siIGGK6v zF5m~Zsia-IwyWf9Cyh$3gf$jUPFFY0)YNnkv$oa(OdEsk!;1;T(19PG>T7jH+Cm0`EGrs&NNN-1pHmV+~Ffz@?I(L$#`4V?2FMq+I*J0As( z92ielC`fGf79TM)$h-zsnfkOnIf(@|LU#+j_i2P2pT5yi#`=gJUK!h;KYt!Lc<_}O zFD`CLhyRs;^rR#s{NA*`F8}6@`I3f_$@%A@f}SFKQK?(>FX}Yj(f#T)k5wJQhms|z zj-fJ4wsFCsokyzhSbXtySvU)YqqyzT0x`yU9|b@g+Fr$2{xzU4a!2O%qvvq8V0`diDWh2B!^BpQ)6uz29;}94k0MfDOY2rZ z@XYwpmAf#BlurLp{HM)4DpW>`rq`f_duId2tV6yqau(+1nn3=HjvjKhtzRSiP* zq7FHS5a#-@!+Wf=3ZWv!1aqt3$Vo_qaB&z`%;AXa$JNH&t4SnR93}cLdXR`w`l=m~pjKHJ7=)UnAcZZC99!<`82I*0 zfPx20RtbaQ$4DB&`3S<0UD_#Jo@{fyxtH1b$REIjy1c!kE4^ubnLm_B9B7d>jW$>p zftc`p4;_Mj9NdD$g5a%tL6pg4a#D^~2Bs+PGd7lZWZRIIeulYx1gBp{EPgMX8~aw= zeq*^!Rv4mnwA?FZHn5UCeWW<10efDi8phfsCMM0Cts6 zSa|mH8z3?aqy;=C`aL!81TXn9XOPpuj{xnOLh3xGx7X17h!MuZNKsV}D&1MwRb@l# zBUbAAPj__$1Oyl=3GLisefl&FOWoDo%}ShGSTIBl47@Akp4s0bFc#C((@kz;p7QI) z#{OZP-@Weh2oYUfU6k$Hw-@FkW4{~u@%rC?jc~?5q-sKH@lVIvMZhP-c>a*L%LRW_ z-oL*N=Wh^rh>qapAWRs4(b&ixyzsSC8Pf>82H&l~=yrHGMj)-fE3n(iC9by>!|h>i zScm;M^Yxzsb^ww$JeiOyz1nB@~I9M9)LK0_9ent^$`0dwO)|1CE0c7KiX1eF4}kB?7yWF#pwd>4Ow z0wH;%F|Wymadia+t_BBzx{JlzoLxfnCJ)=v(qexYmZHbPPq)GpKIQgt@-zyHicOh@ zYYQ8WD+8pQ0Vzocy#Ii}1`w9X^zc^-h5NY)efI%Lzum z#=e%A{mYA>Mg(K&46rwJfGFD9+W0_;fD~ig(P`~Vp3@D~tLjiGXmSD1xh`mb``De7 z6i%!xb0QtJgO`>ln9s2HRsfyK94dw-%bm8L7*;7SZLF+9p%2L-5%S#E&Ls}~5H|8S z#;?Jl(+9qd#oyQ>GelAE13rrcGK;ZibZ|J2Y7k-ccYF9W}N9Qe0H_` z5g4KO4=BLEaDVn;(H^i~lz`0A;aV=?g=F2p$Ax1VmH55_8Lbb!_-dvzI356A2}Q*X zkn|8&Mu?w3CpzO1cd3$+tZZ?sqg69?ux=kX^q>~_;_{f5XnMlUEe;TR|B>R7lA@he z3|h^;?G@oLd)2pC8&3Tpq?Z0RDlJWvV3{W;&oN!SHzwySttjjVR`$k?O-`pzpG0Lf zJKGzdz+{HO!+YBC926TUIG*NsM7&m#xSr5GEBT~tM=#%W@MmW~ zzi_PKj;;vGpE~4(wJ&C~UVi#?b0kbQ(Nsi7{{vLvM&imX&U=r-l0@ZTWNFC{9Qo($ zc`PWFb^KZD4afQJ^CoUO#dThOesdFI%}|a}si~=KbvrvdN^Pd{{{eslA>Ia?b9o7j zHqjJ?@}co$2h1G2DyDlG-ZAQy6HpSf-BIFeE}823l{5AfNo$!*UT*9p9EiWYzd+>4OhMU(ZqKYdPB>Dt~yLM5c*TG=TFUd#5CKWuQdCCVng~aOm`t?T&givq7{zh3! zhqDK~`uFeOE#LrBPyp9LS%dej%3S=ZQC(9*T!(G!db9&rVQWRw^bsop^~4buRb#%Q z8kz#94AY^9jv?F}fq zK_(x}4Aq!2Y!q4+yRo?N!p+=YKIk7B;yinH!oSYTjau;W%x?O_M~`;HLWI>c)x}x` z$N!Ud4jJrPcn5C}JrrUgvHfKPV%R(Um*xgkK!{?b>xqJHYj5A$+KPd+%Z9%T#m+rc zv^B@VCuRCfbkJ->2}3?3Lju|BMN<<$ej1N4*PPzp-F*oqo8x2`3)e^)RZASe2^0!B z)F^nWJ3GY@){za3O-%4uH`wiFRdtB#=p?FOux5zOu^>TQ*w2+g&)6o9VZN5Ck65;|mL0g|BnP5rYOo3l;ZElu-|T zr%pbftlnT-by3$LaB5F>cJ>QA2@-<_z?;PP3=LfwE3%UZJ*|i*Pt;IpHUVVsHTs#K zSc33H__l84ilK-8ZugJGjvYTA4IlXvjtb%2lzR<@qU4rB0K{>KbNJ&1+i4blmdYvc z2V0-4C8rC;RwM0r{P)caFs7tu z+9i^em6f*t{Iway63P6?Vu-m^HMNa!ZfD^^d33#4F+JWcf@6pqtv<&?JVIF0edE&s zpQHJul+igH515`ouO#7Xfn<8n&&`CGBOc;{>cxP4osaz$HBAd+kIO7wBiXp=x-fwHJW-5Z0{ z;!!sZ>$+@F6T%+XSO|aYB^n_ooSmZ(J*rz;gpea^kz_0n99RwL4_MW(fKs{MoP#4Y z4Mo$G_dfR2re z2??RVHI>@A^EcFfHSaPF%jV2)YY>fs@=+{g%BCOsc@p@M-Ey0HfJ1utu8c9h?inPw%&-(IZ3*zgJ zA(0C(;bHnQmxR4hUQ#tQgzw$E_xY*oMo2w^{blOO!bUIxd8;DtBsQ=5fpxCLru8sD zyZje};itzSn`WUIcJboHb-cU-h3}x`BqSwQAoieVMR>&ErSeud#KUv%K~oSOoxMJH zrUkk87`Q7bCAEqk4z!WyBJ!&skC+b5pV14X6#TK!LwS49dj9P#Bq84Ds~YjbexxN^j!3nke309E)ql(t~Km z`2QT`M2dO(R2_{7=CM)ytYPX7w^A*%ZxNX?PzWG2quEZPGpr%lLi}Q4?(dg}PN434 ziL0A{zKJIzO~;S4Qt*T8xViVBSToB>(+?JdgDec6Vy1TwLMGG=GHPF;>4*B)uV1j4 zvX)j>D^Zld`mKRnMyS=e_)7!Zz^RuW58F%%8yNDa=+?7m+CbhNnRsUV32z%RYZYvV zX-q3emJLNM`6<_;=#wMv2Wcha0X@2_*HDy)#5skFGPY`ZuHNb17E)aN2Q29(e9&WZ zE|8C(s6vQcVz?s(kUGn3_*Qy4VJTP}2{r`&a?nCl`R)NVL3$%>EJu68aowmN@j^aOmLv$?Db1X*rdTxH&74elz zRBIEu5-KT60d-ywr-aO1?==|X+XiDARah0lqn@yTa2fsLJzK|hC<+?#oh2n zw`yE7xgeU~ilSs}YZWVW#3syt_&GI%{qM);@EMcF*!DfI$%jqI=T686&Hf+%wqJ{hw0jl>|4jDmrxzL=5B(okiqjYX literal 0 HcmV?d00001 diff --git a/raster/r.texture/r_texture_window_efficiency.png b/raster/r.texture/r_texture_window_efficiency.png new file mode 100644 index 0000000000000000000000000000000000000000..a28bb6a33fa4205538ae71578990b37ee9d81ade GIT binary patch literal 48672 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 Sy(N?IpVOr26HnNCNB%d>L3P^z literal 0 HcmV?d00001 diff --git a/raster/r.texture/r_texture_window_time.png b/raster/r.texture/r_texture_window_time.png new file mode 100644 index 0000000000000000000000000000000000000000..69d0aff1ba195938621deba1ce7b48ae62996bf1 GIT binary patch literal 43656 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

BP0PjeJI!OL(u zMF$9}fcNiF2=1c6oP@Z5vHut%rGrDIUJDUIJq%a3-XR{et1D+haP^ZZCV!e%;vchi~%h z6oJhF8N9~p2?hE2)Q=yNKv|)xr8PB41UT5(?m=(%hoZtG;t~>~m8)ZB^RGL(x%Fp? zcz)g4x!a^01(_JNfB-Q*#eLlztDXBd76^A!`pT|^j!yCJsxtN%bU zgy>H&MSosdp@f`&a}!#O27dG1z*`7kfIfgrF0g>mhv*$3YzRO|NrZ_&>seJ*)d>V) z2e2jCW%Tq&Mv!pbQdd_;^bEiPBchz0DDFB!B!kJ(`VRV-;Y^8ItL6hyuMm*s0n|J- z{E?vGdCHCl$aFw}V+{Rt+1v%t`>p&UUT5~;{V@b_V$ARl*%vQtoY}NsXu|BoQ3&c} z2iwKr;qcw+@`{SSI0pHzzkWr)tVKspj|v>SnUi)Zs6ZH zdBD6S1%0ioynOxp_W&U0qQYbWZS=wmRLl^!Ap?I3N=1j`EhW1Z7-lfH zfR}?FNmWfv3OLNU!1l$*od6fWbH)!2!-0W;6EOXH?85D!Dfnh+2ovZ&;Hv2;*uX($ zvkkat`dHb4rKPmL`z!L0BOU|BD-IMj4cCwt05~$F9n4?Mj~?Y93<6kKSWE!@3<&`) z=Ri+S0K~SJFoqzp;Q}629$3+Ea3^3rEd$zD0#efFFe${Rq%4?F2H$`15=KINMuq}B zXaRUOCg9>!NC=a#a2iN0Ex>LV4{|;R28PJl4*T_6p}od;@7$p-P)2;jcaUN6n2mf* z1XUn|jEoFSfpqW__{D%#mVtROUzK?q%pc!3HpF27{G3IIos7DJ0BuMHBuo{E(4dBf zl7T8>5G=_Rx=rFxF|`on5UcEuGghFb!6hXln?zJwfwB_tBEfACEu57Bk_-UieUK~2 z1uM}cth1p4K_`zi4)Hf6WDC**29ShlgChcZ=LAAeK^r>`9AbDfFy=x%@2$3_K?btb zadX81ADzl?tL^mQ;oH5sIVO7g0I(2jb-lmM3mG<!EWUR7 z9YYp12@$vW)2BFi77Jjx#eeu9Ay*J59YryikpyDC7*JtALKz98O%F6g5M5_}36^p^ zd>9OyI*&lW0;Pw=AG8bvqK_~$IfEHxXm~geLX8r*(wj~|4fh=S=5eL(RXF61j}qh{ zA?9$|Urvdl5|M-MR|^yH&U`i9+{+JfaZIA3nPFjJh)&A*D)R;Ov}JyP*S-R=hx7Vp zG-f8IY*3WMs4^D-^coj;0%G=~$B&cu*E(K7cW~YuA>Cg*IqhVBXFKnyYP7l*Uf>E6v;r8I9K>> zV`B?gw33p%5JSiw+fx@57AgZt?dscun{55Ix3Svgg+Nf_=H$#qR2g(#@cC6JWIcB9 zhn#&ICTiU@QHKr4@t};cAlEF{toDaFXA8Vb(_mZC2COSCE*R#+AQcsdJhlLo(sD0f z7J{&?A__7BMBjr9Y|ME%dA17=GBS3=K5H$=AwE@AWkW7>^Qa3^g|K9=|saD%4GQKb(Hf_kW5nWl(Y4i6c2Z)gnV0YRQg(|2XHFb4< zosLqC$~am2-1KycieHd;n;05CwW>=E3DNgHQTw-*7oED&2P>L8NC%&t0Z7ly%bUNm zV}*${ahO%alsP)TukhWOM}IojBecsbL&tBWa!+@c0U9c(SY%2$G` z+8UrOku$DN25z;CK2No6@N<*7FKd<=-jAo-*FmyrxqXa184<{7| zFxk;34SSeEMnVs=8R#vMiEChQbOE;)3>A%fSbHKE+_)kyt3Wr?Gb#qgu9UPirz{cL zT-X$klPgeFQIVLKm;lzgB=C_vtXdTiEO!VQheI%wwh{PCfpS%53?w%@#1H~)daDbf zdl>kfGgR7ur3r40X8jp*baHwGz8&bEGO&@R+Xge63>Y_mCbF=xF#s8rz3c7O$($uD z^Hp_qFN1f%i7Jb2eOC?TM9@1oF1-0_>PQhkgUszPDf^gMs96 zv^&kf4T_wvL$Kkn09snes1QS@$C!(Oo}O3R4Z!i!K)YjHItB{=b5JC&C+vYLR99Cw zB*bDcj)50kVin%Jy&zVz{xD+jUNSq1M>-f5q*ZTEtPop8kYD(^d+X@v0Po&@3K9;E z_A}Zy&9FNFLk3oPNbg_^;i!J1=+y%|&OHD=2SNARfM2%0nG_$7@K`O^LXwlOY+1tv zGfyXM9YDUMn*u8|SouO44`TVV7cTgrlJ>5yo4s1Fsa&8uI8zPLfB6y;-md9s0*EoX zS~u5MAgZ$e`GqpSs0bXkczU39s;vZ{5lr_qPo7X1AAV~&1a&UL6IW%_4baaA)SR3+ zknWr6LRw17I@Gf+#k~j4 z#RmOBJ{NleNS)xYPAmIRSLc%V10N3$W;KC|vTJh`B-ne(QDkb|ZeDtLQslR|R1Ujm zplR&~>#lnP*U{0@1V~QJOPo|Tzhs10RdIsVrF&_K9J0r*v^VV-u(K*XT%yXXPmc5s z5>-TP;e<*?CXNjZJdLIjF|@E4Nccako%=u5cOS>ERf`m&WJ{=&?z&C14p!8XtljHm zj8<|e%{1Yn134ci&H1)ch?+e}X=+OjyE@1zn{qcKl*pXI9MXY>JwLmDxc`B>pL!Io z>-v1&pZELqe!agw&li19wD}?JZZjslX&vLRouli+DExcN32EYNfZK>2=>P=z)^_)9 z6;;&`KCP*K;;LQc&KBdwr<0Nztc-d!>?M`1rG>(9G6s>rUfQzdlKg4hgaOgLs}PFh(e7cG1OfpBeZUQ_|4T5PrKbLSZZVq*-KHHT3*vy7@bLMf*efb$Xl~gQG)VHf>aq3=IvaJEqdI{e5%L^?N7@O6`Q7g60eYSVbJWz-=(!(Bvu@gdZ5m9p4LrS|{r>hfW!WvI zg>FG3!u{di_&IA1Ph0n1{bM`y$~gGa@iG57MjjHuF{N{pQrOp7^@XQC zIH_@M1tPa-M{Oy+-YpzP{K4jn|~y zVrxB%+E>#v2nCQKxj+Fgbwj(`eQH}yc7l=?Vtujgz47n0Dl!N79BBEo+dY53Z6x7d z%#Y31Tm8ekv8KA(H{(UO>XVVsfbJLA>>phpR69VSA@-P`7F$BgGuK4|H`iEKIudNO$)Jn(Az9P`18%xKkSA zxB1!UIMDbMnQIk4A3t{z#)GgTWm?rEBdoH&zdvr%F?BCU-}ei=oLT1YGBUzn&E2X= zwPT*MYJc^i<5zcHnygav@Zme)c_LzBgh3ylkWhVoOW;UXrme|!nJg|YE{KEDbT~RH z%DbEkoa{@`zJkaHPsdaGxWG#eb7{O`k*BIwa1C3VKZLsf`ncm*sbE#dT zY;4)GkuV$|x`E$d;QG+u_3&`Zop_y2h>f~iQ{*Kgx??ogOjcJ{$AbJY{r=p=i>L6m zWSPmSB~(ellI9B|>g)qGc}2mj?wdcXw?K6YSfDU$1Jg^^I&{282HZF4C${3$7jv3p zfR|#Li1P6ex(!*9nsx;XXY-lWY7E)*GuPBy_wx-;I0Fl}?YOSxY58wP4>XmrB?9pP1F{?nyoH7BF($ckuE&4g>kKRV;sXrn012loZyUxj+nl1H($z{Vhm9X4nv7tA($EQJnAd zT$gw)H#yTd6YoBoi*#s|0QiJ5)uSPci;oWm5#$FkLM+?a^`U2Ta$=z^_OG3Yp;v+r zkiTejwt}K!DO2M7s{1^o@G4U)FGJ*=cMSJTf-ofyIL6A_`YE_VkxEq1?{bMnGdJZ5{`X zQcNBVPg77ZH8Yz86x{$;FXDJEi9>VsL2;3f;em$bLYokPOHL@D)n#?dV0z93V07S` zO_EA@#Iq6U>5iSWAjpCkHmJWamo2sU?6cDDCalema87{+8oE6)O2ND)4dRG}f0|W- zR5tMZeUsZTPTy_7io~7V_CGHTqW`!G&_?VQo_KLd$)wrQli~OT(}mml_O58^opZ@L zl8e_{5%8o`5jB2(eq6{X1S@mesR022+b%ha=~dcVMRtPIJx~^Y#>97)!q*|Iw9;=7 z7Z_~f!WBb9LosM&0ODR*L6L3g>Z(6)(}Q?0@*i0~^x{QJM@QY4Z>n(6g|P=hqPV{P zUHU0o<=r&D0Hei=B9n^P2R^|uK*5|)fi*sFwZttY{Kk#7JQE-i7l6v?Dy%V02&cg4 zVCQtx4I9p7W*)=o*>K6a7>6jbgQsPfI<}Var66!?8?uWF+-ATa*kCl;**NV1xj7S6H7dHK&Ug_lezbl$7+9sE-6@@ zn3$+TJ|rRa4-STGT7W-40l_JV!9od@ty^)B`I_F&tKxfQE9nOp!+mG`UPQ%XR4ntM zjS>}8I4u4?78h#;VHi+pf!%S@&q9Bn$#~7t*;y_yFp$tad-du}ky&_e^JYaFGH7D^ zCY;y2_yq$@cnaoer6U-sVF;}qJ9qB66E{!PSvtr9!>J|_22B$0R-NDm zt#9$to-*1tSiU@r35z)HJa;kwHcBTxDMRo>kg7WxaJPzuD@%KjvMw;SE+aHFbYA?X{H0S4 z<#UOJHxZ}{{-rfpcTNFWNu02iHHN;c|Etz)8!5}xUf!;70uuInV2gRkIsX?YMA1v&?u|VbT9)bE)&4{yVyJ)GONafBoCI ZvO}=rgK5@kUK0L%ym71952g+we*u8once^Z literal 0 HcmV?d00001 diff --git a/raster/r.texture/testsuite/test_texture.py b/raster/r.texture/testsuite/test_rtexture.py similarity index 100% rename from raster/r.texture/testsuite/test_texture.py rename to raster/r.texture/testsuite/test_rtexture.py diff --git a/raster/r.texture/testsuite/test_rtexture_parallel.py b/raster/r.texture/testsuite/test_rtexture_parallel.py new file mode 100644 index 00000000000..4137e0c3a00 --- /dev/null +++ b/raster/r.texture/testsuite/test_rtexture_parallel.py @@ -0,0 +1,255 @@ +""" +Name: r.texture test +Purpose: Tests r.texture and its flags/options. + +Author: Sunveer Singh, Google Code-in 2017 + Chung-Yuan Liang, modified in 2024 +Copyright: (C) 2017 by Sunveer Singh and the GRASS Development Team +Licence: This program is free software under the GNU General Public + License (>=v2). Read the file COPYING that comes with GRASS + for details. +""" + +from grass.gunittest.case import TestCase +import sys + +IS_MAC = sys.platform.startswith("darwin") +THREADS = 4 + + +class TestRasterreport(TestCase): + input = "lsat7_2002_80" + + @classmethod + def setUpClass(cls): + cls.use_temp_region() + cls.runModule("g.region", raster=cls.input) + + @classmethod + def tearDownClass(cls): + cls.del_temp_region() + maps = [ + "asm_ASM", + "contrast_Contr", + "corr_Corr", + "var_Var", + "idm_IDM", + "sa_SA", + "sv_SV", + "se_SE", + "entr_Entr", + "dv_DV", + "de_DE", + "moc1_MOC-1", + "moc2_MOC-2", + ] + cls.runModule("g.remove", flags="f", type="raster", name=maps) + + def test_asm(self): + """Testing method asm""" + basename = "ASM" + method = "asm" + output = f"{method}_{basename}" + values = """min=0.104167 + max=1 + mean=0.112871783083256 + variance=0.000158101620160753 + n=996244""" + self.assertModule( + "r.texture", input=self.input, output=method, method=method, nprocs=THREADS + ) + self.assertRasterFitsUnivar(output, reference=values, precision=1e-2) + + def test_contrast(self): + """Testing method contrast""" + basename = "Contr" + method = "contrast" + output = f"{method}_{basename}" + values = """min=0 + max=14680.771484375 + mean=176.569350873014 + variance=185767.349118713 + n=996244""" + self.assertModule( + "r.texture", input=self.input, output=method, method=method, nprocs=THREADS + ) + self.assertRasterFitsUnivar(output, reference=values, precision=1e-2) + + def test_corr(self): + """Testing method corr""" + basename = "Corr" + method = "corr" + output = f"{method}_{basename}" + values = """min=-0.533553719520569 + max=0.443134188652039 + mean=-0.0511981885382368 + variance=0.0306650179549719 + n=996244""" + self.assertModule( + "r.texture", input=self.input, output=method, method=method, nprocs=THREADS + ) + self.assertRasterFitsUnivar(output, reference=values, precision=1e-2) + + def test_var(self): + """Testing method var""" + basename = "Var" + method = "var" + output = f"{method}_{basename}" + values = """min=0 + max=8776.787109375 + mean=90.3218668390769 + variance=58439.1071892438 + n=996244""" + self.assertModule( + "r.texture", input=self.input, output=method, method=method, nprocs=THREADS + ) + self.assertRasterFitsUnivar(output, reference=values, precision=1e-2) + + def test_idm(self): + """Testing method idm""" + basename = "IDM" + method = "idm" + output = f"{method}_{basename}" + values = """min=0.000490661768708378 + max=1 + mean=0.12308521457551 + variance=0.00497110161251246 + n=996244""" + self.assertModule( + "r.texture", input=self.input, output=method, method=method, nprocs=THREADS + ) + self.assertRasterFitsUnivar(output, reference=values, precision=1e-2) + + def test_sa(self): + """Testing method sa""" + basename = "SA" + method = "sa" + output = f"{method}_{basename}" + values = """min=95.9583358764648 + max=2942.79150390625 + mean=742.367934271396 + variance=31616.3527726298 + n=996244""" + self.assertModule( + "r.texture", input=self.input, output=method, method=method, nprocs=THREADS + ) + self.assertRasterFitsUnivar(output, reference=values, precision=1e-2) + + def test_sv(self): + """Testing method sv""" + basename = "SV" + method = "sv" + output = f"{method}_{basename}" + # The results on macOS is slightly different from the other platforms + if IS_MAC: + values = """min=0 + max=45368496 + mean=2248724.38215788 + variance=2332049495199.41 + n=996244""" + else: + values = """min=0 + max=45368492 + mean=2248724.35829364 + variance=2332049431762.5 + n=996244""" + self.assertModule( + "r.texture", input=self.input, output=method, method=method, nprocs=THREADS + ) + self.assertRasterFitsUnivar(output, reference=values, precision=1e-2) + + def test_se(self): + """Testing method se""" + basename = "SE" + method = "se" + output = f"{method}_{basename}" + values = """min=0 + max=2.29248142242432 + mean=1.99162619886133 + variance=0.0255830167563798 + n=996244""" + self.assertModule( + "r.texture", input=self.input, output=method, method=method, nprocs=THREADS + ) + self.assertRasterFitsUnivar(output, reference=values, precision=1e-2) + + def test_entr(self): + """Testing method entr""" + basename = "Entr" + method = "entr" + output = f"{method}_{basename}" + values = """min=-0 + max=3.29248118400574 + mean=3.20825728104855 + variance=0.0106857128131089 + n=996244""" + self.assertModule( + "r.texture", input=self.input, output=method, method=method, nprocs=THREADS + ) + self.assertRasterFitsUnivar(output, reference=values, precision=1e-2) + + def test_dv(self): + """Testing method dv""" + basename = "DV" + method = "dv" + output = f"{method}_{basename}" + values = """min=-4.33281384175643e-05 + max=0.115451395511627 + mean=0.00110390526476111 + variance=2.37568891441793e-06 + n=996244""" + self.assertModule( + "r.texture", input=self.input, output=method, method=method, nprocs=THREADS + ) + self.assertRasterFitsUnivar(output, reference=values, precision=1e-2) + + def test_de(self): + """Testing method de""" + basename = "DE" + method = "de" + output = f"{method}_{basename}" + values = """min=-0 + max=2.29248142242432 + mean=1.6115293021953 + variance=0.055056566141371 + n=996244""" + self.assertModule( + "r.texture", input=self.input, output=method, method=method, nprocs=THREADS + ) + self.assertRasterFitsUnivar(output, reference=values, precision=1e-2) + + def test_moc1(self): + """Testing method moc1""" + basename = "MOC-1" + method = "moc1" + output = f"{method}_{basename}" + values = """min=-0.910445094108582 + max=0 + mean=-0.782939935071241 + variance=0.00736236364469843 + n=996244""" + self.assertModule( + "r.texture", input=self.input, output=method, method=method, nprocs=THREADS + ) + self.assertRasterFitsUnivar(output, reference=values, precision=1e-2) + + def test_moc2(self): + """Testing method moc2""" + basename = "MOC-2" + method = "moc2" + output = f"{method}_{basename}" + values = """min=-0 + max=0.996889352798462 + mean=0.98765725693893 + variance=0.000334346005602857 + n=996244""" + self.assertModule( + "r.texture", input=self.input, output=method, method=method, nprocs=THREADS + ) + self.assertRasterFitsUnivar(output, reference=values, precision=1e-2) + + +if __name__ == "__main__": + from grass.gunittest.main import test + + test() From 3f30f1bb59f79d18074a3527d9101200c0975214 Mon Sep 17 00:00:00 2001 From: Vaclav Petras Date: Sun, 23 Jun 2024 11:52:05 -0400 Subject: [PATCH 009/293] grass.app: Refactor PATH setup in grass init script and grass.script.setup (#3694) --- lib/init/grass.py | 288 +++++------------------------------ mswindows/env.bat | 2 +- python/grass/app/Makefile | 4 +- python/grass/app/runtime.py | 261 +++++++++++++++++++++++++++++++ python/grass/script/setup.py | 67 +++----- 5 files changed, 324 insertions(+), 298 deletions(-) create mode 100644 python/grass/app/runtime.py diff --git a/lib/init/grass.py b/lib/init/grass.py index 6d9d8b3b3e1..7ac89e0c0c7 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -106,51 +106,6 @@ MACOS = sys.platform.startswith("darwin") -def decode(bytes_, encoding=ENCODING): - """Decode bytes with default locale and return (unicode) string - Adapted from grass.script.core.utils. - - No-op if parameter is not bytes (assumed unicode string). - - :param bytes bytes_: the bytes to decode - :param encoding: encoding to be used, default value is the system's default - encoding or, if that cannot be determined, 'UTF-8'. - """ - if isinstance(bytes_, str): - return bytes_ - elif isinstance(bytes_, bytes): - return bytes_.decode(encoding) - else: - # if something else than text - raise TypeError("can only accept types str and bytes") - - -def encode(string, encoding=ENCODING): - """Encode string with default locale and return bytes with that encoding - Adapted from grass.script.core.utils. - - No-op if parameter is bytes (assumed already encoded). - This ensures garbage in, garbage out. - - :param str string: the string to encode - :param encoding: encoding to be used, default value is the system's default - encoding or, if that cannot be determined, 'UTF-8'. - """ - if isinstance(string, bytes): - return string - elif isinstance(string, str): - return string.encode(encoding) - else: - # if something else than text - raise TypeError("can only accept types str and bytes") - - -# see https://trac.osgeo.org/grass/ticket/3508 -def to_text_string(obj, encoding=ENCODING): - """Convert `obj` to (unicode) text string""" - return decode(obj, encoding=encoding) - - def try_remove(path): try: os.remove(path) @@ -433,6 +388,7 @@ def get_grass_config_dir(): Configuration directory is for example used for grass env file (the one which caries mapset settings from session to session). """ + # The code is in sync with grass.app.runtime (but not the same). if WINDOWS: grass_config_dirname = f"GRASS{GRASS_VERSION_MAJOR}" win_conf_path = os.getenv("APPDATA") @@ -452,6 +408,9 @@ def get_grass_config_dir(): ) ) directory = os.path.join(win_conf_path, grass_config_dirname) + elif MACOS: + version = f"{GRASS_VERSION_MAJOR}.{GRASS_VERSION_MINOR}" + return os.path.join(os.getenv("HOME"), "Library", "GRASS", version) else: grass_config_dirname = f".grass{GRASS_VERSION_MAJOR}" directory = os.path.join(os.getenv("HOME"), grass_config_dirname) @@ -672,196 +631,6 @@ def read_gui(gisrc, default_gui): return grass_gui -def path_prepend(directory, var): - path = os.getenv(var) - if path: - path = directory + os.pathsep + path - else: - path = directory - os.environ[var] = path - - -def path_append(directory, var): - path = os.getenv(var) - if path: - path = path + os.pathsep + directory - else: - path = directory - os.environ[var] = path - - -def set_paths(grass_config_dir): - # addons (path) - addon_path = os.getenv("GRASS_ADDON_PATH") - if addon_path: - for path in addon_path.split(os.pathsep): - path_prepend(addon_path, "PATH") - - # addons (base) - addon_base = os.getenv("GRASS_ADDON_BASE") - if not addon_base: - if MACOS: - version = f"{GRASS_VERSION_MAJOR}.{GRASS_VERSION_MINOR}" - addon_base = os.path.join( - os.getenv("HOME"), "Library", "GRASS", version, "Addons" - ) - else: - addon_base = os.path.join(grass_config_dir, "addons") - os.environ["GRASS_ADDON_BASE"] = addon_base - if not WINDOWS: - path_prepend(os.path.join(addon_base, "scripts"), "PATH") - path_prepend(os.path.join(addon_base, "bin"), "PATH") - - # standard installation - if not WINDOWS: - path_prepend(gpath("scripts"), "PATH") - path_prepend(gpath("bin"), "PATH") - - # Set PYTHONPATH to find GRASS Python modules - if os.path.exists(gpath("etc", "python")): - pythonpath = gpath("etc", "python") - path_prepend(pythonpath, "PYTHONPATH") - # the env var PYTHONPATH is only evaluated when python is started, - # thus: - sys.path.append(pythonpath) - # now we can import stuff from grass package - - # set path for the GRASS man pages - grass_man_path = gpath("docs", "man") - addons_man_path = os.path.join(addon_base, "docs", "man") - man_path = os.getenv("MANPATH") - sys_man_path = None - if man_path: - path_prepend(addons_man_path, "MANPATH") - path_prepend(grass_man_path, "MANPATH") - else: - try: - nul = open(os.devnull, "w") - p = Popen(["manpath"], stdout=subprocess.PIPE, stderr=nul) - nul.close() - s = p.stdout.read() - p.wait() - sys_man_path = s.strip() - except: - pass - - if sys_man_path: - os.environ["MANPATH"] = to_text_string(sys_man_path) - path_prepend(addons_man_path, "MANPATH") - path_prepend(grass_man_path, "MANPATH") - else: - os.environ["MANPATH"] = to_text_string(addons_man_path) - path_prepend(grass_man_path, "MANPATH") - - # Set LD_LIBRARY_PATH (etc) to find GRASS shared libraries - # this works for subprocesses but won't affect the current process - if LD_LIBRARY_PATH_VAR: - path_prepend(gpath("lib"), LD_LIBRARY_PATH_VAR) - - -def find_exe(pgm): - for directory in os.getenv("PATH").split(os.pathsep): - path = os.path.join(directory, pgm) - if os.access(path, os.X_OK): - return path - return None - - -def set_defaults(): - # GRASS_PAGER - if not os.getenv("GRASS_PAGER"): - if find_exe("more"): - pager = "more" - elif find_exe("less"): - pager = "less" - elif WINDOWS: - pager = "more" - else: - pager = "cat" - os.environ["GRASS_PAGER"] = pager - - # GRASS_PYTHON - if not os.getenv("GRASS_PYTHON"): - if WINDOWS: - os.environ["GRASS_PYTHON"] = "python3.exe" - else: - os.environ["GRASS_PYTHON"] = "python3" - - # GRASS_GNUPLOT - if not os.getenv("GRASS_GNUPLOT"): - os.environ["GRASS_GNUPLOT"] = "gnuplot -persist" - - # GRASS_PROJSHARE - if not os.getenv("GRASS_PROJSHARE"): - os.environ["GRASS_PROJSHARE"] = CONFIG_PROJSHARE - - -def set_display_defaults(): - """Predefine monitor size for certain architectures""" - if os.getenv("HOSTTYPE") == "arm": - # small monitor on ARM (iPAQ, zaurus... etc) - os.environ["GRASS_RENDER_HEIGHT"] = "320" - os.environ["GRASS_RENDER_WIDTH"] = "240" - - -def set_browser(): - # GRASS_HTML_BROWSER - browser = os.getenv("GRASS_HTML_BROWSER") - if not browser: - if MACOS: - # OSX doesn't execute browsers from the shell PATH - route through a - # script - browser = gpath("etc", "html_browser_mac.sh") - os.environ["GRASS_HTML_BROWSER_MACOSX"] = "-b com.apple.helpviewer" - - if WINDOWS: - browser = "start" - elif CYGWIN: - browser = "explorer" - else: - # the usual suspects - browsers = [ - "xdg-open", - "x-www-browser", - "htmlview", - "konqueror", - "mozilla", - "mozilla-firefox", - "firefox", - "iceweasel", - "opera", - "google-chrome", - "chromium", - "netscape", - "dillo", - "lynx", - "links", - "w3c", - ] - for b in browsers: - if find_exe(b): - browser = b - break - - elif MACOS: - # OSX doesn't execute browsers from the shell PATH - route through a - # script - os.environ["GRASS_HTML_BROWSER_MACOSX"] = "-b %s" % browser - browser = gpath("etc", "html_browser_mac.sh") - - if not browser: - # even so we set to 'xdg-open' as a generic fallback - browser = "xdg-open" - - os.environ["GRASS_HTML_BROWSER"] = browser - - -def ensure_home(): - """Set HOME if not set on MS Windows""" - if WINDOWS and not os.getenv("HOME"): - os.environ["HOME"] = os.path.join(os.getenv("HOMEDRIVE"), os.getenv("HOMEPATH")) - - def create_initial_gisrc(filename): # for convenience, define GISDBASE as pwd: s = ( @@ -2243,15 +2012,12 @@ def get_username(): user = os.getenv("USER") if not user: user = os.getenv("LOGNAME") - if not user: + if not user and (whoami_executable := shutil.which("whoami")): try: - p = Popen(["whoami"], stdout=subprocess.PIPE) - s = p.stdout.read() - p.wait() - user = s.strip() - if type(user) is bytes: - user = decode(user) - except: + user = subprocess.run( + [whoami_executable], stdout=subprocess.PIPE, text=True, check=True + ).stdout.strip() + except (OSError, subprocess.SubprocessError): pass if not user: user = "user_%d" % os.getuid() @@ -2455,6 +2221,20 @@ def validate_cmdline(params): # without --exec (usefulness to be evaluated). +def find_grass_python_package(): + """Find path to grass package and add it to path""" + if os.path.exists(gpath("etc", "python")): + path_to_package = gpath("etc", "python") + sys.path.append(path_to_package) + # now we can import stuff from grass package + else: + # Not translatable because we don't have translations loaded. + raise RuntimeError( + "The grass Python package is missing. " + "Is the installation of GRASS GIS complete?" + ) + + def main(): """The main function which does the whole setup and run procedure @@ -2527,14 +2307,28 @@ def main(): # Create the session grassrc file gisrc = create_gisrc(tmpdir, gisrcrc) + find_grass_python_package() + + from grass.app.runtime import ( + ensure_home, + set_paths, + set_defaults, + set_display_defaults, + set_browser, + ) + ensure_home() # Set PATH, PYTHONPATH, ... - set_paths(grass_config_dir=grass_config_dir) + set_paths( + install_path=GISBASE, + grass_config_dir=grass_config_dir, + ld_library_path_variable_name=LD_LIBRARY_PATH_VAR, + ) # Set GRASS_PAGER, GRASS_PYTHON, GRASS_GNUPLOT, GRASS_PROJSHARE - set_defaults() - set_display_defaults() + set_defaults(config_projshare_path=CONFIG_PROJSHARE) # Set GRASS_HTML_BROWSER - set_browser() + set_browser(install_path=GISBASE) + set_display_defaults() # First time user - GISRC is defined in the GRASS script if not os.access(gisrc, os.F_OK): diff --git a/mswindows/env.bat b/mswindows/env.bat index 5f6687644fa..1a4e60fd9d3 100644 --- a/mswindows/env.bat +++ b/mswindows/env.bat @@ -12,7 +12,7 @@ set GDAL_DATA=%GISBASE%\share\gdal set FONTCONFIG_FILE=%GISBASE%\etc\fonts.conf -set PATH=%GISBASE%\extrabin;%GISBASE%\bin;%PYTHONHOME%;%PATH% +set PATH=%GISBASE%\extrabin;%PYTHONHOME%;%PATH% REM set RStudio temporarily to %PATH% if it exists diff --git a/python/grass/app/Makefile b/python/grass/app/Makefile index e76586dd761..1e00ee90b66 100644 --- a/python/grass/app/Makefile +++ b/python/grass/app/Makefile @@ -5,7 +5,9 @@ include $(MODULE_TOPDIR)/include/Make/Python.make DSTDIR = $(ETC)/python/grass/app -MODULES = data +MODULES = \ + data \ + runtime PYFILES := $(patsubst %,$(DSTDIR)/%.py,$(MODULES) __init__) PYCFILES := $(patsubst %,$(DSTDIR)/%.pyc,$(MODULES) __init__) diff --git a/python/grass/app/runtime.py b/python/grass/app/runtime.py new file mode 100644 index 00000000000..48ff8cde92b --- /dev/null +++ b/python/grass/app/runtime.py @@ -0,0 +1,261 @@ +"""Provides functions for the main GRASS GIS executable + +(C) 2024 by Vaclav Petras and the GRASS Development Team + +This program is free software under the GNU General Public +License (>=v2). Read the file COPYING that comes with GRASS +for details. + +.. sectionauthor:: Vaclav Petras + +This is not a stable part of the API. Use at your own risk. +""" + +import collections +import os +import shutil +import subprocess +import sys + +# Get the system name +WINDOWS = sys.platform.startswith("win") +CYGWIN = sys.platform.startswith("cygwin") +MACOS = sys.platform.startswith("darwin") + + +def get_grass_config_dir(major_version, minor_version, env): + """Get configuration directory + + Determines path of GRASS GIS user configuration directory. + """ + # The code is in sync with grass.app.runtime (but not the same). + if WINDOWS: + config_dirname = f"GRASS{major_version}" + return os.path.join(env.get("APPDATA"), config_dirname) + elif MACOS: + version = f"{major_version}.{minor_version}" + return os.path.join(env.get("HOME"), "Library", "GRASS", version) + else: + config_dirname = f".grass{major_version}" + return os.path.join(env.get("HOME"), config_dirname) + + +def append_left_main_executable_paths(paths, install_path): + """Add executables to PATH""" + paths.appendleft(os.path.join(install_path, "bin")) + if WINDOWS: + # Standalone installer has dependencies which are on path in other cases. + path = os.path.join(install_path, "extrabin") + if os.path.exists(path): + paths.appendleft(path) + else: + # Without FHS, scripts are separated like in the source code. + path = os.path.join(install_path, "scripts") + if os.path.exists(path): + paths.appendleft(path) + + +def append_left_addon_paths(paths, config_dir, env): + """Add addons to path""" + # addons (base) + addon_base = env.get("GRASS_ADDON_BASE") + if not addon_base: + if MACOS: + name = "Addons" + else: + name = "addons" + addon_base = os.path.join(config_dir, name) + env["GRASS_ADDON_BASE"] = addon_base + + if not WINDOWS: + script_path = os.path.join(addon_base, "scripts") + if os.path.exists(script_path): + paths.appendleft(script_path) + paths.appendleft(os.path.join(addon_base, "bin")) + + # addons (path) + addon_path = env.get("GRASS_ADDON_PATH") + if addon_path: + for path in addon_path.split(os.pathsep): + paths.appendleft(path) + + +def set_executable_paths(install_path, grass_config_dir, env): + """Add paths with executables to PATH in _env_""" + paths = collections.deque() + # Addons + append_left_addon_paths(paths, grass_config_dir, env=env) + # Standard installation + append_left_main_executable_paths(paths, install_path=install_path) + + paths.append(env.get("PATH")) + env["PATH"] = os.pathsep.join(paths) + + +def set_paths(install_path, grass_config_dir, ld_library_path_variable_name): + """Set variables with executable paths, library paths, and other paths""" + set_executable_paths( + install_path=install_path, grass_config_dir=grass_config_dir, env=os.environ + ) + # Set LD_LIBRARY_PATH (etc) to find GRASS shared libraries + # this works for subprocesses but won't affect the current process + if ld_library_path_variable_name: + set_dynamic_library_path( + variable_name=ld_library_path_variable_name, + install_path=install_path, + env=os.environ, + ) + set_python_path_variable(install_path=install_path, env=os.environ) + + # retrieving second time, but now it is always set + addon_base = os.getenv("GRASS_ADDON_BASE") + set_man_path(install_path=install_path, addon_base=addon_base, env=os.environ) + + +def set_man_path(install_path, addon_base, env): + """Set path for the GRASS man pages""" + grass_man_path = os.path.join(install_path, "docs", "man") + addons_man_path = os.path.join(addon_base, "docs", "man") + man_path = env.get("MANPATH") + paths = collections.deque() + if man_path: + paths.appendleft(man_path) + else: + system_path = None + if manpath_executable := shutil.which("manpath"): + try: + system_path = subprocess.run( + [manpath_executable], + text=True, + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + timeout=2, + ).stdout.strip() + except (OSError, subprocess.SubprocessError): + pass + + if system_path: + # Variable does not exist, but the system has the information. + paths.appendleft(system_path) + paths.appendleft(addons_man_path) + paths.appendleft(grass_man_path) + os.environ["MANPATH"] = os.pathsep.join(paths) + + +def set_dynamic_library_path(variable_name, install_path, env): + """Define path to dynamic libraries (LD_LIBRARY_PATH on Linux)""" + if variable_name not in env: + env[variable_name] = "" + env[variable_name] += os.pathsep + os.path.join(install_path, "lib") + + +def set_python_path_variable(install_path, env): + """Set PYTHONPATH to find GRASS Python package in subprocesses""" + path = env.get("PYTHONPATH") + etcpy = os.path.join(install_path, "etc", "python") + if path: + path = etcpy + os.pathsep + path + else: + path = etcpy + env["PYTHONPATH"] = path + + +def set_path_to_python_executable(env): + """Set GRASS_PYTHON environment variable""" + if not env.get("GRASS_PYTHON"): + if WINDOWS: + env["GRASS_PYTHON"] = "python3.exe" + else: + env["GRASS_PYTHON"] = "python3" + + +def set_defaults(config_projshare_path): + """Set paths or commands for dependencies and auxiliary utilities""" + # GRASS_PAGER + if not os.getenv("GRASS_PAGER"): + if shutil.which("more"): + pager = "more" + elif shutil.which("less"): + pager = "less" + elif WINDOWS: + pager = "more" + else: + pager = "cat" + os.environ["GRASS_PAGER"] = pager + + # GRASS_PYTHON + set_path_to_python_executable(env=os.environ) + + # GRASS_GNUPLOT + if not os.getenv("GRASS_GNUPLOT"): + os.environ["GRASS_GNUPLOT"] = "gnuplot -persist" + + # GRASS_PROJSHARE + if not os.getenv("GRASS_PROJSHARE") and config_projshare_path: + os.environ["GRASS_PROJSHARE"] = config_projshare_path + + +def set_display_defaults(): + """Predefine monitor size for certain architectures""" + if os.getenv("HOSTTYPE") == "arm": + # small monitor on ARM (iPAQ, zaurus... etc) + os.environ["GRASS_RENDER_HEIGHT"] = "320" + os.environ["GRASS_RENDER_WIDTH"] = "240" + + +def set_browser(install_path): + """Set path to HTML browser""" + # GRASS_HTML_BROWSER + browser = os.getenv("GRASS_HTML_BROWSER") + if not browser: + if MACOS: + # OSX doesn't execute browsers from the shell PATH - route through a script + browser = os.path.join(install_path, "etc", "html_browser_mac.sh") + os.environ["GRASS_HTML_BROWSER_MACOSX"] = "-b com.apple.helpviewer" + + if WINDOWS: + browser = "start" + elif CYGWIN: + browser = "explorer" + else: + # the usual suspects + browsers = [ + "xdg-open", + "x-www-browser", + "htmlview", + "konqueror", + "mozilla", + "mozilla-firefox", + "firefox", + "iceweasel", + "opera", + "google-chrome", + "chromium", + "netscape", + "dillo", + "lynx", + "links", + "w3c", + ] + for candidate in browsers: + if shutil.which(candidate): + browser = candidate + break + + elif MACOS: + # OSX doesn't execute browsers from the shell PATH - route through a script + os.environ["GRASS_HTML_BROWSER_MACOSX"] = "-b %s" % browser + browser = os.path.join(install_path, "etc", "html_browser_mac.sh") + + if not browser: + # even so we set to 'xdg-open' as a generic fallback + browser = "xdg-open" + + os.environ["GRASS_HTML_BROWSER"] = browser + + +def ensure_home(): + """Set HOME if not set on MS Windows""" + if WINDOWS and not os.getenv("HOME"): + os.environ["HOME"] = os.path.join(os.getenv("HOMEDRIVE"), os.getenv("HOMEPATH")) diff --git a/python/grass/script/setup.py b/python/grass/script/setup.py index dd6fd137485..cadf05bbe38 100644 --- a/python/grass/script/setup.py +++ b/python/grass/script/setup.py @@ -215,57 +215,26 @@ def setup_runtime_env(gisbase=None, *, env=None): if not env: env = os.environ + from grass.app.runtime import ( + get_grass_config_dir, + set_dynamic_library_path, + set_executable_paths, + set_path_to_python_executable, + set_python_path_variable, + ) + # Set GISBASE env["GISBASE"] = gisbase - - # define PATH - path_addition = os.pathsep + os.path.join(gisbase, "bin") - path_addition += os.pathsep + os.path.join(gisbase, "scripts") - if WINDOWS: - path_addition += os.pathsep + os.path.join(gisbase, "extrabin") - - # add addons to the PATH, use GRASS_ADDON_BASE if set - # copied and simplified from lib/init/grass.py - addon_base = env.get("GRASS_ADDON_BASE") - if not addon_base: - if WINDOWS: - config_dirname = f"GRASS{VERSION_MAJOR}" - addon_base = os.path.join(env.get("APPDATA"), config_dirname, "addons") - elif MACOS: - version = f"{VERSION_MAJOR}.{VERSION_MINOR}" - addon_base = os.path.join( - env.get("HOME"), "Library", "GRASS", version, "Addons" - ) - else: - config_dirname = f".grass{VERSION_MAJOR}" - addon_base = os.path.join(env.get("HOME"), config_dirname, "addons") - env["GRASS_ADDON_BASE"] = addon_base - - if not WINDOWS: - path_addition += os.pathsep + os.path.join(addon_base, "scripts") - path_addition += os.pathsep + os.path.join(addon_base, "bin") - - env["PATH"] = path_addition + os.pathsep + env.get("PATH") - - # define LD_LIBRARY_PATH - if "@LD_LIBRARY_PATH_VAR@" not in env: - env["@LD_LIBRARY_PATH_VAR@"] = "" - env["@LD_LIBRARY_PATH_VAR@"] += os.pathsep + os.path.join(gisbase, "lib") - - # Set GRASS_PYTHON and PYTHONPATH to find GRASS Python modules - if not env.get("GRASS_PYTHON"): - if WINDOWS: - env["GRASS_PYTHON"] = "python3.exe" - else: - env["GRASS_PYTHON"] = "python3" - - path = env.get("PYTHONPATH") - etcpy = os.path.join(gisbase, "etc", "python") - if path: - path = etcpy + os.pathsep + path - else: - path = etcpy - env["PYTHONPATH"] = path + set_executable_paths( + install_path=gisbase, + grass_config_dir=get_grass_config_dir(VERSION_MAJOR, VERSION_MINOR, env=env), + env=env, + ) + set_dynamic_library_path( + variable_name="@LD_LIBRARY_PATH_VAR@", install_path=gisbase, env=env + ) + set_python_path_variable(install_path=gisbase, env=env) + set_path_to_python_executable(env=env) def init(path, location=None, mapset=None, *, grass_path=None, env=None): From 3f124c5c789681a9f7996ebbea4d7c43b9478415 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Sun, 23 Jun 2024 17:32:51 -0400 Subject: [PATCH 010/293] d.linegraph: Fix null pointer issue in qsort call (#3878) * Fix null pointer issue in qsort call in d.linegraph * Fix formatting issue in d.linegraph * Fixing formatting issue in d.linegraph --------- Co-authored-by: Shubham Vasudeo Desai --- display/d.linegraph/main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/display/d.linegraph/main.c b/display/d.linegraph/main.c index e49b9f7b8d0..9399b1d16c0 100644 --- a/display/d.linegraph/main.c +++ b/display/d.linegraph/main.c @@ -126,7 +126,9 @@ static char *icon_files(void) closedir(dir); - qsort(list, count, sizeof(char *), cmp); + if (list != NULL) { + qsort(list, count, sizeof(char *), cmp); + } if (len > 0) { ret = G_malloc((len + 1) * sizeof(char)); /* \0 */ From f825a223c28f7cd5ab6d075bd02b8f5e73956c4a Mon Sep 17 00:00:00 2001 From: Chung-Yuan Liang <77927944+cyliang368@users.noreply.github.com> Date: Mon, 24 Jun 2024 13:23:51 -0400 Subject: [PATCH 011/293] v.surf.rst: Cross-validation OpenMP support (#3590) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * v.surf.rst: cv openmp support * Update main.c Use /* ... */ instead of // for consistency and compatibility with pre-99 C standard (https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Comments.html) * Update main.c Fix a string in `#if 0` * Update vector/v.surf.rst/main.c clang-format * v.surf.rst: devi openmp support * Update profile.sh remove nproc argument * Apply suggestions from code review * v.surf.rst: parallelize point2d.c * v.surf.rst: add tests for multithread cv & dev * v.surf.rst: point2d_parallel.c, function, test updates * clean formats * update document and remove unused lines * replace replicate part in point2d.c by write_point_2d * add benchmark for parallelization * remove unused variables in point2d.c * remove c-flag from vsurfrst.py benchmark Co-authored-by: Anna Petrasova * correct the number in the label in benchmark_v_surf_rst_cv.py * include benmarks into the document page * remove unnecessary changes in the html file * remove syntax error in v.surf.rst.html * revise performance and author sections in the html file * remove invalid syntax in the html file * revise the description of performance in the html file Co-authored-by: Anna Petrasova * breakdown long lines in v.surf.rst.html --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Huidae Cho Co-authored-by: Edouard Choinière <27212526+echoix@users.noreply.github.com> Co-authored-by: Anna Petrasova --- lib/rst/interp_float/interpf.h | 35 ++-- lib/rst/interp_float/point2d.c | 114 ++++++------ lib/rst/interp_float/point2d_parallel.c | 112 ++++++++++++ lib/rst/interp_float/ressegm2d.c | 6 +- lib/rst/interp_float/segmen2d.c | 4 +- lib/rst/interp_float/segmen2d_parallel.c | 167 +++++++++++------- .../benchmark/benchmark_v_surf_rst.py | 50 ++++++ .../benchmark/benchmark_v_surf_rst_cv.py | 51 ++++++ vector/v.surf.rst/main.c | 28 +-- vector/v.surf.rst/testsuite/test_vsurfrst.py | 55 +++++- vector/v.surf.rst/v.surf.rst.html | 34 +++- vector/v.surf.rst/vsurfrst_benchmark.png | Bin 0 -> 43502 bytes vector/v.surf.rst/vsurfrst_cv_benchmark.png | Bin 0 -> 57290 bytes 13 files changed, 503 insertions(+), 153 deletions(-) create mode 100644 lib/rst/interp_float/point2d_parallel.c create mode 100644 vector/v.surf.rst/benchmark/benchmark_v_surf_rst.py create mode 100644 vector/v.surf.rst/benchmark/benchmark_v_surf_rst_cv.py create mode 100644 vector/v.surf.rst/vsurfrst_benchmark.png create mode 100644 vector/v.surf.rst/vsurfrst_cv_benchmark.png diff --git a/lib/rst/interp_float/interpf.h b/lib/rst/interp_float/interpf.h index 576b660ce29..a4055a41e8a 100644 --- a/lib/rst/interp_float/interpf.h +++ b/lib/rst/interp_float/interpf.h @@ -50,7 +50,7 @@ typedef int matrix_create_fn(struct interp_params *, struct triple *, int, double **, int *); typedef int check_points_fn(struct interp_params *, struct quaddata *, double *, - double *, double, double, struct triple); + double *, double, double, struct triple *); typedef int secpar_fn(struct interp_params *, int, int, int, struct BM *, double *, double *, double *, double *, double *, @@ -68,15 +68,19 @@ struct interp_params { FILE *fdinp; /**< input stream */ - int elatt; /**< which floating point attr to use? first = 1, second = 2, etc + int elatt; /**< which floating point attr to + * use? first = 1, second = 2, etc */ - int smatt; /**< which floating point attr to use for smoothing? first = 1, - second = 2, etc */ + int smatt; /**< which floating point attr to use + for smoothing? first = 1, second = + 2, etc */ - int kmin; /**< min number of points per segment for interpolation */ + int kmin; /**< min number of points per segment + for interpolation */ - int kmax; /**< max number of points per segment */ + int kmax; /**< max number of points per segment + */ char *maskmap; /**< name of mask */ @@ -87,9 +91,11 @@ struct interp_params { double fi; /**< tension */ - int KMAX2; /**< max num. of points for interp. */ + int KMAX2; /**< max num. of points for interp. + */ - int scik1, scik2, scik3; /**< multipliers for interp. values */ + int scik1, scik2, scik3; /**< multipliers for + interp. values */ double rsm; /**< smoothing */ @@ -102,7 +108,8 @@ struct interp_params { int deriv, cv; /**< 1 if compute partial derivs */ - double theta; /**< anisotropy angle, 0=East,counter-clockwise */ + double theta; /**< anisotropy angle, + 0=East,counter-clockwise */ double scalex; /**< anisotropy scaling factor */ @@ -149,6 +156,7 @@ void IL_init_params_2d(struct interp_params *, FILE *, int, int, double, int, void IL_init_func_2d(struct interp_params *, grid_calc_fn *, matrix_create_fn *, check_points_fn *, secpar_fn *, interp_fn *, interpder_fn *, wr_temp_fn *); + /* input2d.c */ int IL_input_data_2d(struct interp_params *, struct tree_info *, double *, double *, double *, double *, double *, double *, int *); @@ -183,7 +191,14 @@ int IL_output_2d(struct interp_params *, struct Cell_head *, double, double, double, char *, double, int, int, int); /* point2d.c */ int IL_check_at_points_2d(struct interp_params *, struct quaddata *, double *, - double *, double, double, struct triple); + double *, double, double, struct triple *); +int IL_write_point_2d(struct triple, double); + +/* point2d_parallel.c */ +int IL_check_at_points_2d_cvdev(struct interp_params *, struct quaddata *, + double *, double *, double, double, + struct triple *); + /* resout2d.c */ /* resout2dmod.c */ int IL_resample_output_2d(struct interp_params *, double, double, double, diff --git a/lib/rst/interp_float/point2d.c b/lib/rst/interp_float/point2d.c index 3d1140a5889..8a5a0aa8d42 100644 --- a/lib/rst/interp_float/point2d.c +++ b/lib/rst/interp_float/point2d.c @@ -54,24 +54,22 @@ int IL_check_at_points_2d(struct interp_params *params, double *b, /*!< solution of linear equations */ double *ertot, /*!< total error */ double zmin, /*!< min z-value */ - double dnorm, struct triple skip_point) + double dnorm, struct triple *skip_point) { int n_points = data->n_points; /* number of points */ struct triple *points = data->points; /* points for interpolation */ + struct triple point_writeout; double east = data->xmax; double west = data->x_orig; double north = data->ymax; double south = data->y_orig; double /* rfsta2, errmax, */ h, xx, yy, r2, hz, zz, err, xmm, ymm, r; double skip_err; - int /* n1, */ mm, m, cat; + int /* n1, */ mm, m; /* double fstar2; */ int inside; - /* Site *site; */ - char buf[1024]; - /* if ((site = G_site_new_struct (-1, 2, 0, 1)) == NULL) G_fatal_error ("Memory error for site struct"); */ @@ -109,30 +107,10 @@ int IL_check_at_points_2d(struct interp_params *params, if (params->create_devi) { if (inside) { /* if the point is inside the region */ - Vect_reset_line(Pnts); - Vect_reset_cats(Cats2); - - Vect_append_point(Pnts, xmm, ymm, zz); - cat = count; - Vect_cat_set(Cats2, 1, cat); - Vect_write_line(&Map2, GV_POINT, Pnts, Cats2); - - db_zero_string(&sql2); - sprintf(buf, "insert into %s values ( %d ", ff->table, cat); - db_append_string(&sql2, buf); - - sprintf(buf, ", %f", err); - db_append_string(&sql2, buf); - db_append_string(&sql2, ")"); - G_debug(3, "IL_check_at_points_2d: %s", db_get_string(&sql2)); - - if (db_execute_immediate(driver2, &sql2) != DB_OK) { - db_close_database(driver2); - db_shutdown_driver(driver2); - G_fatal_error("Cannot insert new row: %s", - db_get_string(&sql2)); - } - count++; + point_writeout.x = xmm; + point_writeout.y = ymm; + point_writeout.z = zz; + IL_write_point_2d(point_writeout, err); } } (*ertot) += err * err; @@ -142,8 +120,8 @@ int IL_check_at_points_2d(struct interp_params *params, if (params->cv) { h = b[0]; for (m = 1; m <= n_points - 1; m++) { - xx = points[m - 1].x - skip_point.x; - yy = points[m - 1].y - skip_point.y; + xx = points[m - 1].x - skip_point->x; + yy = points[m - 1].y - skip_point->y; r2 = yy * yy + xx * xx; if (r2 != 0.) { /* rfsta2 = fstar2 * r2; */ @@ -152,10 +130,10 @@ int IL_check_at_points_2d(struct interp_params *params, } } hz = h + zmin; - zz = skip_point.z + zmin; + zz = skip_point->z + zmin; skip_err = hz - zz; - xmm = skip_point.x * dnorm + params->x_orig + west; - ymm = skip_point.y * dnorm + params->y_orig + south; + xmm = skip_point->x * dnorm + params->x_orig + west; + ymm = skip_point->y * dnorm + params->y_orig + south; if ((xmm >= west + params->x_orig) && (xmm <= east + params->x_orig) && (ymm >= south + params->y_orig) && (ymm <= north + params->y_orig)) @@ -164,32 +142,52 @@ int IL_check_at_points_2d(struct interp_params *params, inside = 0; if (inside) { /* if the point is inside the region */ - Vect_reset_line(Pnts); - Vect_reset_cats(Cats2); - - Vect_append_point(Pnts, xmm, ymm, zz); - cat = count; - Vect_cat_set(Cats2, 1, cat); - Vect_write_line(&Map2, GV_POINT, Pnts, Cats2); - - db_zero_string(&sql2); - sprintf(buf, "insert into %s values ( %d ", ff->table, cat); - db_append_string(&sql2, buf); - - sprintf(buf, ", %f", skip_err); - db_append_string(&sql2, buf); - db_append_string(&sql2, ")"); - G_debug(3, "IL_check_at_points_2d: %s", db_get_string(&sql2)); - - if (db_execute_immediate(driver2, &sql2) != DB_OK) { - db_close_database(driver2); - db_shutdown_driver(driver2); - G_fatal_error("Cannot insert new row: %s", - db_get_string(&sql2)); - } - count++; + point_writeout.x = xmm; + point_writeout.y = ymm; + point_writeout.z = zz; + IL_write_point_2d(point_writeout, skip_err); } } /* cv */ return 1; } + +/*! + * \brief A function to write out point and deviation at point to database. + * + * \param point point to write out + * \param error deviation at point + * + * \return 1 + */ + +int IL_write_point_2d(struct triple point, double err) +{ + + char buf[1024]; + + Vect_reset_line(Pnts); + Vect_reset_cats(Cats2); + + Vect_append_point(Pnts, point.x, point.y, point.z); + Vect_cat_set(Cats2, 1, count); + Vect_write_line(&Map2, GV_POINT, Pnts, Cats2); + + db_zero_string(&sql2); + sprintf(buf, "insert into %s values ( %d ", ff->table, count); + db_append_string(&sql2, buf); + + sprintf(buf, ", %f", err); + db_append_string(&sql2, buf); + db_append_string(&sql2, ")"); + G_debug(3, "IL_check_at_points_2d: %s", db_get_string(&sql2)); + + if (db_execute_immediate(driver2, &sql2) != DB_OK) { + db_close_database(driver2); + db_shutdown_driver(driver2); + G_fatal_error("Cannot insert new row: %s", db_get_string(&sql2)); + } + count++; + + return 1; +} diff --git a/lib/rst/interp_float/point2d_parallel.c b/lib/rst/interp_float/point2d_parallel.c new file mode 100644 index 00000000000..b345b868a8d --- /dev/null +++ b/lib/rst/interp_float/point2d_parallel.c @@ -0,0 +1,112 @@ +/*! + * \file point2d_parallel.c + * + * \author + * Lubos Mitas (original program and various modifications) + * + * \author + * C.Y. Liang, + * H. Mitasova, + * I. Kosinovsky, + * D. Gerdes, + * D. McCauley + * (GRASS4.1 version of the program and GRASS4.2 modifications) + * + * \author modified by Liang in May 2024 + * \author modified by McCauley in August 1995 + * \author modified by Mitasova in August 1995, Nov. 1996 + * + * \copyright + * (C) 1993-2006 by Helena Mitasova and the GRASS Development Team + * + * \copyright + * This program is free software under the + * GNU General Public License (>=v2). + * Read the file COPYING that comes with GRASS for details. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* needed for AIX */ +#ifdef hz +#undef hz +#endif + +/*! + * \brief A parallel version of IL_check_at_points_2d. + * Sperate the cross-validation/deviation computing part + * and the database writing part. + * + * \param params interpolation parameters + * \param data data in the segment for computing, NULL for writing out + * \param b solution of linear equations for computing, NULL for writing out + * \param ertot total error for computing, point at single point for writing out + * \param zmin min z-value for computing + * \param dnorm normalization factor for computing + * \param target_point point for computing or writing out + * + * \return 1 + * + * \todo + * Alternative description: + * ...calculate the maximum and RMS deviation caused by smoothing. + */ + +int IL_check_at_points_2d_cvdev(struct interp_params *params, + struct quaddata *data, /*!< current region */ + double *b, /*!< solution of linear equations */ + double *ertot, /*!< total error */ + double zmin, /*!< min z-value */ + double dnorm, struct triple *target_point) +{ + if ((data != NULL) && (b != NULL)) { + int n_points = data->n_points; /* number of points */ + struct triple *points = data->points; /* points for interpolation */ + double west = data->x_orig; + double south = data->y_orig; + double /* rfsta2, errmax, */ h, xx, yy, r2, hz, zz, err, xmm, ymm, r; + int /* n1, */ m; + + if (params->cv) { /* one point is skipped for cross-validation*/ + n_points -= 1; + } + + h = b[0]; + for (m = 1; m <= n_points; m++) { + xx = target_point->x - points[m - 1].x; + yy = target_point->y - points[m - 1].y; + r2 = yy * yy + xx * xx; + if (r2 != 0.) { + /* rfsta2 = fstar2 * r2; */ + r = r2; + h = h + b[m] * params->interp(r, params->fi); + } + } + /* modified by helena january 1997 - normalization of z was + removed from segm2d.c and interp2d.c + hz = (h * dnorm) + zmin; + zz = (points[mm - 1].z * dnorm) + zmin; + */ + hz = h + zmin; + zz = target_point->z + zmin; + err = hz - zz; + xmm = target_point->x * dnorm + params->x_orig + west; + ymm = target_point->y * dnorm + params->y_orig + south; + (*ertot) += err * err; + + /* take the values out*/ + target_point->x = xmm; + target_point->y = ymm; + target_point->z = err; + } + else { + IL_write_point_2d(*target_point, *ertot); + } + return 1; +} diff --git a/lib/rst/interp_float/ressegm2d.c b/lib/rst/interp_float/ressegm2d.c index f761cee6181..a22d6f82fbb 100644 --- a/lib/rst/interp_float/ressegm2d.c +++ b/lib/rst/interp_float/ressegm2d.c @@ -190,7 +190,7 @@ int IL_resample_interp_segments_2d( b[0] = 0.; G_lubksb(matrix, m1 + 1, indx, b); - params->check_points(params, data, b, ertot, zmin, *dnorm, triple); + params->check_points(params, data, b, ertot, zmin, *dnorm, &triple); if (params->grid_calc(params, data, bitmask, zmin, zmax, zminac, zmaxac, gmin, gmax, c1min, c1max, c2min, c2max, ertot, b, @@ -381,7 +381,7 @@ int IL_resample_interp_segments_2d( G_lubksb(new_matrix, data->n_points + 1, new_indx, b); params->check_points(params, data, b, ertot, zmin, *dnorm, - triple); + &triple); if (params->grid_calc(params, data, bitmask, zmin, zmax, zminac, zmaxac, gmin, gmax, c1min, @@ -424,7 +424,7 @@ int IL_resample_interp_segments_2d( G_lubksb(matrix, data->n_points + 1, indx, b); params->check_points(params, data, b, ertot, zmin, *dnorm, - triple); + &triple); if (params->grid_calc(params, data, bitmask, zmin, zmax, zminac, zmaxac, gmin, gmax, c1min, diff --git a/lib/rst/interp_float/segmen2d.c b/lib/rst/interp_float/segmen2d.c index 50f00ae2445..f2b13951691 100644 --- a/lib/rst/interp_float/segmen2d.c +++ b/lib/rst/interp_float/segmen2d.c @@ -293,7 +293,7 @@ int IL_interp_segments_2d( G_lubksb(matrix, data->n_points + 1, indx, b); /* put here condition to skip error if not needed */ params->check_points(params, data, b, ertot, zmin, dnorm, - skip_point); + &skip_point); } else if (segtest == 1) { for (i = 0; i < data->n_points - 1; i++) @@ -301,7 +301,7 @@ int IL_interp_segments_2d( b[0] = 0.; G_lubksb(matrix, data->n_points, indx, b); params->check_points(params, data, b, ertot, zmin, dnorm, - skip_point); + &skip_point); } } /*end of cv loop */ diff --git a/lib/rst/interp_float/segmen2d_parallel.c b/lib/rst/interp_float/segmen2d_parallel.c index 688d5a4f55b..cda1a1f9ba2 100644 --- a/lib/rst/interp_float/segmen2d_parallel.c +++ b/lib/rst/interp_float/segmen2d_parallel.c @@ -125,9 +125,10 @@ int IL_interp_segments_2d_parallel( int MINPTS; double pr; struct triple *point; - struct triple skip_point; - int m_skip, skip_index, k, segtest; + struct triple target_point; + int npoints, point_index, k; double xx, yy /*, zz */; + double xmm, ymm, err, pointz; // struct quaddata *data_local; @@ -292,88 +293,124 @@ int IL_interp_segments_2d_parallel( */ } - /* cv stuff */ - if (params->cv) { - m_skip = data_local[tid]->n_points; - } - else { - m_skip = 1; + target_point.x = 0; + target_point.y = 0; + target_point.z = 0; + + /* one time interpolation and devi */ + if (!params->cv) { + if (/* params */ + IL_matrix_create_alloc(params, data_local[tid]->points, + data_local[tid]->n_points, + matrix[tid], indx[tid], + A[tid]) < 0) { + some_thread_failed = -1; + continue; + } + + for (i = 0; i < data_local[tid]->n_points; i++) { + b[tid][i + 1] = data_local[tid]->points[i].z; + } + b[tid][0] = 0.; + G_lubksb(matrix[tid], data_local[tid]->n_points + 1, + indx[tid], b[tid]); + /* put here condition to skip error if not needed */ + + if (!params->create_devi) { /* check_points only for one + time interpolation */ + params->check_points(params, data_local[tid], b[tid], + ertot, zmin, dnorm, &target_point); + } } - /* remove after cleanup - this is just for testing */ - skip_point.x = 0.; - skip_point.y = 0.; - skip_point.z = 0.; - for (skip_index = 0; skip_index < m_skip; skip_index++) { - if (params->cv) { - segtest = 0; - j = 0; - xx = point[skip_index].x * dnorm + - data_local[tid]->x_orig + params->x_orig; - yy = point[skip_index].y * dnorm + - data_local[tid]->y_orig + params->y_orig; - /* zz = point[skip_index].z; */ + npoints = (params->cv || params->create_devi) + ? data_local[tid]->n_points + : 0; + for (point_index = 0; point_index < npoints; + point_index++) { /* loop only for cv or devi*/ + if (params->cv) { /* cv: skip one point */ + /* skip point for cv */ + target_point.x = point[point_index].x; + target_point.y = point[point_index].y; + target_point.z = point[point_index].z; + + xx = target_point.x * dnorm + data_local[tid]->x_orig + + params->x_orig; + yy = target_point.y * dnorm + data_local[tid]->y_orig + + params->y_orig; + /* zz = point[point_index].z; */ + if (xx >= data_local[tid]->x_orig + params->x_orig && xx <= data_local[tid]->xmax + params->x_orig && yy >= data_local[tid]->y_orig + params->y_orig && yy <= data_local[tid]->ymax + params->y_orig) { - segtest = 1; - skip_point.x = point[skip_index].x; - skip_point.y = point[skip_index].y; - skip_point.z = point[skip_index].z; - for (k = 0; k < m_skip; k++) { - if (k != skip_index && params->cv) { + + j = 0; + for (k = 0; k < npoints; k++) { + if (k != point_index) { data_local[tid]->points[j].x = point[k].x; data_local[tid]->points[j].y = point[k].y; data_local[tid]->points[j].z = point[k].z; j++; } } - } /* segment area test */ - } - if (!params->cv) { - if (/* params */ - IL_matrix_create_alloc( - params, data_local[tid]->points, - data_local[tid]->n_points, matrix[tid], - indx[tid], A[tid]) < 0) { - some_thread_failed = -1; - continue; + + /* segment area test for cv */ + if (/* params */ + IL_matrix_create_alloc( + params, data_local[tid]->points, + data_local[tid]->n_points - 1, matrix[tid], + indx[tid], A[tid]) < 0) { + some_thread_failed = -1; + continue; + } + + for (i = 0; i < data_local[tid]->n_points - 1; + i++) { + b[tid][i + 1] = data_local[tid]->points[i].z; + } + b[tid][0] = 0.; + G_lubksb(matrix[tid], data_local[tid]->n_points, + indx[tid], b[tid]); } - } - else if (segtest == 1) { - if (/* params */ - IL_matrix_create_alloc( - params, data_local[tid]->points, - data_local[tid]->n_points - 1, matrix[tid], - indx[tid], A[tid]) < 0) { - some_thread_failed = -1; + else { continue; } + } /* cv: skip one point */ + + if (params->create_devi) { + target_point.x = data_local[tid]->points[point_index].x; + target_point.y = data_local[tid]->points[point_index].y; + target_point.z = data_local[tid]->points[point_index].z; } - if (!params->cv) { - for (i = 0; i < data_local[tid]->n_points; i++) { - b[tid][i + 1] = data_local[tid]->points[i].z; - } - b[tid][0] = 0.; - G_lubksb(matrix[tid], data_local[tid]->n_points + 1, - indx[tid], b[tid]); - /* put here condition to skip error if not needed */ - params->check_points(params, data_local[tid], b[tid], - ertot, zmin, dnorm, skip_point); - } - else if (segtest == 1) { - for (i = 0; i < data_local[tid]->n_points - 1; i++) { - b[tid][i + 1] = data_local[tid]->points[i].z; + + /* x, y, z is required input, while xmm, ymm, err output*/ + pointz = target_point.z; + params->check_points(params, data_local[tid], b[tid], ertot, + zmin, dnorm, &target_point); + + err = target_point.z; + target_point.z = pointz + zmin; + xmm = target_point.x; + ymm = target_point.y; + + /* write out vector (point), if the point is inside the + * region*/ + if (xmm >= data_local[tid]->x_orig + params->x_orig && + xmm <= data_local[tid]->xmax + params->x_orig && + ymm >= data_local[tid]->y_orig + params->y_orig && + ymm <= data_local[tid]->ymax + params->y_orig) { + /* vect append, count, vect_write, db_execute will have + * conflicts between threads */ +#pragma omp critical + { + params->check_points(params, NULL, NULL, &err, 0.0, + 0.0, &target_point); } - b[tid][0] = 0.; - G_lubksb(matrix[tid], data_local[tid]->n_points, - indx[tid], b[tid]); - params->check_points(params, data_local[tid], b[tid], - ertot, zmin, dnorm, skip_point); } - } /*end of cv loop */ + } /* end of computations for every point in cv or devi*/ + /* write out grid*/ if (!params->cv) { if ((params->Tmp_fd_z != NULL) || (params->Tmp_fd_dx != NULL) || diff --git a/vector/v.surf.rst/benchmark/benchmark_v_surf_rst.py b/vector/v.surf.rst/benchmark/benchmark_v_surf_rst.py new file mode 100644 index 00000000000..91fd784e0b3 --- /dev/null +++ b/vector/v.surf.rst/benchmark/benchmark_v_surf_rst.py @@ -0,0 +1,50 @@ +"""Benchmarking of v.surf.rst + +@author Aaron Saw Min Sern + Chung-Yuan Liang +""" + +from grass.pygrass.modules import Module +import grass.benchmark as bm +from subprocess import DEVNULL +import math + + +def main(): + results = [] + # Users can add more or modify existing reference maps + raster_cells = [1e6, 2e6, 4e6, 8e6] # 1M, 2M, 4M, 8M cells + for ncells in raster_cells: + benchmark(math.sqrt(ncells), f"v.surf.rst_{int(ncells / 1e6)}M", results) + bm.nprocs_plot(results) + + +def benchmark(size, label, results): + reference = "v_surf_rst_reference_map" + output = "benchmark_v_surf_rst_nprocs" + + npoints = size * size * 0.1 + generate_map(npoints=npoints, rows=size, cols=size, fname=reference) + + module = Module( + "v.surf.rst", + input=reference, + npmin=100, + elevation=output, + stdout_=DEVNULL, + overwrite=True, + ) + + results.append(bm.benchmark_nprocs(module, label=label, max_nprocs=8, repeat=3)) + Module("g.remove", quiet=True, flags="f", type="raster", name=reference) + Module("g.remove", quiet=True, flags="f", type="raster", name=output) + + +def generate_map(npoints, rows, cols, fname): + Module("g.region", flags="p", rows=rows, cols=cols, res=1) + print("Generating reference map using v.random...") + Module("v.random", output=fname, npoints=npoints, overwrite=True) + + +if __name__ == "__main__": + main() diff --git a/vector/v.surf.rst/benchmark/benchmark_v_surf_rst_cv.py b/vector/v.surf.rst/benchmark/benchmark_v_surf_rst_cv.py new file mode 100644 index 00000000000..21a3c7459eb --- /dev/null +++ b/vector/v.surf.rst/benchmark/benchmark_v_surf_rst_cv.py @@ -0,0 +1,51 @@ +"""Benchmarking of cross-valudation in v.surf.rst + +@author Aaron Saw Min Sern + Chung-Yuan Liang +""" + +from grass.pygrass.modules import Module +import grass.benchmark as bm +from subprocess import DEVNULL +import math + + +def main(): + results = [] + # Users can add more or modify existing reference maps + raster_cells = [1e5, 2e5, 4e5, 8e5] # 100k, 200k, 400k, 800k cells + for ncells in raster_cells: + benchmark(math.sqrt(ncells), f"v.surf.rst_cv_{int(ncells / 1e3)}k", results) + bm.nprocs_plot(results) + + +def benchmark(size, label, results): + reference = "v_surf_rst_reference_map" + output = "benchmark_v_surf_rst_nprocs" + + npoints = size * size * 0.1 + generate_map(npoints=npoints, rows=size, cols=size, fname=reference) + + module = Module( + "v.surf.rst", + input=reference, + npmin=100, + cvdev=output, + stdout_=DEVNULL, + overwrite=True, + c=True, + ) + + results.append(bm.benchmark_nprocs(module, label=label, max_nprocs=8, repeat=3)) + Module("g.remove", quiet=True, flags="f", type="raster", name=reference) + Module("g.remove", quiet=True, flags="f", type="raster", name=output) + + +def generate_map(npoints, rows, cols, fname): + Module("g.region", flags="p", rows=rows, cols=cols, res=1) + print("Generating reference map using v.random...") + Module("v.random", output=fname, npoints=npoints, overwrite=True) + + +if __name__ == "__main__": + main() diff --git a/vector/v.surf.rst/main.c b/vector/v.surf.rst/main.c index 4acb2f9c1af..c4581737eed 100644 --- a/vector/v.surf.rst/main.c +++ b/vector/v.surf.rst/main.c @@ -38,7 +38,6 @@ #include #include #include - #include #include #include @@ -412,16 +411,7 @@ int main(int argc, char *argv[]) threads, abs(threads)); threads = abs(threads); } - if (parm.devi->answer && threads > 1) { - G_warning(_( - "Parallel computation disabled when deviation output is required")); - threads = 1; - } - if (parm.cvdev->answer && threads > 1) { - G_warning(_("Parallel computation disabled when cross validation " - "output is required")); - threads = 1; - } + #if defined(_OPENMP) omp_set_num_threads(threads); #else @@ -666,11 +656,27 @@ int main(int argc, char *argv[]) Tmp_fd_z, Tmp_fd_dx, Tmp_fd_dy, Tmp_fd_xx, Tmp_fd_yy, Tmp_fd_xy, create_devi, NULL, cv, parm.wheresql->answer); +#if defined(_OPENMP) + if (cv || create_devi) { + /* use the particular check_point function for cv or dev*/ + /* IL_interp_segments_2d_parallel should use these functions*/ + IL_init_func_2d(¶ms, IL_grid_calc_2d, IL_matrix_create, + IL_check_at_points_2d_cvdev, IL_secpar_loop_2d, IL_crst, + IL_crstg, IL_write_temp_2d); + } + else { + IL_init_func_2d(¶ms, IL_grid_calc_2d, IL_matrix_create, + IL_check_at_points_2d, IL_secpar_loop_2d, IL_crst, + IL_crstg, IL_write_temp_2d); + } +#else IL_init_func_2d(¶ms, IL_grid_calc_2d, IL_matrix_create, IL_check_at_points_2d, IL_secpar_loop_2d, IL_crst, IL_crstg, IL_write_temp_2d); +#endif totsegm = IL_vector_input_data_2d(¶ms, &Map, with_z ? 0 : field, zcol, + scol, info, &xmin, &xmax, &ymin, &ymax, &zmin, &zmax, &NPOINT, &dmax); if (totsegm <= 0) { diff --git a/vector/v.surf.rst/testsuite/test_vsurfrst.py b/vector/v.surf.rst/testsuite/test_vsurfrst.py index 8e70455613e..a1d3c654112 100644 --- a/vector/v.surf.rst/testsuite/test_vsurfrst.py +++ b/vector/v.surf.rst/testsuite/test_vsurfrst.py @@ -13,7 +13,9 @@ class TestVsurfrst(TestCase): tcurvature = "tcurvature" mcurvature = "mcurvature" deviations = "deviations" + deviations_threads = "deviations_threads" cvdev = "cvdev" + cvdev_threads = "cvdev_threads" treeseg = "treeseg" overwin = "overwin" @@ -47,7 +49,9 @@ def tearDownClass(cls): cls.tcurvature, cls.mcurvature, cls.deviations, + cls.deviations_threads, cls.cvdev, + cls.cvdev_threads, cls.treeseg, cls.overwin, ], @@ -63,6 +67,15 @@ def setUp(self): overwrite=True, ) + self.vsurfrst_cv = SimpleModule( + "v.surf.rst", + input="elev_points3d", + npmin=100, + cvdev=self.cvdev, + overwrite=True, + c=True, + ) + def test_more_threads(self): self.assertModule(self.vsurfrst) try: @@ -76,6 +89,35 @@ def test_more_threads(self): # original version of v.surf.rst without parallel processing return + # deviations + try: + self.vsurfrst.inputs["nprocs"].value = 4 + self.vsurfrst.outputs.deviations = self.deviations_threads + self.assertModule(self.vsurfrst) + values = "min=-0.035444\nmax=0.048801\nmean=4.21945e-05" + self.assertVectorFitsUnivar( + map=self.deviations_threads, + column="flt1", + reference=values, + precision=1e-8, + ) + except KeyError: + # original version of v.surf.rst without parallel processing + return + + # cross-validation + try: + self.vsurfrst_cv.inputs["nprocs"].value = 4 + self.vsurfrst_cv.outputs.cvdev = self.cvdev_threads + self.assertModule(self.vsurfrst_cv) + values = "min=-0.678321\nmax=1.5803\nmean=0.00120956" + self.assertVectorFitsUnivar( + map=self.cvdev_threads, column="flt1", reference=values, precision=1e-8 + ) + except KeyError: + # original version of v.surf.rst without parallel processing + return + def test_run_outputs(self): self.vsurfrst.outputs.slope = self.slope self.vsurfrst.outputs.aspect = self.aspect @@ -116,7 +158,9 @@ def test_run_outputs(self): raster=self.pcurvature, reference=values, precision=1e-8 ) # tcurvature - values = "min=-0.0455724261701107\nmax=0.0380486063659191\nmean=-0.000136686790876467" + values = ( + "min=-0.0455724261701107\nmax=0.0380486063659191\nmean=-1.36686790876467e-4" + ) self.assertRasterFitsUnivar( raster=self.tcurvature, reference=values, precision=1e-8 ) @@ -147,6 +191,15 @@ def test_run_outputs(self): self.elevation, self.elevation_attrib, precision=1e-8 ) + # cross-validation + self.vsurfrst_cv.outputs.cvdev = self.cvdev + self.assertModule(self.vsurfrst_cv) + self.assertVectorExists(name=self.cvdev) + values = "min=-0.678321\nmax=1.5803\nmean=0.00120956" + self.assertVectorFitsUnivar( + map=self.cvdev, column="flt1", reference=values, precision=1e-8 + ) + if __name__ == "__main__": test() diff --git a/vector/v.surf.rst/v.surf.rst.html b/vector/v.surf.rst/v.surf.rst.html index 488130f67bd..2c68e18acbc 100644 --- a/vector/v.surf.rst/v.surf.rst.html +++ b/vector/v.surf.rst/v.surf.rst.html @@ -101,7 +101,8 @@

NOTES

function so that the important relationships between these parameters are preserved. The equations for computation of these parameters and their interpretation is described in -Mitasova and Hofierka, 1993 + + Mitasova and Hofierka, 1993 or Neteler and Mitasova, 2004). Slopes and aspect are computed in degrees (0-90 and 1-360 respectively). The aspect raster map has value 0 assigned to flat areas (with slope less @@ -293,6 +294,27 @@

Cross validation procedure

with lost details and fluctuations) or when significant noise is present that needs to be smoothed out. +

Performance

+To enable parallel processing, the user can specify the number of threads +to be used with the nprocs parameter (default 1). +Figures 1 and 2 show benchmark results running on +Intel® Core™ i5-10210U CPU @ 1.60GHz × 8. See benchmark scripts +in the source code for more details. + +
+ benchmark for v.surf.rst +
+ Figure 1: Benchmark shows execution time for different + number of cells (1M, 2M, 4M, and 8M). +
+
+ benchmark for cross-validation of 
+     v.surf.rst +
+ Figure 2: Benchmark shows execution time for running cross-validation on + different number of cells (100k, 200k, 400k, and 800k). +
+

EXAMPLE

Setting for lidar point cloud

@@ -346,7 +368,8 @@

REFERENCES

Mitasova, H., Mitas, L. and Harmon, R.S., 2005, Simultaneous spline approximation and topographic analysis for lidar elevation data in open source GIS, IEEE GRSL 2 (4), 375- 379. -
  • Hofierka, J., 2005, Interpolation of Radioactivity Data Using Regularized Spline with Tension. Applied GIS, Vol. 1, No. 2, pp. 16-01 to 16-13. DOI: 10.2104/ag050016
  • +
  • Hofierka, J., 2005, Interpolation of Radioactivity Data Using Regularized Spline with Tension. + Applied GIS, Vol. 1, No. 2, pp. 16-01 to 16-13. DOI: 10.2104/ag050016
  • Hofierka J., Parajka J., Mitasova H., Mitas L., 2002, Multivariate Interpolation of Precipitation Using Regularized Spline with Tension. @@ -386,7 +409,8 @@

    SEE ALSO

    -Overview: Interpolation and Resampling in GRASS GIS +Overview: + Interpolation and Resampling in GRASS GIS

    For examples of applications see @@ -418,3 +442,7 @@

    AUTHORS

    Parallelization using OpenMP:
    Stanislav Zubal, Czech Technical University in Prague
    Michal Lacko, Pavol Jozef Safarik University in Kosice + +

    +Parallelization of cross-validation using OpenMP: +
    Chung-Yuan Liang, Purdue University, West Lafayette, Indiana, USA diff --git a/vector/v.surf.rst/vsurfrst_benchmark.png b/vector/v.surf.rst/vsurfrst_benchmark.png new file mode 100644 index 0000000000000000000000000000000000000000..7e7ad4477a2e65251451a1a2487c9c8c4302a018 GIT binary patch literal 43502 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 M?3dUn=KAdT2iF^i-~a#s literal 0 HcmV?d00001 diff --git a/vector/v.surf.rst/vsurfrst_cv_benchmark.png b/vector/v.surf.rst/vsurfrst_cv_benchmark.png new file mode 100644 index 0000000000000000000000000000000000000000..7e7c927913d9f1501acd08a8974d8ab4f1546040 GIT binary patch literal 57290 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 MHqd&p-#YMr0DTIj9RL6T literal 0 HcmV?d00001 From f49bdf48bdcbe9e64c87c573066139f8348c1e35 Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Mon, 24 Jun 2024 18:26:43 -0400 Subject: [PATCH 012/293] d.legend.vect: Fix dead store warnings in draw.c (#3922) --- display/d.legend.vect/draw.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/display/d.legend.vect/draw.c b/display/d.legend.vect/draw.c index 516a23a45a7..6f23c0f4924 100644 --- a/display/d.legend.vect/draw.c +++ b/display/d.legend.vect/draw.c @@ -86,7 +86,6 @@ void draw(char *file_name, double LL, double LT, char *title, int cols, if (strstr(buf, sub_delim) == NULL) { /* Get the maximum symbol size */ tokens = G_tokenize(buf, sep); - symb_name = G_store(tokens[1]); size = atof(tokens[2]); type_str = G_store(tokens[7]); G_free_tokens(tokens); @@ -137,7 +136,6 @@ void draw(char *file_name, double LL, double LT, char *title, int cols, } if (strstr(buf, sub_delim) != NULL) { /* Group subtitle */ - label = G_malloc(GNAME_MAX); part = strtok(buf, sep); label = G_store(part); From 5d25c53517ab6eb339ad07177cfc8ec9d79f0df8 Mon Sep 17 00:00:00 2001 From: Markus Neteler Date: Tue, 25 Jun 2024 01:45:00 +0200 Subject: [PATCH 013/293] wxGUI: update menu creation explanations (#3920) --- gui/wxpython/Makefile | 2 +- gui/wxpython/README | 79 ------------------------------------------ gui/wxpython/README.md | 69 ++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 80 deletions(-) delete mode 100644 gui/wxpython/README create mode 100644 gui/wxpython/README.md diff --git a/gui/wxpython/Makefile b/gui/wxpython/Makefile index b56e64ebaa5..b1785f75dc0 100644 --- a/gui/wxpython/Makefile +++ b/gui/wxpython/Makefile @@ -14,7 +14,7 @@ SRCFILES := $(wildcard icons/*.py xml/*) \ mapswipe/*.py modules/*.py nviz/*.py psmap/*.py rdigit/*.py \ rlisetup/*.py startup/*.py timeline/*.py vdigit/*.py \ vnet/*.py web_services/*.py wxplot/*.py iscatt/*.py tplot/*.py photo2image/*.py image2target/*.py) \ - wxgui.py README + wxgui.py README.md DSTFILES := $(patsubst %,$(DSTDIR)/%,$(SRCFILES)) \ $(patsubst %.py,$(DSTDIR)/%.pyc,$(filter %.py,$(SRCFILES))) diff --git a/gui/wxpython/README b/gui/wxpython/README deleted file mode 100644 index c88d3f63d2f..00000000000 --- a/gui/wxpython/README +++ /dev/null @@ -1,79 +0,0 @@ -GRASS GIS - wxPython Graphical User Interface (wxGUI) -===================================================== - -$Date$ - -1 - REQUIREMENTS - - GRASS GIS >= 6.4 - Python >= 2.4 - Python ElementTree (only for Python 2.4) - wxPython >= 2.8.10.1 - NumPy >= 1.0.4 - PIL >= 1.1.7 - - -2 - STARTUP WITH GRASS INITIALIZATION - -If you want to launch wxPython GUI automatically, start GRASS with -`--gui` parameter - -$ grass --gui - - -3 - STARTUP FROM GRASS TERMINAL - -Simply run - -$ g.gui wxpython - -from the GRASS terminal. - -You can also specify workspace file to load on startup. - -$ g.gui gui=wxpython workspace=myworkspace.gxw - - -4 - DEBUGGING - -To enable GUI debug messages on given level set WX_DEBUG environment -variable, e.g. - -$ g.gisenv set="WX_DEBUG=3" - - -5 - CLI BASED DISPLAY USAGE - -Use command d.mon (shell script in gui/scripts directory) to start map -display: - -GRASS> d.mon wx[0-6] - -After a while, new window should appear. If this is your case, add some -raster layer to the map display: - -GRASS> d.rast aspect - -And try the vector layer too - -GRASS> d.vect roads - -You should be able to zoom && pan through the map, once the layers are -displayed. You should be also able to store the display content as well as -clear the display and start from scratch. - - -6 TRANSLATION HANDLING - -Notes: -- Help part of menu entries is coming from the module descriptions -- The menu is maintained manually in xml/menudata.xml - -Update of module description strings in xml/menudata.xml: -- in a GRASS session, run tools/update_menudata.py - -From this (updated) xml/menudata.xml, the gettext strings are generated -via Makefile and stored into the file "menustrings.py". - -When generating the po files in locale/po/ but locale/Makefile, all -.py files are parsed and the strings are stored in locale/po/grasswxpy_XX.po diff --git a/gui/wxpython/README.md b/gui/wxpython/README.md new file mode 100644 index 00000000000..cf73dad8c66 --- /dev/null +++ b/gui/wxpython/README.md @@ -0,0 +1,69 @@ +# GRASS GIS - wxPython Graphical User Interface (wxGUI) + +## 1 - REQUIREMENTS + +See `[../../REQUIREMENTS.md](../../REQUIREMENTS.md)` + +## 2 - STARTUP WITH GRASS INITIALIZATION + +If you want to launch wxPython GUI automatically, start GRASS with +`--gui` parameter + +`$ grass --gui` + +## 3 - STARTUP FROM GRASS TERMINAL + +Simply run + +`$ g.gui wxpython` + +from the GRASS terminal. + +You can also specify workspace file to load on startup. + +`$ g.gui gui=wxpython workspace=myworkspace.gxw` + +## 4 - DEBUGGING + +To enable GUI debug messages on given level set `WX_DEBUG` environment +variable, e.g. + +`$ g.gisenv set="WX_DEBUG=3"` + +## 5 - CLI BASED DISPLAY USAGE + +Use command `d.mon` (shell script in `gui/scripts/` directory) to start map +display: + +`GRASS> d.mon wx[0-6]` + +After a while, new window should appear. If this is your case, you can now +add some layers, e.g. a raster layer to the map display: + +`GRASS> d.rast aspect` + +And try the vector layer, too + +`GRASS> d.vect roads` + +You should be able to zoom & pan through the map, once the layers are +displayed. You should be also able to store the display content as well as +clear the display and start from scratch. + +## 6 TRANSLATION HANDLING + +Notes: + +- Help part of menu entries is coming from the module descriptions +- The menu is generated by `./tools/build_modules_xml.py` + +Update of module description strings in `./xml/menudata.xml`: + +- in a GRASS session, run `./tools/update_menudata.py` + +From this (updated) `./xml/menudata.xml`, the gettext strings are generated +via Makefile and stored into the file `menustrings.py`. + +While generating the po files in `../../locale/po/` by `../../locale/Makefile`, +all `.py` files are parsed and the strings are stored in +`../../locale/po/grasswxpy_XX.po`. From 3ef6e61d997a016a1a138ef6e9c727a7774db9b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Tue, 25 Jun 2024 04:24:40 -0400 Subject: [PATCH 014/293] CI(super-linter): Specify linter rules path to pick up configuration files (#3915) --- .github/workflows/pytest.yml | 8 ++++++-- .github/workflows/super-linter.yml | 6 ++++++ .travis.yml | 4 ++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 56c519afc7d..b307227c920 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -77,13 +77,17 @@ jobs: run: | export PYTHONPATH=`grass --config python_path`:$PYTHONPATH export LD_LIBRARY_PATH=$(grass --config path)/lib:$LD_LIBRARY_PATH - pytest --verbose --color=yes --durations=0 --durations-min=0.5 --numprocesses auto -ra . -m 'not needs_solo_run' + pytest --verbose --color=yes --durations=0 --durations-min=0.5 \ + --numprocesses auto -ra . \ + -m 'not needs_solo_run' - name: Run pytest with a single worker (for tests marked with needs_solo_run) run: | export PYTHONPATH=`grass --config python_path`:$PYTHONPATH export LD_LIBRARY_PATH=$(grass --config path)/lib:$LD_LIBRARY_PATH - pytest --verbose --color=yes --durations=0 --durations-min=0.5 -ra . -m 'needs_solo_run' + pytest --verbose --color=yes --durations=0 --durations-min=0.5 \ + -ra . \ + -m 'needs_solo_run' - name: Print installed versions if: always() diff --git a/.github/workflows/super-linter.yml b/.github/workflows/super-linter.yml index 92cda00f45a..460b5e02fe3 100644 --- a/.github/workflows/super-linter.yml +++ b/.github/workflows/super-linter.yml @@ -36,6 +36,10 @@ jobs: DEFAULT_BRANCH: main # To report GitHub Actions status checks GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # By default, super-linter expect all linters to have their config + # files inside .github/linters. + # Setting it to the root of the repo for easier configuration here. + LINTER_RULES_PATH: . # Listed but commented out linters would be nice to have. # (see https://github.com/github/super-linter#environment-variables) # @@ -52,3 +56,5 @@ jobs: VALIDATE_POWERSHELL: true # VALIDATE_XML: true VALIDATE_YAML: true + MARKDOWN_CONFIG_FILE: .markdownlint.yml + YAML_CONFIG_FILE: .yamllint diff --git a/.travis.yml b/.travis.yml index 5ae946cdb3d..50dff8d2fa7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,8 +10,8 @@ cache: ccache # safelist branches: only: - - main - - releasebranch* + - main + - releasebranch* jobs: include: From 541276ac7e4a568d92692951a5c3076475f22f2a Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Tue, 25 Jun 2024 04:36:36 -0400 Subject: [PATCH 015/293] d.linegraph: Fix dead store warning in main.c (#3918) Fix dead store warning in d.linegraph module Co-authored-by: Shubham Vasudeo Desai --- display/d.linegraph/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/display/d.linegraph/main.c b/display/d.linegraph/main.c index 9399b1d16c0..16108a8781c 100644 --- a/display/d.linegraph/main.c +++ b/display/d.linegraph/main.c @@ -633,7 +633,7 @@ int main(int argc, char **argv) in[i].value = 0.0; in[i].num_pnts = 0; - while ((err = fscanf(in[i].fp, "%f", &in[i].value)) != EOF) { + while (fscanf(in[i].fp, "%f", &in[i].value) != EOF) { if (scale_y_values) in[i].value *= y_scale; in[i].num_pnts++; From df371ecc93efb0d342d54e013b68a19bd9e2692e Mon Sep 17 00:00:00 2001 From: Ivan Mincik Date: Tue, 25 Jun 2024 09:59:19 +0000 Subject: [PATCH 016/293] CI: add nix to the list of allowed commit prefixes (#3925) --- utils/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/release.yml b/utils/release.yml index 0535fdc17a1..eb601419274 100644 --- a/utils/release.yml +++ b/utils/release.yml @@ -34,7 +34,7 @@ notes: example: 'win:' - title: Packaging, Configuration, Portability, and Compilation - regexp: '(packaging|pkg|rpm|deb|pkg-config|configure|config|[Mm]ake|build): ' + regexp: '(packaging|pkg|rpm|deb|nix|pkg-config|configure|config|[Mm]ake|build): ' example: 'build:' - title: Docker From 9599acab14702d2b7907e229f20610d9e9f9a0dd Mon Sep 17 00:00:00 2001 From: Ivan Mincik Date: Tue, 25 Jun 2024 16:06:17 +0000 Subject: [PATCH 017/293] packaging: improve nix development environment (#3924) nix: improve nix development environment * inherit build dependencies from grass package * add `dev-help` function --- flake.nix | 74 +++++++++++++++++++------------------------------------ 1 file changed, 25 insertions(+), 49 deletions(-) diff --git a/flake.nix b/flake.nix index d3d2b47e995..5283a4a0ca9 100644 --- a/flake.nix +++ b/flake.nix @@ -19,58 +19,34 @@ packages.grass = pkgs.callPackage ./package.nix { }; devShells.default = pkgs.mkShell { - buildInputs = - # from package nativeBuildInputs - with pkgs; [ - bison - flex - gdal # for `gdal-config` - geos # for `geos-config` - netcdf # for `nc-config` - pkg-config - ] ++ (with python3Packages; [ pytest python-dateutil numpy wxPython_4_2 ]) + inputsFrom = [ self'.packages.grass ]; - # from package buildInputs - ++ [ - blas - cairo - ffmpeg - fftw - freetype - gdal - geos - lapack - libGLU - libpng - libsvm - libtiff - libxml2 - netcdf - pdal - postgresql - proj - readline - sqlite - wxGTK32 - zlib - zstd - ] ++ lib.optionals stdenv.isDarwin [ libiconv ]; + # additional packages + buildInputs = with pkgs.python3Packages; [ + pytest + ]; shellHook = '' - echo -e "\nWelcome to a GRASS development environment !" - echo "Build GRASS using following commands:" - echo - echo " 1. ./configure --prefix=\$(pwd)/app" - echo " 2. make -j$(nproc)" - echo " 3. make install" - echo - echo "Run tests:" - echo - echo " 1. export PYTHONPATH=\$(app/bin/grass --config python_path):\$PYTHONPATH" - echo " 2. export LD_LIBRARY_PATH=\$(app/bin/grass --config path)/lib:\$LD_LIBRARY_PATH" - echo " 3. pytest" - echo - echo "Note: run 'nix flake update' from time to time to update dependencies." + function dev-help { + echo -e "\nWelcome to a GRASS development environment !" + echo "Build GRASS using following commands:" + echo + echo " 1. ./configure --prefix=\$(pwd)/app" + echo " 2. make -j$(nproc)" + echo " 3. make install" + echo + echo "Run tests:" + echo + echo " 1. export PYTHONPATH=\$(app/bin/grass --config python_path):\$PYTHONPATH" + echo " 2. export LD_LIBRARY_PATH=\$(app/bin/grass --config path)/lib:\$LD_LIBRARY_PATH" + echo " 3. pytest" + echo + echo "Note: run 'nix flake update' from time to time to update dependencies." + echo + echo "Run 'dev-help' to see this message again." + } + + dev-help ''; }; }; From 4b5b6cf9a7fa77da6e1de5e3e99a1eddf19acde0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Tue, 25 Jun 2024 16:29:26 -0400 Subject: [PATCH 018/293] CI(pre-commit): Enable markdownlint fixes (#3916) * CI(pre-commit): Enable markdownlint fixes * CI: Add fix: true in .markdownlint.yml for tools using that config key * Update .markdownlint.yml * Update v.surf.rst.html to remove trailing whitespaces --- .markdownlint.yml | 3 +++ .pre-commit-config.yaml | 2 +- vector/v.surf.rst/v.surf.rst.html | 14 +++++++------- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.markdownlint.yml b/.markdownlint.yml index 56c5c555930..e14a39a2289 100644 --- a/.markdownlint.yml +++ b/.markdownlint.yml @@ -1,3 +1,6 @@ --- default: true + +# Fix any fixable errors (depending on the markdownlint wrapper tool used) +fix: true diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ebfdaf8a39c..437295ab1e3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -38,7 +38,7 @@ repos: - repo: https://github.com/igorshubovych/markdownlint-cli rev: v0.41.0 hooks: - - id: markdownlint + - id: markdownlint-fix # Using this mirror lets us use mypyc-compiled black, which is about 2x faster - repo: https://github.com/psf/black-pre-commit-mirror rev: 24.4.2 diff --git a/vector/v.surf.rst/v.surf.rst.html b/vector/v.surf.rst/v.surf.rst.html index 2c68e18acbc..66a39ebc6f8 100644 --- a/vector/v.surf.rst/v.surf.rst.html +++ b/vector/v.surf.rst/v.surf.rst.html @@ -295,9 +295,9 @@

    Cross validation procedure

    present that needs to be smoothed out.

    Performance

    -To enable parallel processing, the user can specify the number of threads -to be used with the nprocs parameter (default 1). -Figures 1 and 2 show benchmark results running on +To enable parallel processing, the user can specify the number of threads +to be used with the nprocs parameter (default 1). +Figures 1 and 2 show benchmark results running on Intel® Core™ i5-10210U CPU @ 1.60GHz × 8. See benchmark scripts in the source code for more details. @@ -308,10 +308,10 @@

    Performance

    number of cells (1M, 2M, 4M, and 8M).
    - benchmark for cross-validation of 
+    <img src=
    - Figure 2: Benchmark shows execution time for running cross-validation on + Figure 2: Benchmark shows execution time for running cross-validation on different number of cells (100k, 200k, 400k, and 800k).
    @@ -368,8 +368,8 @@

    REFERENCES

    Mitasova, H., Mitas, L. and Harmon, R.S., 2005, Simultaneous spline approximation and topographic analysis for lidar elevation data in open source GIS, IEEE GRSL 2 (4), 375- 379.
  • -
  • Hofierka, J., 2005, Interpolation of Radioactivity Data Using Regularized Spline with Tension. - Applied GIS, Vol. 1, No. 2, pp. 16-01 to 16-13. DOI: 10.2104/ag050016
  • +
  • Hofierka, J., 2005, Interpolation of Radioactivity Data Using Regularized Spline with Tension. + Applied GIS, Vol. 1, No. 2, pp. 16-01 to 16-13. DOI: 10.2104/ag050016
  • Hofierka J., Parajka J., Mitasova H., Mitas L., 2002, Multivariate Interpolation of Precipitation Using Regularized Spline with Tension. From 4a35e80a1856cfc3b185c0c333a4508adaf34437 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 25 Jun 2024 19:33:47 -0400 Subject: [PATCH 019/293] CI(deps): Update clang-format to v17 (#3860) --- .github/workflows/clang-format-check.yml | 2 +- .pre-commit-config.yaml | 2 +- general/g.region/printwindow.c | 4 ++-- imagery/i.eb.netrad/r_net.c | 2 +- imagery/i.fft/main.c | 2 +- imagery/i.ifft/main.c | 2 +- imagery/i.maxlik/invert.c | 30 ++++++++++++------------ imagery/i.segment/flag.h | 6 ++--- include/grass/gprojects.h | 2 +- lib/bitmap/bitmap.c | 2 +- lib/bitmap/sparse.c | 2 +- lib/external/parson/parson.c | 10 ++++---- lib/gis/lrand48.c | 2 +- lib/gis/lz4.c | 2 +- lib/gis/pi.h | 4 ++-- lib/gis/sleep.c | 2 +- lib/gmath/solvers_direct.c | 3 ++- lib/gpde/test/test_arrays.c | 15 ++++++++---- lib/ogsf/gs2.c | 2 +- lib/ogsf/gsget.h | 8 +++---- lib/ogsf/rgbpack.h | 4 ++-- lib/ogsf/rowcol.h | 10 ++++---- lib/raster/get_row.c | 2 +- lib/rst/interp_float/segmen2d_parallel.c | 10 ++++---- lib/vector/Vlib/intersect2.c | 2 +- lib/vector/dglib/graph_v1.h | 2 +- lib/vector/diglib/prune.c | 2 +- misc/m.cogo/main.c | 4 ++-- raster/r.buffer/distance.h | 2 +- raster/r.cost/flag.h | 6 ++--- raster/r.cost/heap.c | 2 +- raster/r.mfilter/execute.c | 2 +- raster/r.proj/main.c | 2 +- raster/r.random.cells/flag.h | 6 ++--- raster/r.random.cells/ransurf.h | 2 +- raster/r.random.surface/ransurf.h | 2 +- raster/r.ros/main.c | 2 +- raster/r.ros/spot_dist.c | 2 +- raster/r.sim/simlib/hydro.c | 2 +- raster/r.slope.aspect/main.c | 15 ++++++------ raster/r.spread/collect_ori.c | 2 +- raster/r.spread/main.c | 2 +- raster/r.spread/ram2out.c | 2 +- raster/r.spread/select_linksB.c | 2 +- raster/r.spread/spot.c | 2 +- raster/r.spread/spread.c | 2 +- raster/r.stream.extract/local_proto.h | 2 +- raster/r.sun/main.c | 26 ++++++++++---------- raster/r.surf.contour/flag.h | 6 ++--- raster/r.surf.idw/pi.h | 4 ++-- raster/r.to.vect/areas.c | 2 +- raster/r.transect/parse_line.c | 4 ++-- raster/r.walk/flag.h | 6 ++--- raster/r.walk/heap.c | 2 +- raster/r.water.outlet/ramseg.h | 2 +- raster/r.watershed/ram/do_astar.h | 2 +- raster/r.watershed/ram/flag.h | 6 ++--- raster/r.watershed/ram/ramseg.h | 3 ++- vector/v.cluster/main.c | 2 +- vector/v.label.sa/labels.c | 2 +- 60 files changed, 135 insertions(+), 131 deletions(-) diff --git a/.github/workflows/clang-format-check.yml b/.github/workflows/clang-format-check.yml index da54d1cd178..8eeadca924d 100644 --- a/.github/workflows/clang-format-check.yml +++ b/.github/workflows/clang-format-check.yml @@ -22,7 +22,7 @@ jobs: - uses: DoozyX/clang-format-lint-action@11b773b1598aa4ae3b32f023701bca5201c3817d # v0.17 with: source: "." - clangFormatVersion: 15 + clangFormatVersion: 17 inplace: True - name: Create and uploads code suggestions to apply id: diff diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 437295ab1e3..d596da4f85c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -58,7 +58,7 @@ repos: .*/testsuite/.* ) - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v15.0.7 + rev: v17.0.6 hooks: - id: clang-format types_or: [c, c++, javascript, json, objective-c] diff --git a/general/g.region/printwindow.c b/general/g.region/printwindow.c index d35ed167926..9d8d91cb447 100644 --- a/general/g.region/printwindow.c +++ b/general/g.region/printwindow.c @@ -5,8 +5,8 @@ #include #include "local_proto.h" -#define DEG2RAD(a) ((a)*M_PI / 180.0) -#define RAD2DEG(a) ((a)*180.0 / M_PI) +#define DEG2RAD(a) ((a) * M_PI / 180.0) +#define RAD2DEG(a) ((a) * 180.0 / M_PI) static double get_shift(double east) { diff --git a/imagery/i.eb.netrad/r_net.c b/imagery/i.eb.netrad/r_net.c index 80dd33f857a..a2519858afa 100644 --- a/imagery/i.eb.netrad/r_net.c +++ b/imagery/i.eb.netrad/r_net.c @@ -35,7 +35,7 @@ double r_net(double bbalb, double ndvi UNUSED, double tempk, double dtair, Kin = 1358.0 * (cos(sunzangle * PI / 180) * tsw / (ds * ds)); /* Lin is incoming longwave radiation */ - Lin = (e_atm)*5.67 * pow(10, -8) * pow((tempk - dtair), 4); + Lin = (e_atm) * 5.67 * pow(10, -8) * pow((tempk - dtair), 4); /* Lout is surface grey body emission in Longwave spectrum */ Lout = e0 * 5.67 * pow(10, -8) * pow(tempk, 4); diff --git a/imagery/i.fft/main.c b/imagery/i.fft/main.c index cae36d87036..ad9a87a7890 100644 --- a/imagery/i.fft/main.c +++ b/imagery/i.fft/main.c @@ -125,7 +125,7 @@ int main(int argc, char *argv[]) cell_real = Rast_allocate_d_buf(); cell_imag = Rast_allocate_d_buf(); -#define C(i, j) ((i)*cols + (j)) +#define C(i, j) ((i) * cols + (j)) /* Read in cell map values */ G_message(_("Reading the raster map <%s>..."), Cellmap_orig); diff --git a/imagery/i.ifft/main.c b/imagery/i.ifft/main.c index 8d07af9b695..6012e69ec2d 100644 --- a/imagery/i.ifft/main.c +++ b/imagery/i.ifft/main.c @@ -136,7 +136,7 @@ int main(int argc, char *argv[]) cell_real = Rast_allocate_d_buf(); cell_imag = Rast_allocate_d_buf(); -#define C(i, j) ((i)*cols + (j)) +#define C(i, j) ((i) * cols + (j)) /* Read in cell map values */ G_message(_("Reading raster maps...")); diff --git a/imagery/i.maxlik/invert.c b/imagery/i.maxlik/invert.c index 5d550463810..f5545c077a6 100644 --- a/imagery/i.maxlik/invert.c +++ b/imagery/i.maxlik/invert.c @@ -124,35 +124,35 @@ int invert(struct One_Sig *s, int nbands, int *ik, int *jk, double *det) /* zero means non-invertible */ if (*det == 0.0) - return 0; + return 0; /* * if negative, then matrix is not positive-definite * (But this probably not a sufficient test) */ if (*det < 0.0) - return -1; + return -1; /* restore ordering of matrix */ for (k = nbands - 1; k >= 0; k--) { /* 530 */ - j = ik[k]; + j = ik[k]; - if (j > k) - for (i = 0; i < nbands; i++) { /* 510 */ - v = s->var[i][k]; - s->var[i][k] = -(s->var[i][j]); - s->var[i][j] = v; + if (j > k) + for (i = 0; i < nbands; i++) { /* 510 */ + v = s->var[i][k]; + s->var[i][k] = -(s->var[i][j]); + s->var[i][j] = v; /*510 */ } - i = jk[k]; + i = jk[k]; - if (i > k) - for (j = 0; j < nbands; j++) { /* 520 */ - v = s->var[k][j]; - s->var[k][j] = -(s->var[i][j]); - s->var[i][j] = v; + if (i > k) + for (j = 0; j < nbands; j++) { /* 520 */ + v = s->var[k][j]; + s->var[k][j] = -(s->var[i][j]); + s->var[i][j] = v; /*520 */ } - /*530 */ + /*530 */ } return 1; diff --git a/imagery/i.segment/flag.h b/imagery/i.segment/flag.h index 9ba05270928..1a3c4c36585 100644 --- a/imagery/i.segment/flag.h +++ b/imagery/i.segment/flag.h @@ -47,13 +47,13 @@ FLAG }; #define FLAG_UNSET(flags, row, col) \ - (flags)->array[(row)][(col) >> 3] &= ~(1 << ((col)&7)) + (flags)->array[(row)][(col) >> 3] &= ~(1 << ((col) & 7)) #define FLAG_SET(flags, row, col) \ - (flags)->array[(row)][(col) >> 3] |= (1 << ((col)&7)) + (flags)->array[(row)][(col) >> 3] |= (1 << ((col) & 7)) #define FLAG_GET(flags, row, col) \ - (flags)->array[(row)][(col) >> 3] & (1 << ((col)&7)) + (flags)->array[(row)][(col) >> 3] & (1 << ((col) & 7)) /* flag.c */ FLAG *flag_create(int, int); diff --git a/include/grass/gprojects.h b/include/grass/gprojects.h index c51d3ad14f0..7872d6cb4b6 100644 --- a/include/grass/gprojects.h +++ b/include/grass/gprojects.h @@ -26,7 +26,7 @@ /* adapted from gdal_version.h */ #ifndef PROJ_COMPUTE_VERSION #define PROJ_COMPUTE_VERSION(maj, min, rev) \ - ((maj)*1000000 + (min)*10000 + (rev)*100) + ((maj) * 1000000 + (min) * 10000 + (rev) * 100) #endif /* just in case PROJ introduces PROJ_VERSION_NUM in a future version */ diff --git a/lib/bitmap/bitmap.c b/lib/bitmap/bitmap.c index bb64d9b13ec..5f33c2c374f 100644 --- a/lib/bitmap/bitmap.c +++ b/lib/bitmap/bitmap.c @@ -38,7 +38,7 @@ #include #define BM_col_to_byte(x) ((x) >> 3) /* x / 8 */ -#define BM_col_to_bit(x) ((x)&7) /* x % 8 */ +#define BM_col_to_bit(x) ((x) & 7) /* x % 8 */ static int Mode = BM_FLAT; static int Size = 1; diff --git a/lib/bitmap/sparse.c b/lib/bitmap/sparse.c index 18cd20a2657..81528200db2 100644 --- a/lib/bitmap/sparse.c +++ b/lib/bitmap/sparse.c @@ -23,7 +23,7 @@ #include #define BM_col_to_byte(x) ((x) >> 3) /* x / 8 */ -#define BM_col_to_bit(x) ((x)&7) /* x % 8 */ +#define BM_col_to_bit(x) ((x) & 7) /* x % 8 */ static int depth; diff --git a/lib/external/parson/parson.c b/lib/external/parson/parson.c index 21490938bb7..b9975c2f5ea 100644 --- a/lib/external/parson/parson.c +++ b/lib/external/parson/parson.c @@ -93,7 +93,7 @@ #if defined(isnan) && defined(isinf) #define IS_NUMBER_INVALID(x) (isnan((x)) || isinf((x))) #else -#define IS_NUMBER_INVALID(x) (((x)*0.0) != 0.0) +#define IS_NUMBER_INVALID(x) (((x) * 0.0) != 0.0) #endif #define OBJECT_INVALID_IX ((size_t)-1) @@ -109,7 +109,7 @@ static JSON_Number_Serialization_Function parson_number_serialization_function = NULL; #define IS_CONT(b) \ - (((unsigned char)(b)&0xC0) == 0x80) /* is utf-8 continuation byte */ + (((unsigned char)(b) & 0xC0) == 0x80) /* is utf-8 continuation byte */ typedef int parson_bool_t; @@ -900,13 +900,13 @@ static JSON_Status parse_utf16(const char **unprocessed, char **processed) } else if (cp < 0x800) { processed_ptr[0] = ((cp >> 6) & 0x1F) | 0xC0; /* 110xxxxx */ - processed_ptr[1] = ((cp)&0x3F) | 0x80; /* 10xxxxxx */ + processed_ptr[1] = ((cp) & 0x3F) | 0x80; /* 10xxxxxx */ processed_ptr += 1; } else if (cp < 0xD800 || cp > 0xDFFF) { processed_ptr[0] = ((cp >> 12) & 0x0F) | 0xE0; /* 1110xxxx */ processed_ptr[1] = ((cp >> 6) & 0x3F) | 0x80; /* 10xxxxxx */ - processed_ptr[2] = ((cp)&0x3F) | 0x80; /* 10xxxxxx */ + processed_ptr[2] = ((cp) & 0x3F) | 0x80; /* 10xxxxxx */ processed_ptr += 2; } else if (cp >= 0xD800 && @@ -927,7 +927,7 @@ static JSON_Status parse_utf16(const char **unprocessed, char **processed) processed_ptr[0] = (((cp >> 18) & 0x07) | 0xF0); /* 11110xxx */ processed_ptr[1] = (((cp >> 12) & 0x3F) | 0x80); /* 10xxxxxx */ processed_ptr[2] = (((cp >> 6) & 0x3F) | 0x80); /* 10xxxxxx */ - processed_ptr[3] = (((cp)&0x3F) | 0x80); /* 10xxxxxx */ + processed_ptr[3] = (((cp) & 0x3F) | 0x80); /* 10xxxxxx */ processed_ptr += 3; } else { /* trail surrogate before lead surrogate */ diff --git a/lib/gis/lrand48.c b/lib/gis/lrand48.c index fdf862d4d66..eea69a77fe0 100644 --- a/lib/gis/lrand48.c +++ b/lib/gis/lrand48.c @@ -41,7 +41,7 @@ static const uint32 b0 = 0xB; static int seeded; -#define LO(x) ((x)&0xFFFFU) +#define LO(x) ((x) & 0xFFFFU) #define HI(x) ((x) >> 16) /*! diff --git a/lib/gis/lz4.c b/lib/gis/lz4.c index 29aeb9e9613..1575be2c481 100644 --- a/lib/gis/lz4.c +++ b/lib/gis/lz4.c @@ -204,7 +204,7 @@ typedef size_t uptrval; /* generally true, except OpenVMS-64 */ #if defined(__x86_64__) typedef U64 reg_t; /* 64-bits in x32 mode */ #else -typedef size_t reg_t; /* 32-bits in x32 mode */ +typedef size_t reg_t; /* 32-bits in x32 mode */ #endif /*-************************************ diff --git a/lib/gis/pi.h b/lib/gis/pi.h index e808bd3c867..5ea273a28e5 100644 --- a/lib/gis/pi.h +++ b/lib/gis/pi.h @@ -3,7 +3,7 @@ #include -#define Radians(x) ((x)*M_PI / 180.0) -#define Degrees(x) ((x)*180.0 / M_PI) +#define Radians(x) ((x) * M_PI / 180.0) +#define Degrees(x) ((x) * 180.0 / M_PI) #endif /* __PI_H__ */ diff --git a/lib/gis/sleep.c b/lib/gis/sleep.c index 8fed1f59ac5..1a5e2a07ae0 100644 --- a/lib/gis/sleep.c +++ b/lib/gis/sleep.c @@ -12,7 +12,7 @@ void G_sleep(unsigned int seconds) { #ifdef __MINGW32__ /* note: Sleep() cannot be interrupted */ - Sleep((seconds)*1000); + Sleep((seconds) * 1000); #else sleep(seconds); #endif diff --git a/lib/gmath/solvers_direct.c b/lib/gmath/solvers_direct.c index 6317f5c510d..549d4134f87 100644 --- a/lib/gmath/solvers_direct.c +++ b/lib/gmath/solvers_direct.c @@ -231,7 +231,8 @@ int G_math_cholesky_decomposition(double **A, int rows, int bandwidth) colsize = bandwidth; for (k = 0; k < rows; k++) { -#pragma omp parallel for schedule (static) private(i, j, sum_2) shared(A, k) reduction(+:sum_1) +#pragma omp parallel for schedule(static) private(i, j, sum_2) shared(A, k) \ + reduction(+ : sum_1) for (j = 0; j < k; j++) { sum_1 += A[k][j] * A[k][j]; } diff --git a/lib/gpde/test/test_arrays.c b/lib/gpde/test/test_arrays.c index 9aec6f240d2..8f63bd58621 100644 --- a/lib/gpde/test/test_arrays.c +++ b/lib/gpde/test/test_arrays.c @@ -70,7 +70,8 @@ int fill_array_2d(N_array_2d *a) cols = a->cols; type = N_get_array_2d_type(a); -#pragma omp parallel for private (i, j) shared (cols, rows, type, a) reduction(+:res) +#pragma omp parallel for private(i, j) shared(cols, rows, type, a) \ + reduction(+ : res) for (j = 0; j < rows; j++) { for (i = 0; i < cols; i++) { if (type == CELL_TYPE) { @@ -129,7 +130,8 @@ int compare_array_2d(N_array_2d *a, N_array_2d *b) rows = a->rows; type = N_get_array_2d_type(a); -#pragma omp parallel for private (i, j) shared (cols, rows, type, a, b) reduction(+:res) +#pragma omp parallel for private(i, j) shared(cols, rows, type, a, b) \ + reduction(+ : res) for (j = 0; j < rows; j++) { for (i = 0; i < cols; i++) { if (type == CELL_TYPE) { @@ -166,7 +168,8 @@ int fill_array_3d(N_array_3d *a) depths = a->depths; type = N_get_array_3d_type(a); -#pragma omp parallel for private (i, j, k) shared (depths, rows, cols, type, a) reduction(+:res) +#pragma omp parallel for private(i, j, k) shared(depths, rows, cols, type, a) \ + reduction(+ : res) for (k = 0; k < depths; k++) { for (j = 0; j < rows; j++) { for (i = 0; i < cols; i++) { @@ -204,7 +207,8 @@ int fill_array_3d_null(N_array_3d *a) depths = a->depths; type = N_get_array_3d_type(a); -#pragma omp parallel for private (i, j, k) shared (cols, rows, depths, type, a) reduction(+:res) +#pragma omp parallel for private(i, j, k) shared(cols, rows, depths, type, a) \ + reduction(+ : res) for (k = 0; k < depths; k++) { for (j = 0; j < rows; j++) { for (i = 0; i < cols; i++) { @@ -231,7 +235,8 @@ int compare_array_3d(N_array_3d *a, N_array_3d *b) depths = a->depths; type = N_get_array_3d_type(a); -#pragma omp parallel for private (i, j, k) shared (depths, rows, cols, type, a, b) reduction(+:res) +#pragma omp parallel for private(i, j, k) \ + shared(depths, rows, cols, type, a, b) reduction(+ : res) for (k = 0; k < depths; k++) { for (i = 0; i < rows; i++) { for (j = 0; j < cols; j++) { diff --git a/lib/ogsf/gs2.c b/lib/ogsf/gs2.c index d1175963d08..f971c3f58c2 100644 --- a/lib/ogsf/gs2.c +++ b/lib/ogsf/gs2.c @@ -3428,7 +3428,7 @@ void GS_clear(int col) */ glClearDepth(1.0); glClearColor( - ((float)((col)&0xff)) / 255., (float)((col) >> 8 & 0xff) / 255., + ((float)((col) & 0xff)) / 255., (float)((col) >> 8 & 0xff) / 255., (float)((col) >> 16 & 0xff) / 255., (float)((col) >> 24 & 0xff) / 255.); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); diff --git a/lib/ogsf/gsget.h b/lib/ogsf/gsget.h index 597fad905b4..4117c50dfd7 100644 --- a/lib/ogsf/gsget.h +++ b/lib/ogsf/gsget.h @@ -48,10 +48,10 @@ nv[Z] = (int)((i) & NZMASK) * GS_global_exag()/(float)ZMAXPOS */ -#define FNORM(i, nv) \ - nv[X] = ((int)(((i)&NXMASK) >> 21) - XYMAXPOS) / (float)XYMAXPOS; \ - nv[Y] = ((int)(((i)&NYMASK) >> 10) - XYMAXPOS) / (float)XYMAXPOS; \ - nv[Z] = (int)((i)&NZMASK) / (float)ZMAXPOS +#define FNORM(i, nv) \ + nv[X] = ((int)(((i) & NXMASK) >> 21) - XYMAXPOS) / (float)XYMAXPOS; \ + nv[Y] = ((int)(((i) & NYMASK) >> 10) - XYMAXPOS) / (float)XYMAXPOS; \ + nv[Z] = (int)((i) & NZMASK) / (float)ZMAXPOS /* Pack Normal vector into int */ #define PNORM(i, nv) \ diff --git a/lib/ogsf/rgbpack.h b/lib/ogsf/rgbpack.h index e277e9a067d..3f3f0beb706 100644 --- a/lib/ogsf/rgbpack.h +++ b/lib/ogsf/rgbpack.h @@ -9,8 +9,8 @@ #define INT_TO_GRN(i, g) (g = (i & GRN_MASK) >> 8) #define INT_TO_BLU(i, b) (b = (i & BLU_MASK) >> 16) -#define RGB_TO_INT(r, g, b, i) \ - (i = (((r)&RED_MASK) + ((int)((g) << 8) & GRN_MASK) + \ +#define RGB_TO_INT(r, g, b, i) \ + (i = (((r) & RED_MASK) + ((int)((g) << 8) & GRN_MASK) + \ ((int)((b) << 16) & BLU_MASK))) #define CONST_COLS 45 diff --git a/lib/ogsf/rowcol.h b/lib/ogsf/rowcol.h index d3db44bf0b6..24c006aa071 100644 --- a/lib/ogsf/rowcol.h +++ b/lib/ogsf/rowcol.h @@ -14,7 +14,7 @@ #define VCOLS(gs) (int)((gs->cols - 1) / gs->x_mod) /* data row & col to offset */ -#define DRC2OFF(gs, drow, dcol) (int)((dcol) + (drow)*gs->cols) +#define DRC2OFF(gs, drow, dcol) (int)((dcol) + (drow) * gs->cols) /* ycoord/xcoord to data row/col */ #define Y2DROW(gs, py) (int)((gs->yrange - (py)) / gs->yres) @@ -32,11 +32,11 @@ #define VCOL2DCOL(gs, vcol) (int)(gs->x_mod * (vcol)) /* data row/col to ycoord/xcoord */ -#define DROW2Y(gs, drow) (gs->yrange - ((drow)*gs->yres)) -#define DCOL2X(gs, dcol) ((dcol)*gs->xres) +#define DROW2Y(gs, drow) (gs->yrange - ((drow) * gs->yres)) +#define DCOL2X(gs, dcol) ((dcol) * gs->xres) /* viewres row/col to ycoord/xcoord */ -#define VROW2Y(gs, vrow) (gs->yrange - ((vrow)*gs->yres * gs->y_mod)) -#define VCOL2X(gs, vcol) ((vcol)*gs->xres * gs->x_mod) +#define VROW2Y(gs, vrow) (gs->yrange - ((vrow) * gs->yres * gs->y_mod)) +#define VCOL2X(gs, vcol) ((vcol) * gs->xres * gs->x_mod) #endif /* _ROWCOL_H */ diff --git a/lib/raster/get_row.c b/lib/raster/get_row.c index 96f48504fc4..1f08a1203f3 100644 --- a/lib/raster/get_row.c +++ b/lib/raster/get_row.c @@ -917,7 +917,7 @@ int Rast__read_null_bits(int fd, int row, unsigned char *flags) } #define check_null_bit(flags, bit_num) \ - ((flags)[(bit_num) >> 3] & ((unsigned char)0x80 >> ((bit_num)&7)) ? 1 : 0) + ((flags)[(bit_num) >> 3] & ((unsigned char)0x80 >> ((bit_num) & 7)) ? 1 : 0) static void get_null_value_row_nomask(int fd, char *flags, int row) { diff --git a/lib/rst/interp_float/segmen2d_parallel.c b/lib/rst/interp_float/segmen2d_parallel.c index cda1a1f9ba2..38e81df4eae 100644 --- a/lib/rst/interp_float/segmen2d_parallel.c +++ b/lib/rst/interp_float/segmen2d_parallel.c @@ -105,11 +105,11 @@ int IL_interp_segments_2d_parallel( cut_tree(tree, all_leafs, &i); G_message(_("Starting parallel work")); -#pragma omp parallel firstprivate( \ - tid, i, j, zmin, zmax, tree, totsegm, offset1, dnorm, smseg, ertot, \ - params, info, all_leafs, bitmask, b, indx, matrix, data_local, A) \ - shared(cursegm, threads, some_thread_failed, zminac, zmaxac, gmin, gmax, \ - c1min, c1max, c2min, c2max) default(none) +#pragma omp parallel firstprivate( \ + tid, i, j, zmin, zmax, tree, totsegm, offset1, dnorm, smseg, ertot, \ + params, info, all_leafs, bitmask, b, indx, matrix, data_local, A) \ + shared(cursegm, threads, some_thread_failed, zminac, zmaxac, gmin, gmax, \ + c1min, c1max, c2min, c2max) default(none) { #pragma omp for schedule(dynamic) for (i_cnt = 0; i_cnt < totsegm; i_cnt++) { diff --git a/lib/vector/Vlib/intersect2.c b/lib/vector/Vlib/intersect2.c index c00ea0c7fb2..a3a7ad1110f 100644 --- a/lib/vector/Vlib/intersect2.c +++ b/lib/vector/Vlib/intersect2.c @@ -344,7 +344,7 @@ static int cross_seg(int i, int j, int b) #define QEVT_CRS 3 #define GET_PARENT(p, c) ((p) = (int)(((c)-2) / 3 + 1)) -#define GET_CHILD(c, p) ((c) = (int)(((p)*3) - 1)) +#define GET_CHILD(c, p) ((c) = (int)(((p) * 3) - 1)) struct qitem { int l; /* line 0 - A line , 1 - B line */ diff --git a/lib/vector/dglib/graph_v1.h b/lib/vector/dglib/graph_v1.h index 7aa3b97d0a4..af954ff4e03 100644 --- a/lib/vector/dglib/graph_v1.h +++ b/lib/vector/dglib/graph_v1.h @@ -65,7 +65,7 @@ #define DGL_EDGESET_EDGECOUNT_v1(p) ((p)[DGL_ILA_TOCNT_v1]) #define DGL_EDGESET_EDGEARRAY_PTR_v1(p) ((p) + DGL_ILA_TOARR_v1) #define DGL_EDGESET_EDGE_PTR_v1(p, i, C) \ - (((p) + DGL_ILA_TOARR_v1) + (i)*DGL_EDGE_WSIZE_v1(C)) + (((p) + DGL_ILA_TOARR_v1) + (i) * DGL_EDGE_WSIZE_v1(C)) /* * Edge macros - addresses in a flat edge diff --git a/lib/vector/diglib/prune.c b/lib/vector/diglib/prune.c index cc9181708c7..0dff46741ff 100644 --- a/lib/vector/diglib/prune.c +++ b/lib/vector/diglib/prune.c @@ -216,7 +216,7 @@ int dig_prune(struct line_pnts *points, double thresh) nt[++it] = ij; } else - endseg : { /* All points are inside threshold. */ + endseg: { /* All points are inside threshold. */ /* Former start becomes new end */ nu[++inu] = jd; if (--it < 0) diff --git a/misc/m.cogo/main.c b/misc/m.cogo/main.c index b2316c809dd..6b4b758e830 100644 --- a/misc/m.cogo/main.c +++ b/misc/m.cogo/main.c @@ -24,8 +24,8 @@ #include #include -#define DEG2RAD(a) ((a)*M_PI / 180.0) -#define RAD2DEG(a) ((a)*180.0 / M_PI) +#define DEG2RAD(a) ((a) * M_PI / 180.0) +#define RAD2DEG(a) ((a) * 180.0 / M_PI) #define DMS2DD(d, m, s) ((d) + ((m) / 60.0) + ((s) / 3600.0)) #define FORMAT_1 " %s %1[NS] %d%c%d%c%lf %1[EW] %lf " #define FORMAT_2 " %1[NS] %d%c%d%c%lf %1[EW] %lf " diff --git a/raster/r.buffer/distance.h b/raster/r.buffer/distance.h index 01262361d10..62fdc3c2e28 100644 --- a/raster/r.buffer/distance.h +++ b/raster/r.buffer/distance.h @@ -48,7 +48,7 @@ extern double meters_to_grid; extern double ns_to_ew_squared; extern int count_rows_with_data; -#define MAPINDEX(r, c) ((size_t)(r)*window.cols + (c)) +#define MAPINDEX(r, c) ((size_t)(r) * window.cols + (c)) #define ZONE_INCR 2 #define FEET_TO_METERS 0.3048 diff --git a/raster/r.cost/flag.h b/raster/r.cost/flag.h index 9ba05270928..1a3c4c36585 100644 --- a/raster/r.cost/flag.h +++ b/raster/r.cost/flag.h @@ -47,13 +47,13 @@ FLAG }; #define FLAG_UNSET(flags, row, col) \ - (flags)->array[(row)][(col) >> 3] &= ~(1 << ((col)&7)) + (flags)->array[(row)][(col) >> 3] &= ~(1 << ((col) & 7)) #define FLAG_SET(flags, row, col) \ - (flags)->array[(row)][(col) >> 3] |= (1 << ((col)&7)) + (flags)->array[(row)][(col) >> 3] |= (1 << ((col) & 7)) #define FLAG_GET(flags, row, col) \ - (flags)->array[(row)][(col) >> 3] & (1 << ((col)&7)) + (flags)->array[(row)][(col) >> 3] & (1 << ((col) & 7)) /* flag.c */ FLAG *flag_create(int, int); diff --git a/raster/r.cost/heap.c b/raster/r.cost/heap.c index df8119ce305..3b8f03b15a8 100644 --- a/raster/r.cost/heap.c +++ b/raster/r.cost/heap.c @@ -43,7 +43,7 @@ #include "cost.h" #define GET_PARENT(c) (((c)-2) / 3 + 1) -#define GET_CHILD(p) (((p)*3) - 1) +#define GET_CHILD(p) (((p) * 3) - 1) static long next_point = 0; static long heap_size = 0; diff --git a/raster/r.mfilter/execute.c b/raster/r.mfilter/execute.c index a2eeb6751b5..9a68412d1a0 100644 --- a/raster/r.mfilter/execute.c +++ b/raster/r.mfilter/execute.c @@ -92,7 +92,7 @@ int execute_filter(ROWIO *r, int *out, FILTER *filter, DCELL **cell) DCELL *cellp = cell[MASTER]; #pragma omp parallel firstprivate(starty, id, start, end, cellp) private( \ - i, count, row, col, cp) if (nprocs > 1) + i, count, row, col, cp) if (nprocs > 1) { #if defined(_OPENMP) if (nprocs > 1) { diff --git a/raster/r.proj/main.c b/raster/r.proj/main.c index 87ff8e6c839..4195b1dbf66 100644 --- a/raster/r.proj/main.c +++ b/raster/r.proj/main.c @@ -259,7 +259,7 @@ int main(int argc, char **argv) #else G_warning(_("Input and output projects are the same")); #endif - G_get_window(&outcellhd); + G_get_window(&outcellhd); if (gprint_bounds->answer && !print_bounds->answer) print_bounds->answer = gprint_bounds->answer; diff --git a/raster/r.random.cells/flag.h b/raster/r.random.cells/flag.h index cf05b0386a2..8c545523077 100644 --- a/raster/r.random.cells/flag.h +++ b/raster/r.random.cells/flag.h @@ -13,13 +13,13 @@ FLAG }; #define FLAG_UNSET(flags, row, col) \ - (flags)->array[(row)][(col) >> 3] &= ~(1 << ((col)&7)) + (flags)->array[(row)][(col) >> 3] &= ~(1 << ((col) & 7)) #define FLAG_SET(flags, row, col) \ - (flags)->array[(row)][(col) >> 3] |= (1 << ((col)&7)) + (flags)->array[(row)][(col) >> 3] |= (1 << ((col) & 7)) #define FLAG_GET(flags, row, col) \ - (flags)->array[(row)][(col) >> 3] & (1 << ((col)&7)) + (flags)->array[(row)][(col) >> 3] & (1 << ((col) & 7)) /* flag.[ch] is a set of routines which will set up an array of bits ** that allow the programmer to "flag" cells in a raster map. diff --git a/raster/r.random.cells/ransurf.h b/raster/r.random.cells/ransurf.h index fdcec8b03c3..ee14648c100 100644 --- a/raster/r.random.cells/ransurf.h +++ b/raster/r.random.cells/ransurf.h @@ -5,7 +5,7 @@ #include #include "flag.h" -#define ODD(a) ((a)&1) +#define ODD(a) ((a) & 1) #define PI M_PI diff --git a/raster/r.random.surface/ransurf.h b/raster/r.random.surface/ransurf.h index 6976a4e4e23..f7fc1e3e59d 100644 --- a/raster/r.random.surface/ransurf.h +++ b/raster/r.random.surface/ransurf.h @@ -6,7 +6,7 @@ #include #include -#define ODD(a) ((a)&1) +#define ODD(a) ((a) & 1) #define MAX_INTERVAL 10 #define MIN_INTERVAL -10 diff --git a/raster/r.ros/main.c b/raster/r.ros/main.c index 68b61fd2153..b8e6dc79655 100644 --- a/raster/r.ros/main.c +++ b/raster/r.ros/main.c @@ -76,7 +76,7 @@ #include #include "local_proto.h" -#define DATA(map, r, c) (map)[(r)*ncols + (c)] +#define DATA(map, r, c) (map)[(r) * ncols + (c)] /*measurements of the 13 fuel models, input of Rothermel equation (1972) */ float WO[4][14] = {{0, 0.034, 0.092, 0.138, 0.230, 0.046, 0.069, 0.052, 0.069, diff --git a/raster/r.ros/spot_dist.c b/raster/r.ros/spot_dist.c index a420d9a35da..c8fbedcd02e 100644 --- a/raster/r.ros/spot_dist.c +++ b/raster/r.ros/spot_dist.c @@ -34,7 +34,7 @@ #include #include -#define DATA(map, r, c) (map)[(r)*ncols + (c)] +#define DATA(map, r, c) (map)[(r) * ncols + (c)] #define DEG2RAD M_D2R /*#define DEBUG */ diff --git a/raster/r.sim/simlib/hydro.c b/raster/r.sim/simlib/hydro.c index 2acc8648d40..1edcadc9458 100644 --- a/raster/r.sim/simlib/hydro.c +++ b/raster/r.sim/simlib/hydro.c @@ -273,7 +273,7 @@ void main_loop(void) eff = 0.0; #pragma omp parallel firstprivate(l, lw, k, decr, d1, hhc, velx, vely, eff, \ - gaux, gauy) // nwalka + gaux, gauy) // nwalka { #if defined(_OPENMP) int steps = diff --git a/raster/r.slope.aspect/main.c b/raster/r.slope.aspect/main.c index 10fa97a66f0..b3c30956467 100644 --- a/raster/r.slope.aspect/main.c +++ b/raster/r.slope.aspect/main.c @@ -589,10 +589,10 @@ int main(int argc, char *argv[]) int start = written; int end = written + range; -#pragma omp parallel if (nprocs > 1) \ - firstprivate(north, east, south, west, ns_med, H, V) private( \ - row, col, size, slp_ptr, asp_ptr, pcurv_ptr, tcurv_ptr, dx_ptr, \ - dxx_ptr, dxy_ptr, dy_ptr, dyy_ptr) +#pragma omp parallel if (nprocs > 1) \ + firstprivate(north, east, south, west, ns_med, H, V) private( \ + row, col, size, slp_ptr, asp_ptr, pcurv_ptr, tcurv_ptr, dx_ptr, \ + dxx_ptr, dxy_ptr, dy_ptr, dyy_ptr) { int t_id = FIRST_THREAD; @@ -617,10 +617,9 @@ int main(int argc, char *argv[]) /* static scheduling is essential for buffer to be initialized * properly */ -#pragma omp for schedule(static) reduction(min \ - : c1min, c2min, min_asp, min_slp) \ - reduction(max \ - : c1max, c2max, max_asp, max_slp) +#pragma omp for schedule(static) \ + reduction(min : c1min, c2min, min_asp, min_slp) \ + reduction(max : c1max, c2max, max_asp, max_slp) for (row = start; row < end; row++) { if (!initialized) { initialized = true; diff --git a/raster/r.spread/collect_ori.c b/raster/r.spread/collect_ori.c index 8618468c380..1972e74577a 100644 --- a/raster/r.spread/collect_ori.c +++ b/raster/r.spread/collect_ori.c @@ -16,7 +16,7 @@ #include "costHa.h" #include "local_proto.h" -#define DATA(map, r, c) (map)[(r)*ncols + (c)] +#define DATA(map, r, c) (map)[(r) * ncols + (c)] /*#define DEBUG */ diff --git a/raster/r.spread/main.c b/raster/r.spread/main.c index 8d1a9513c5d..e1b5e7ff31e 100644 --- a/raster/r.spread/main.c +++ b/raster/r.spread/main.c @@ -39,7 +39,7 @@ #include "cell_ptrHa.h" #include "local_proto.h" -#define DATA(map, r, c) (map)[(r)*ncols + (c)] +#define DATA(map, r, c) (map)[(r) * ncols + (c)] CELL *cell; CELL *x_cell; diff --git a/raster/r.spread/ram2out.c b/raster/r.spread/ram2out.c index 945578e6c0b..ee13cf170c6 100644 --- a/raster/r.spread/ram2out.c +++ b/raster/r.spread/ram2out.c @@ -5,7 +5,7 @@ #include "costHa.h" #include "local_proto.h" -#define DATA(map, r, c) (map)[(r)*ncols + (c)] +#define DATA(map, r, c) (map)[(r) * ncols + (c)] void ram2out(void) { diff --git a/raster/r.spread/select_linksB.c b/raster/r.spread/select_linksB.c index 6ced6e55711..b33341c34b3 100644 --- a/raster/r.spread/select_linksB.c +++ b/raster/r.spread/select_linksB.c @@ -28,7 +28,7 @@ #define PI M_PI #endif -#define DATA(map, r, c) (map)[(r)*ncols + (c)] +#define DATA(map, r, c) (map)[(r) * ncols + (c)] /*#define DEBUG */ diff --git a/raster/r.spread/spot.c b/raster/r.spread/spot.c index 31a2b0d9c23..8be0de16799 100644 --- a/raster/r.spread/spot.c +++ b/raster/r.spread/spot.c @@ -54,7 +54,7 @@ #define PI M_PI #endif -#define DATA(map, r, c) (map)[(r)*ncols + (c)] +#define DATA(map, r, c) (map)[(r) * ncols + (c)] void spot(struct costHa *pres_cell, int dir /* direction of forward ROS */) { diff --git a/raster/r.spread/spread.c b/raster/r.spread/spread.c index ea4012fdd1f..f3b570a0045 100644 --- a/raster/r.spread/spread.c +++ b/raster/r.spread/spread.c @@ -42,7 +42,7 @@ #ifndef PI #define PI M_PI #endif -#define DATA(map, r, c) (map)[(r)*ncols + (c)] +#define DATA(map, r, c) (map)[(r) * ncols + (c)] /*#define DEBUG */ diff --git a/raster/r.stream.extract/local_proto.h b/raster/r.stream.extract/local_proto.h index 0474b51588d..3f31908645a 100644 --- a/raster/r.stream.extract/local_proto.h +++ b/raster/r.stream.extract/local_proto.h @@ -5,7 +5,7 @@ #include "flag.h" #include "seg.h" -#define INDEX(r, c) ((r)*ncols + (c)) +#define INDEX(r, c) ((r) * ncols + (c)) #define MAXDEPTH 1000 /* maximum supported tree depth of stream network */ #define POINT struct a_point diff --git a/raster/r.sun/main.c b/raster/r.sun/main.c index 2b8babfe348..87dd9e00f98 100644 --- a/raster/r.sun/main.c +++ b/raster/r.sun/main.c @@ -1818,21 +1818,19 @@ void calculate(double singleSlope, double singleAspect, double singleAlbedo, } sunVarGeom.zmax = zmax; shadowoffset_base = (j % (numRows)) * n * arrayNumInt; -#pragma omp parallel firstprivate( \ - q1, tan_lam_l, z1, i, shadowoffset, longitTime, coslat, coslatsq, \ - latitude, longitude, sin_phi_l, latid_l, sin_u, cos_u, sin_v, cos_v, lum, \ - gridGeom, elevin, aspin, slopein, civiltime, linkein, albedo, latin, \ - coefbh, coefdh, incidout, longin, horizon, beam_rad, insol_time, diff_rad, \ - refl_rad, glob_rad, mapset, per, decimals, str_step, shouldBeBestAM, \ - isBestAM) +#pragma omp parallel firstprivate( \ + q1, tan_lam_l, z1, i, shadowoffset, longitTime, coslat, coslatsq, \ + latitude, longitude, sin_phi_l, latid_l, sin_u, cos_u, sin_v, \ + cos_v, lum, gridGeom, elevin, aspin, slopein, civiltime, linkein, \ + albedo, latin, coefbh, coefdh, incidout, longin, horizon, \ + beam_rad, insol_time, diff_rad, refl_rad, glob_rad, mapset, per, \ + decimals, str_step, shouldBeBestAM, isBestAM) { -#pragma omp for schedule(dynamic) firstprivate(sunGeom, sunVarGeom, \ - sunSlopeGeom, sunRadVar) \ - lastprivate(sunGeom, sunVarGeom, sunSlopeGeom, sunRadVar) reduction( \ - max \ - : linke_max, albedo_max, lat_max, sunrise_max, sunset_max) \ - reduction(min \ - : linke_min, albedo_min, lat_min, sunrise_min, sunset_min) +#pragma omp for schedule(dynamic) \ + firstprivate(sunGeom, sunVarGeom, sunSlopeGeom, sunRadVar) \ + lastprivate(sunGeom, sunVarGeom, sunSlopeGeom, sunRadVar) \ + reduction(max : linke_max, albedo_max, lat_max, sunrise_max, sunset_max) \ + reduction(min : linke_min, albedo_min, lat_min, sunrise_min, sunset_min) for (i = 0; i < n; i++) { shadowoffset = shadowoffset_base + (arrayNumInt * i); if (useCivilTime()) { diff --git a/raster/r.surf.contour/flag.h b/raster/r.surf.contour/flag.h index 8e5f3e064a2..e32a307c04e 100644 --- a/raster/r.surf.contour/flag.h +++ b/raster/r.surf.contour/flag.h @@ -46,13 +46,13 @@ FLAG }; #define FLAG_UNSET(flags, row, col) \ - (flags)->array[(row)][(col) >> 3] &= ~(1 << ((col)&7)) + (flags)->array[(row)][(col) >> 3] &= ~(1 << ((col) & 7)) #define FLAG_SET(flags, row, col) \ - (flags)->array[(row)][(col) >> 3] |= (1 << ((col)&7)) + (flags)->array[(row)][(col) >> 3] |= (1 << ((col) & 7)) #define FLAG_GET(flags, row, col) \ - (flags)->array[(row)][(col) >> 3] & (1 << ((col)&7)) + (flags)->array[(row)][(col) >> 3] & (1 << ((col) & 7)) /* flag_clr_all.c */ int flag_clear_all(FLAG *); diff --git a/raster/r.surf.idw/pi.h b/raster/r.surf.idw/pi.h index 8b5f1b7cf48..f58fafc5d4a 100644 --- a/raster/r.surf.idw/pi.h +++ b/raster/r.surf.idw/pi.h @@ -2,5 +2,5 @@ #include #define PI M_PI -#define Radians(x) ((x)*PI / 180.0) -#define Degrees(x) ((x)*180.0 / PI) +#define Radians(x) ((x) * PI / 180.0) +#define Degrees(x) ((x) * 180.0 / PI) diff --git a/raster/r.to.vect/areas.c b/raster/r.to.vect/areas.c index 20daf8420ea..77aefc56e8a 100644 --- a/raster/r.to.vect/areas.c +++ b/raster/r.to.vect/areas.c @@ -100,7 +100,7 @@ static int update_width(struct area_table *, int); static int nabors(void); #define get_raster_value(ptr, col) \ - Rast_get_d_value(G_incr_void_ptr(ptr, (col)*data_size), data_type) + Rast_get_d_value(G_incr_void_ptr(ptr, (col) * data_size), data_type) /* extract_areas - trace boundaries of polygons in file */ diff --git a/raster/r.transect/parse_line.c b/raster/r.transect/parse_line.c index 02412e31fed..3896a19cd15 100644 --- a/raster/r.transect/parse_line.c +++ b/raster/r.transect/parse_line.c @@ -4,8 +4,8 @@ #include "local_proto.h" #define PI M_PI -#define Radians(x) ((x)*PI / 180.0) -#define Degrees(x) ((x)*180.0 / PI) +#define Radians(x) ((x) * PI / 180.0) +#define Degrees(x) ((x) * 180.0 / PI) int parse_line(const char *key, char **s, double *e1, double *n1, double *e2, double *n2, int projection) diff --git a/raster/r.walk/flag.h b/raster/r.walk/flag.h index 9ba05270928..1a3c4c36585 100644 --- a/raster/r.walk/flag.h +++ b/raster/r.walk/flag.h @@ -47,13 +47,13 @@ FLAG }; #define FLAG_UNSET(flags, row, col) \ - (flags)->array[(row)][(col) >> 3] &= ~(1 << ((col)&7)) + (flags)->array[(row)][(col) >> 3] &= ~(1 << ((col) & 7)) #define FLAG_SET(flags, row, col) \ - (flags)->array[(row)][(col) >> 3] |= (1 << ((col)&7)) + (flags)->array[(row)][(col) >> 3] |= (1 << ((col) & 7)) #define FLAG_GET(flags, row, col) \ - (flags)->array[(row)][(col) >> 3] & (1 << ((col)&7)) + (flags)->array[(row)][(col) >> 3] & (1 << ((col) & 7)) /* flag.c */ FLAG *flag_create(int, int); diff --git a/raster/r.walk/heap.c b/raster/r.walk/heap.c index df8119ce305..3b8f03b15a8 100644 --- a/raster/r.walk/heap.c +++ b/raster/r.walk/heap.c @@ -43,7 +43,7 @@ #include "cost.h" #define GET_PARENT(c) (((c)-2) / 3 + 1) -#define GET_CHILD(p) (((p)*3) - 1) +#define GET_CHILD(p) (((p) * 3) - 1) static long next_point = 0; static long heap_size = 0; diff --git a/raster/r.water.outlet/ramseg.h b/raster/r.water.outlet/ramseg.h index 3cf563adc19..03f7cb7fcb3 100644 --- a/raster/r.water.outlet/ramseg.h +++ b/raster/r.water.outlet/ramseg.h @@ -5,4 +5,4 @@ #define SEG_INDEX(s, r, c) \ (int)(((((r) >> RAMSEGBITS) * (s) + ((c) >> RAMSEGBITS)) << DOUBLEBITS) + \ - (((r)&SEGLENLESS) << RAMSEGBITS) + ((c)&SEGLENLESS)) + (((r) & SEGLENLESS) << RAMSEGBITS) + ((c) & SEGLENLESS)) diff --git a/raster/r.watershed/ram/do_astar.h b/raster/r.watershed/ram/do_astar.h index c7481abf671..886b06a18b6 100644 --- a/raster/r.watershed/ram/do_astar.h +++ b/raster/r.watershed/ram/do_astar.h @@ -2,6 +2,6 @@ #define __DO_ASTAR_H__ #define GET_PARENT(p, c) ((p) = (int)(((c)-2) / 3 + 1)) -#define GET_CHILD(c, p) ((c) = (int)(((p)*3) - 1)) +#define GET_CHILD(c, p) ((c) = (int)(((p) * 3) - 1)) #endif /* __DO_ASTAR_H__ */ diff --git a/raster/r.watershed/ram/flag.h b/raster/r.watershed/ram/flag.h index 146bcc87a24..7a26def975d 100644 --- a/raster/r.watershed/ram/flag.h +++ b/raster/r.watershed/ram/flag.h @@ -47,13 +47,13 @@ FLAG }; #define FLAG_UNSET(flags, row, col) \ - (flags)->array[(row)][(col) >> 3] &= ~(1 << ((col)&7)) + (flags)->array[(row)][(col) >> 3] &= ~(1 << ((col) & 7)) #define FLAG_SET(flags, row, col) \ - (flags)->array[(row)][(col) >> 3] |= (1 << ((col)&7)) + (flags)->array[(row)][(col) >> 3] |= (1 << ((col) & 7)) #define FLAG_GET(flags, row, col) \ - (flags)->array[(row)][(col) >> 3] & (1 << ((col)&7)) + (flags)->array[(row)][(col) >> 3] & (1 << ((col) & 7)) /* flag_clr_all.c */ int flag_clear_all(FLAG *); diff --git a/raster/r.watershed/ram/ramseg.h b/raster/r.watershed/ram/ramseg.h index 1a512f141d9..2113866aead 100644 --- a/raster/r.watershed/ram/ramseg.h +++ b/raster/r.watershed/ram/ramseg.h @@ -10,6 +10,7 @@ (size_t)( \ ((((size_t)(r) >> RAMSEGBITS) * (s) + ((size_t)(c) >> RAMSEGBITS)) \ << DOUBLEBITS) + \ - (((size_t)(r)&SEGLENLESS) << RAMSEGBITS) + ((size_t)(c)&SEGLENLESS)) + (((size_t)(r) & SEGLENLESS) << RAMSEGBITS) + \ + ((size_t)(c) & SEGLENLESS)) #endif /* __RAMSEG_H__ */ diff --git a/vector/v.cluster/main.c b/vector/v.cluster/main.c index 470ecb2f942..628c4032129 100644 --- a/vector/v.cluster/main.c +++ b/vector/v.cluster/main.c @@ -31,7 +31,7 @@ #define CL_OPTICS2 5 #define GET_PARENT(p, c) ((p) = (int)(((c)-2) / 3 + 1)) -#define GET_CHILD(c, p) ((c) = (int)(((p)*3) - 1)) +#define GET_CHILD(c, p) ((c) = (int)(((p) * 3) - 1)) struct cl_pnt { int uid; diff --git a/vector/v.label.sa/labels.c b/vector/v.label.sa/labels.c index 73cd2527df1..520becf18af 100644 --- a/vector/v.label.sa/labels.c +++ b/vector/v.label.sa/labels.c @@ -102,7 +102,7 @@ label_t *labels_init(struct params *p, int *n_labels) buffer = atof(p->isize->answer); /* use 1 point = 1 map unit */ - if (FT_Set_Char_Size(face, (int)((font_size)*64.0), 0, 100, 100)) + if (FT_Set_Char_Size(face, (int)((font_size) * 64.0), 0, 100, 100)) G_fatal_error(_("Unable to set font size")); /* start reading the map */ From 093895168e8e67595eceb00a747d837157b0085a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 26 Jun 2024 12:25:42 -0400 Subject: [PATCH 020/293] CI(deps): Update docker/build-push-action action to v6.2.0 (#3931) --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 43209646de3..61942fa6df0 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -76,7 +76,7 @@ jobs: password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push id: docker_build - uses: docker/build-push-action@31159d49c0d4756269a0940a750801a1ea5d7003 # v6.1.0 + uses: docker/build-push-action@15560696de535e4014efeff63c48f16952e52dd1 # v6.2.0 with: push: true pull: true From 3986103fdf2c94fd1a6a1b34c06f24b2fb059abf Mon Sep 17 00:00:00 2001 From: Ivan Mincik Date: Thu, 27 Jun 2024 17:33:52 +0000 Subject: [PATCH 021/293] CI: add nix package build test (#3906) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * CI: add nix package and dev environment test * nix: exclude more files from package source --------- Co-authored-by: Edouard Choinière <27212526+echoix@users.noreply.github.com> --- .github/workflows/test-nix.yml | 40 ++++++++++++++++++++++++++++++++++ flake.nix | 3 +++ package.nix | 10 +++++++-- 3 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/test-nix.yml diff --git a/.github/workflows/test-nix.yml b/.github/workflows/test-nix.yml new file mode 100644 index 00000000000..769481b8bab --- /dev/null +++ b/.github/workflows/test-nix.yml @@ -0,0 +1,40 @@ +--- +name: Nix package and environment + +on: + schedule: + - cron: '0 1 * * 1' + push: + tags: + - '*' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} + cancel-in-progress: true + +permissions: {} + +jobs: + test-nix: + runs-on: ubuntu-22.04 + permissions: + contents: read + + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Install nix + uses: DeterminateSystems/nix-installer-action@v12 + + - name: Setup cachix + uses: cachix/cachix-action@v15 + with: + name: osgeo-grass + authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + + - name: Build package + run: nix build -L --accept-flake-config .#grass + + - name: Test development environment + run: nix develop --accept-flake-config --command echo OK diff --git a/flake.nix b/flake.nix index 5283a4a0ca9..49bd16561b7 100644 --- a/flake.nix +++ b/flake.nix @@ -2,6 +2,9 @@ description = "GRASS GIS"; nixConfig = { + extra-substituters = [ "https://osgeo-grass.cachix.org" ]; + extra-trusted-public-keys = [ "osgeo-grass.cachix.org-1:rLnUl3u0ikSIudZ/oBfTqTL7mb3qwYmfmtuwexPpHjw=" ]; + bash-prompt = "\\[\\033[1m\\][grass-dev]\\[\\033\[m\\]\\040\\w >\\040"; }; diff --git a/package.nix b/package.nix index 2a5def372c2..b1b33492163 100644 --- a/package.nix +++ b/package.nix @@ -41,8 +41,14 @@ stdenv.mkDerivation (finalAttrs: { src = lib.cleanSourceWith { src = ./.; - filter = path: type: - ! (lib.hasSuffix ".nix" path); + filter = ( + path: type: (builtins.all (x: x != baseNameOf path) [ + ".git" + ".github" + "flake.nix" + "package.nix" + ]) + ); }; nativeBuildInputs = [ From ab79e435cc6b1348d1d8a48d0eb12b5dc3e0ece1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 27 Jun 2024 18:53:39 +0000 Subject: [PATCH 022/293] CI(deps): Pin dependencies (#3932) --- .github/workflows/test-nix.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-nix.yml b/.github/workflows/test-nix.yml index 769481b8bab..42b5eee93a5 100644 --- a/.github/workflows/test-nix.yml +++ b/.github/workflows/test-nix.yml @@ -25,10 +25,10 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Install nix - uses: DeterminateSystems/nix-installer-action@v12 + uses: DeterminateSystems/nix-installer-action@7993355175c2765e5733dae74f3e0786fe0e5c4f # v12 - name: Setup cachix - uses: cachix/cachix-action@v15 + uses: cachix/cachix-action@ad2ddac53f961de1989924296a1f236fcfbaa4fc # v15 with: name: osgeo-grass authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' From 6748067af1479c914a512084e6f5f412fe4fde3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Thu, 27 Jun 2024 18:48:04 -0400 Subject: [PATCH 023/293] CI: Initial code coverage configuration with Codecov (#3905) --- .github/workflows/pytest.yml | 18 ++++-- .gitignore | 6 ++ Makefile | 6 +- codecov.yml | 114 +++++++++++++++++++++++++++++++++++ 4 files changed, 139 insertions(+), 5 deletions(-) create mode 100644 codecov.yml diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index b307227c920..52b0cd8a808 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -53,7 +53,7 @@ jobs: python -m pip install --upgrade pip pip install -r .github/workflows/python_requirements.txt pip install -r .github/workflows/optional_requirements.txt - pip install pytest pytest-timeout pytest-github-actions-annotate-failures pytest-xdist + pip install pytest pytest-timeout pytest-github-actions-annotate-failures pytest-xdist pytest-cov - name: Create installation directory run: | @@ -70,6 +70,10 @@ jobs: run: | echo "$HOME/install/bin" >> $GITHUB_PATH + - name: Print installed versions + if: always() + run: .github/workflows/print_versions.sh + - name: Test executing of the grass command run: .github/workflows/test_simple.sh @@ -86,12 +90,18 @@ jobs: export PYTHONPATH=`grass --config python_path`:$PYTHONPATH export LD_LIBRARY_PATH=$(grass --config path)/lib:$LD_LIBRARY_PATH pytest --verbose --color=yes --durations=0 --durations-min=0.5 \ + --cov \ -ra . \ -m 'needs_solo_run' - - name: Print installed versions - if: always() - run: .github/workflows/print_versions.sh + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0 + with: + verbose: true + flags: pytest-python-${{ matrix.python-version }} + name: pytest-python-${{ matrix.python-version }} + token: ${{ secrets.CODECOV_TOKEN }} + pytest-success: name: pytest Result needs: diff --git a/.gitignore b/.gitignore index 0db3adb55f5..42fd7650544 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,9 @@ html/ latex/ lib/*/html/ lib/*/latex/ + +# Ignore code coverage files +*.gcov +*.gcno +*.gcda +.coverage diff --git a/Makefile b/Makefile index 497fb7045b3..be8834ecf42 100644 --- a/Makefile +++ b/Makefile @@ -115,11 +115,15 @@ cleandistdirs: cleanscriptstrings: rm -f locale/scriptstrings/*.c 2>/dev/null -clean: cleandistdirs cleanscriptstrings cleandocs +clean: cleandistdirs cleanscriptstrings cleandocs code-coverage-clean libsclean: cleandistdirs $(MAKE) clean-recursive SUBDIRS=$(LIBDIRS) +code-coverage-clean: + -find . -type f \( -name "*.gcda" -o -name "*.gcno" -o -name "*.gcov" \) -delete + -rm -f .coverage + distclean: clean -rm -f config.cache config.log config.status config.status.$(ARCH) 2>/dev/null -rm -f ChangeLog ChangeLog.bak $(ERRORLOG) grass.pc diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 00000000000..b4b97b01433 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,114 @@ +--- +# When modifying this file, please validate using +# curl -X POST --data-binary @codecov.yml https://codecov.io/validate + +coverage: + status: + project: + default: + target: auto # auto compares coverage to the previous base commit + informational: true + patch: + default: + informational: true + +# Disable PR comments initially, until it is possible to have the information as a job summary +comment: false + +github_checks: + # Until more tests are added, disable GitHub checks annotation when added lines aren't covered by tests + annotations: false + +component_management: + individual_components: + - component_id: modules_database # this is an identifier that should not be changed + name: db # this is a display name, and can be changed freely + paths: + - db/** + - lib/db/** + - scripts/db.*/** + - component_id: modules_display + name: display + paths: + - display/** + - lib/display/** + - scripts/d.*/** + - component_id: modules_general + name: general + paths: + - general/** + - scripts/g.*/** + - component_id: gui + name: gui + paths: + - gui/** + - component_id: modules_imagery + name: imagery + paths: + - imagery/** + - lib/imagery/** + - scripts/i.*/** + - component_id: man + name: man + paths: + - man/** + - component_id: modules_misc + name: imagery + paths: + - misc/** + - scripts/m.*/** + - component_id: modules_postscript + name: ps + paths: + - ps/** + - component_id: modules_raster + name: raster + paths: + - raster/** + - lib/raster/** + - scripts/r.*/** + - component_id: modules_raster3d + name: raster3d + paths: + - raster3d/** + - lib/raster3d/** + - scripts/r3.*/** + - component_id: modules_temporal + name: temporal + paths: + - temporal/** + - lib/temporal/** + - scripts/t.*/** + - component_id: modules_vector + name: vector + paths: + - vector/** + - lib/vector/** + - scripts/v.*/** + - component_id: utils + paths: + - utils/** + - component_id: scripts + paths: + - scripts/** + - component_id: library + name: lib + paths: + - lib/** + - component_id: python_pygrass + name: pygrass + paths: + - python/grass/pygrass/** + - component_id: python_gunittest + name: gunittest + paths: + - python/grass/gunittest/** + - component_id: notebooks + name: notebooks (jupyter) + paths: + - python/grass/jupyter/** + - "**/*.ipynb" + - component_id: python_library + name: python library + paths: + - python/grass/** From cb0e431f06ed982adb75d0408eec9c5e03b3330c Mon Sep 17 00:00:00 2001 From: Anna Petrasova Date: Fri, 28 Jun 2024 20:29:41 +0200 Subject: [PATCH 024/293] r.horizon: change JSON format (#3888) --- raster/r.horizon/main.c | 35 +++++++------- raster/r.horizon/testsuite/test_r_horizon.py | 48 ++++++++++---------- 2 files changed, 42 insertions(+), 41 deletions(-) diff --git a/raster/r.horizon/main.c b/raster/r.horizon/main.c index cb34fed3a74..ff0b2429456 100644 --- a/raster/r.horizon/main.c +++ b/raster/r.horizon/main.c @@ -839,8 +839,9 @@ void calculate_point_mode(const Settings *settings, const Geometry *geometry, origin_point.maxlength = settings->fixedMaxLength; /* JSON variables and formating */ - JSON_Value *azimuths_value, *horizons_value, *distances_value; - JSON_Array *azimuths, *horizons, *distances; + + JSON_Value *horizons_value; + JSON_Array *horizons; switch (format) { case PLAIN: @@ -850,18 +851,15 @@ void calculate_point_mode(const Settings *settings, const Geometry *geometry, fprintf(fp, "\n"); break; case JSON: - json_object_set_number(json_origin, "x", xcoord); json_object_set_number(json_origin, "y", ycoord); - azimuths_value = json_value_init_array(); - azimuths = json_value_get_array(azimuths_value); horizons_value = json_value_init_array(); horizons = json_value_get_array(horizons_value); - distances_value = json_value_init_array(); - distances = json_value_get_array(distances_value); break; } for (int i = 0; i < printCount; i++) { + JSON_Value *value; + JSON_Object *object; OriginAngle origin_angle; com_par(geometry, &origin_angle, angle, xp, yp); @@ -872,7 +870,10 @@ void calculate_point_mode(const Settings *settings, const Geometry *geometry, if (settings->degreeOutput) { shadow_angle *= rad2deg; } - + if (format == JSON) { + value = json_value_init_object(); + object = json_object(value); + } if (settings->compassOutput) { double tmpangle; @@ -887,9 +888,10 @@ void calculate_point_mode(const Settings *settings, const Geometry *geometry, fprintf(fp, "\n"); break; case JSON: - json_array_append_number(azimuths, tmpangle); - json_array_append_number(horizons, shadow_angle); - json_array_append_number(distances, horizon.length); + json_object_set_number(object, "azimuth", tmpangle); + json_object_set_number(object, "angle", shadow_angle); + json_object_set_number(object, "distance", horizon.length); + json_array_append_value(horizons, value); break; } } @@ -902,9 +904,10 @@ void calculate_point_mode(const Settings *settings, const Geometry *geometry, fprintf(fp, "\n"); break; case JSON: - json_array_append_number(azimuths, printangle); - json_array_append_number(horizons, shadow_angle); - json_array_append_number(distances, horizon.length); + json_object_set_number(object, "azimuth", printangle); + json_object_set_number(object, "angle", shadow_angle); + json_object_set_number(object, "distance", horizon.length); + json_array_append_value(horizons, value); break; } } @@ -924,9 +927,7 @@ void calculate_point_mode(const Settings *settings, const Geometry *geometry, } /* end of for loop over angles */ if (format == JSON) { - json_object_set_value(json_origin, "azimuth", azimuths_value); - json_object_set_value(json_origin, "horizon_height", horizons_value); - json_object_set_value(json_origin, "horizon_distance", distances_value); + json_object_set_value(json_origin, "horizons", horizons_value); } } diff --git a/raster/r.horizon/testsuite/test_r_horizon.py b/raster/r.horizon/testsuite/test_r_horizon.py index da95e52dde9..1f5f337935d 100644 --- a/raster/r.horizon/testsuite/test_r_horizon.py +++ b/raster/r.horizon/testsuite/test_r_horizon.py @@ -225,20 +225,20 @@ def test_point_mode_multiple_direction_json(self): ) self.assertModule(module) stdout = json.loads(module.outputs.stdout) - azimuths = [] horizons = [] - distances = [] reference = {} for line in ref6.splitlines()[1:]: azimuth, horizon, distance = line.split(",") - azimuths.append(float(azimuth)) - horizons.append(float(horizon)) - distances.append(float(distance)) + horizons.append( + { + "azimuth": float(azimuth), + "angle": float(horizon), + "distance": float(distance), + } + ) reference["x"] = 634720.0 reference["y"] = 216180.0 - reference["azimuth"] = azimuths - reference["horizon_height"] = horizons - reference["horizon_distance"] = distances + reference["horizons"] = horizons self.assertListEqual([reference], stdout) @@ -255,20 +255,20 @@ def test_point_mode_multiple_points_and_directions_json(self): ) self.assertModule(module) stdout = json.loads(module.outputs.stdout) - azimuths = [] horizons = [] - distances = [] reference = {} for line in ref6.splitlines()[1:]: azimuth, horizon, distance = line.split(",") - azimuths.append(float(azimuth)) - horizons.append(float(horizon)) - distances.append(float(distance)) + horizons.append( + { + "azimuth": float(azimuth), + "angle": float(horizon), + "distance": float(distance), + } + ) reference["x"] = 634720.0 reference["y"] = 216180.0 - reference["azimuth"] = azimuths - reference["horizon_height"] = horizons - reference["horizon_distance"] = distances + reference["horizons"] = horizons self.assertListEqual([reference, reference], stdout) @@ -313,20 +313,20 @@ def test_point_mode_multiple_direction_artificial_distance(self): ) self.assertModule(module) stdout = json.loads(module.outputs.stdout) - azimuths = [] horizons = [] - distances = [] reference = {} for line in ref5.splitlines()[1:]: azimuth, horizon, distance = line.split(",") - azimuths.append(float(azimuth)) - horizons.append(float(horizon)) - distances.append(float(distance)) + horizons.append( + { + "azimuth": float(azimuth), + "angle": float(horizon), + "distance": float(distance), + } + ) reference["x"] = 637505.0 reference["y"] = 221755.0 - reference["azimuth"] = azimuths - reference["horizon_height"] = horizons - reference["horizon_distance"] = distances + reference["horizons"] = horizons self.assertListEqual([reference], stdout) From 695e550164dfe8cd35ee77aa87174bd526a9be0c Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Fri, 28 Jun 2024 15:44:04 -0400 Subject: [PATCH 025/293] d.histogram: Initialize range_dmin and range_dmax to fix uninitialize value warnings in bar.c (#3923) The change satisfies the warning and does not change the behavior. The initial values (garbage or 0) are actually not used. `is_fp` conditions are used to set the values and drive their use (so that the initial values are not used). --- display/d.histogram/bar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/display/d.histogram/bar.c b/display/d.histogram/bar.c index 244e6ffcade..11243582dd3 100644 --- a/display/d.histogram/bar.c +++ b/display/d.histogram/bar.c @@ -39,7 +39,7 @@ int bar(struct stat_list *dist_stats, /* list of distribution statistics */ int draw = YES; long int bar_height; /* height, in pixels, of a histogram bar */ CELL bar_color; /* color/category number of a histogram bar */ - DCELL dmax, range_dmin, range_dmax, dmin, dval; + DCELL dmax, range_dmin = 0, range_dmax = 0, dmin, dval; long int max_tics; /* maximum tics allowed on an axis */ long int xoffset; /* offset for x-axis */ long int yoffset; /* offset for y-axis */ From fb52c5b3924e143e10282ed71eb6cc3508a9e664 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 28 Jun 2024 21:43:08 +0000 Subject: [PATCH 026/293] CI(deps): Update github/codeql-action action to v3.25.11 (#3938) --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/python-code-quality.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 16dc5732791..3ce82c218fb 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,7 +56,7 @@ jobs: if: ${{ matrix.language == 'c-cpp' }} - name: Initialize CodeQL - uses: github/codeql-action/init@23acc5c183826b7a8a97bce3cecc52db901f8251 # v3.25.10 + uses: github/codeql-action/init@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 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@23acc5c183826b7a8a97bce3cecc52db901f8251 # v3.25.10 + uses: github/codeql-action/analyze@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/python-code-quality.yml b/.github/workflows/python-code-quality.yml index 8a2f70bd31d..d54ceb5e0d9 100644 --- a/.github/workflows/python-code-quality.yml +++ b/.github/workflows/python-code-quality.yml @@ -114,7 +114,7 @@ jobs: path: bandit.sarif - name: Upload SARIF File into Security Tab - uses: github/codeql-action/upload-sarif@23acc5c183826b7a8a97bce3cecc52db901f8251 # v3.25.10 + uses: github/codeql-action/upload-sarif@b611370bb5703a7efb587f9d136a52ea24c5c38c # v3.25.11 with: sarif_file: bandit.sarif From 9e87f51dd7efaae0d4f45ab6238ca7f1ecfe41bf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 28 Jun 2024 18:14:22 -0400 Subject: [PATCH 027/293] CI(deps): Update dependency PDAL/PDAL to v2.7.2 (#3937) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- docker/debian/Dockerfile | 2 +- docker/ubuntu_wxgui/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/debian/Dockerfile b/docker/debian/Dockerfile index d46b955ce0b..2ed59e1482e 100644 --- a/docker/debian/Dockerfile +++ b/docker/debian/Dockerfile @@ -10,7 +10,7 @@ ENV DEBIAN_FRONTEND noninteractive # define versions to be used (PDAL is not available on Debian, so we compile it here) # https://github.com/PDAL/PDAL/releases # renovate: datasource=github-tags depName=PDAL/PDAL -ARG PDAL_VERSION=2.7.1 +ARG PDAL_VERSION=2.7.2 SHELL ["/bin/bash", "-c"] diff --git a/docker/ubuntu_wxgui/Dockerfile b/docker/ubuntu_wxgui/Dockerfile index 402bbccd04c..9cad91a607b 100644 --- a/docker/ubuntu_wxgui/Dockerfile +++ b/docker/ubuntu_wxgui/Dockerfile @@ -8,7 +8,7 @@ ENV DEBIAN_FRONTEND noninteractive # define versions to be used (PDAL is not available on Ubuntu/Debian, so we compile it here) # https://github.com/PDAL/PDAL/releases # renovate: datasource=github-tags depName=PDAL/PDAL -ARG PDAL_VERSION=2.7.1 +ARG PDAL_VERSION=2.7.2 SHELL ["/bin/bash", "-c"] From b5bfcd576541c1b4d43187c4d66ff0024167bab1 Mon Sep 17 00:00:00 2001 From: Makiko Shukunobe Date: Sat, 29 Jun 2024 14:19:20 -0400 Subject: [PATCH 028/293] Checks: fix flake8 E741 in gui/wxpython directory (#3926) * Rename self.l to self.item_attr when using self.l as an argument to wx.ItemAttr(). * Rename l to layer when using l for loop through layer list. * Rename l to new_layer_lst when using l as an argument to self._tree.AddLayer(). * Rename l to rli_conf when looping through the configuration files in self.rlipath. * Rename l to layer when looping through the self.map_.GetListOfLayers(). * Rename l to dataset_info when sorting `allDatasets` with the lambda function. * Rename l to layer_num when looping through `catsDict.items()`. * Rename l to layer when retrieving layer information in a function. * Rename l to curr when looping through the `curr_sel_ls`. * Remove E741 for files in gui/wxpython directory. --- .flake8 | 16 ++++++--------- gui/wxpython/iclass/dialogs.py | 8 ++++---- gui/wxpython/iscatt/frame.py | 8 ++++---- gui/wxpython/lmgr/giface.py | 6 +++--- gui/wxpython/rlisetup/frame.py | 6 +++--- gui/wxpython/rlisetup/wizard.py | 10 +++++----- gui/wxpython/timeline/frame.py | 5 ++++- gui/wxpython/tplot/frame.py | 5 ++++- gui/wxpython/vdigit/wxdisplay.py | 4 ++-- gui/wxpython/web_services/widgets.py | 30 ++++++++++++++-------------- 10 files changed, 50 insertions(+), 48 deletions(-) diff --git a/.flake8 b/.flake8 index 92caa926bf4..9786c9775a3 100644 --- a/.flake8 +++ b/.flake8 @@ -71,8 +71,7 @@ per-file-ignores = gui/wxpython/gui_core/widgets.py: F841, E722, E266 gui/wxpython/image2target/*: F841, E722, E265 gui/wxpython/image2target/g.gui.image2target.py: E501, E265, F841 - gui/wxpython/iscatt/*: F841, E722, E741, F405, F403 - gui/wxpython/lmgr/giface.py: E741 + gui/wxpython/iscatt/*: F841, E722, F405, F403 gui/wxpython/lmgr/frame.py: F841, E722 # layertree still includes some formatting issues (it is ignored by Black) gui/wxpython/lmgr/layertree.py: E722, E266, W504, E225 @@ -82,16 +81,15 @@ per-file-ignores = gui/wxpython/photo2image/*: F841, E722, E265 gui/wxpython/photo2image/g.gui.photo2image.py: E501, F841 gui/wxpython/psmap/*: F841, E266, E722, F405, F403 - gui/wxpython/vdigit/*: F841, E722, E741, F405, F403 + gui/wxpython/vdigit/*: F841, E722, F405, F403 gui/wxpython/vnet/*: F841 gui/wxpython/wxgui.py: F841 gui/wxpython/animation/g.gui.animation.py: E501 gui/wxpython/animation/mapwindow.py: F841 gui/wxpython/animation/provider.py: F841 - gui/wxpython/tplot/frame.py: F841, E722, E741 + gui/wxpython/tplot/frame.py: F841, E722 gui/wxpython/tplot/g.gui.tplot.py: E501 gui/wxpython/rdigit/g.gui.rdigit.py: F841 - gui/wxpython/iclass/dialogs.py: E741 gui/wxpython/iclass/digit.py: F405, F403 gui/wxpython/iclass/frame.py: F405, F403 gui/wxpython/iclass/g.gui.iclass.py: E501 @@ -109,14 +107,12 @@ per-file-ignores = gui/wxpython/mapwin/buffered.py: E722 gui/wxpython/mapwin/graphics.py: E722 gui/wxpython/startup/locdownload.py: E722, E402 - gui/wxpython/timeline/g.gui.timeline.py: E501, E741 - gui/wxpython/timeline/frame.py: E741 + gui/wxpython/timeline/g.gui.timeline.py: E501 gui/wxpython/tools/build_modules_xml.py: E722 gui/wxpython/web_services/cap_interface.py: E501 - gui/wxpython/web_services/widgets.py: F841, E741, E402 - gui/wxpython/rlisetup/frame.py: E741 + gui/wxpython/web_services/widgets.py: F841, E402 gui/wxpython/rlisetup/sampling_frame.py: F841 - gui/wxpython/rlisetup/wizard.py: E722, E741 + gui/wxpython/rlisetup/wizard.py: E722 # Generated file gui/wxpython/menustrings.py: E501 # F821 undefined name 'cmp' diff --git a/gui/wxpython/iclass/dialogs.py b/gui/wxpython/iclass/dialogs.py index 48537c10548..1ca26360336 100644 --- a/gui/wxpython/iclass/dialogs.py +++ b/gui/wxpython/iclass/dialogs.py @@ -573,10 +573,10 @@ def OnGetItemAttr(self, item): text_c = wx.Colour(*ContrastColor(back_c)) # if it is in scope of the method, gui falls, using self solved it - self.l = wx.ItemAttr() - self.l.SetBackgroundColour(back_c) - self.l.SetTextColour(text_c) - return self.l + self.item_attr = wx.ItemAttr() + self.item_attr.SetBackgroundColour(back_c) + self.item_attr.SetTextColour(text_c) + return self.item_attr def ContrastColor(color): diff --git a/gui/wxpython/iscatt/frame.py b/gui/wxpython/iscatt/frame.py index 9ff5a6ceac6..9398ce19349 100644 --- a/gui/wxpython/iscatt/frame.py +++ b/gui/wxpython/iscatt/frame.py @@ -543,10 +543,10 @@ def OnGetItemAttr(self, item): text_c = wx.SystemSettings.GetColour(wx.SYS_COLOUR_INACTIVECAPTIONTEXT) # if it is in scope of the method, gui falls, using self solved it - self.l = wx.ItemAttr() - self.l.SetBackgroundColour(back_c) - self.l.SetTextColour(text_c) - return self.l + self.item_attr = wx.ItemAttr() + self.item_attr.SetBackgroundColour(back_c) + self.item_attr.SetTextColour(text_c) + return self.item_attr def OnCategoryRightUp(self, event): """Show context menu on right click""" diff --git a/gui/wxpython/lmgr/giface.py b/gui/wxpython/lmgr/giface.py index 1c347d35a0f..aa5af7223aa 100644 --- a/gui/wxpython/lmgr/giface.py +++ b/gui/wxpython/lmgr/giface.py @@ -66,7 +66,7 @@ def __iter__(self): def __getitem__(self, index): """Select a layer from the LayerList using the index.""" - return [l for l in self][index] + return [layer for layer in self][index] def __repr__(self): """Return a representation of the object.""" @@ -105,10 +105,10 @@ def AddLayer(self, ltype, name=None, checked=None, opacity=1.0, cmd=None): :param opacity: layer opacity level :param cmd: command (given as a list) """ - l = self._tree.AddLayer( + new_layer_lst = self._tree.AddLayer( ltype=ltype, lname=name, lchecked=checked, lopacity=opacity, lcmd=cmd ) - return Layer(l, self._tree.GetPyData(l)) + return Layer(new_layer_lst, self._tree.GetPyData(new_layer_lst)) def DeleteLayer(self, layer): """Remove layer from layer list""" diff --git a/gui/wxpython/rlisetup/frame.py b/gui/wxpython/rlisetup/frame.py index 6b239f8c8b9..14be28d1e66 100644 --- a/gui/wxpython/rlisetup/frame.py +++ b/gui/wxpython/rlisetup/frame.py @@ -217,9 +217,9 @@ def ListFiles(self): listfiles = [] # return all the configuration files in self.rlipath, check if there are # link or directory and doesn't add them - for l in os.listdir(self.rlipath): - if os.path.isfile(os.path.join(self.rlipath, l)): - listfiles.append(l) + for rli_conf in os.listdir(self.rlipath): + if os.path.isfile(os.path.join(self.rlipath, rli_conf)): + listfiles.append(rli_conf) return sorted(listfiles) def OnClose(self, event): diff --git a/gui/wxpython/rlisetup/wizard.py b/gui/wxpython/rlisetup/wizard.py index 320eb370a04..67392a0891c 100644 --- a/gui/wxpython/rlisetup/wizard.py +++ b/gui/wxpython/rlisetup/wizard.py @@ -1888,13 +1888,13 @@ def newCat(self): opacity=1.0, render=True, ) - for l in self.map_.GetListOfLayers(): - if l.name == self.outname: + for layer in self.map_.GetListOfLayers(): + if layer.name == self.outname: self.mapPanel.mapWindow.ZoomToMap( - layers=[l], render=True, ignoreNulls=True + layers=[layer], render=True, ignoreNulls=True ) - elif l.name != self.rast: - self.map_.DeleteLayer(l) + elif layer.name != self.rast: + self.map_.DeleteLayer(layer) self.areaText.SetLabel("Is this area (cat={n}) ok?".format(n=cat)) def OnEnterPage(self, event): diff --git a/gui/wxpython/timeline/frame.py b/gui/wxpython/timeline/frame.py index b594ee1cfbc..632d30f233f 100644 --- a/gui/wxpython/timeline/frame.py +++ b/gui/wxpython/timeline/frame.py @@ -503,7 +503,10 @@ def _checkDatasets(self, datasets): ) mapsets = tgis.get_tgis_c_library_interface().available_mapsets() allDatasets = [ - i for i in sorted(allDatasets, key=lambda l: mapsets.index(l[1])) + i + for i in sorted( + allDatasets, key=lambda dataset_info: mapsets.index(dataset_info[1]) + ) ] for dataset in datasets: diff --git a/gui/wxpython/tplot/frame.py b/gui/wxpython/tplot/frame.py index 0b36b3704fb..bfb2feca0c5 100755 --- a/gui/wxpython/tplot/frame.py +++ b/gui/wxpython/tplot/frame.py @@ -1159,7 +1159,10 @@ def _checkDatasets(self, datasets, typ): ) mapsets = tgis.get_tgis_c_library_interface().available_mapsets() allDatasets = [ - i for i in sorted(allDatasets, key=lambda l: mapsets.index(l[1])) + i + for i in sorted( + allDatasets, key=lambda dataset_info: mapsets.index(dataset_info[1]) + ) ] for dataset in datasets: diff --git a/gui/wxpython/vdigit/wxdisplay.py b/gui/wxpython/vdigit/wxdisplay.py index c44859c976b..afbc877f73f 100644 --- a/gui/wxpython/vdigit/wxdisplay.py +++ b/gui/wxpython/vdigit/wxdisplay.py @@ -1182,8 +1182,8 @@ def _getCatString(self, line): catsDict[layer].append(cats.cat[i]) catsStr = "" - for l, c in catsDict.items(): - catsStr = "%d: (%s)" % (l, ",".join(map(str, c))) + for layer_num, c in catsDict.items(): + catsStr = "%d: (%s)" % (layer_num, ",".join(map(str, c))) return catsStr diff --git a/gui/wxpython/web_services/widgets.py b/gui/wxpython/web_services/widgets.py index 1a4b4c5501f..6e29e22e701 100644 --- a/gui/wxpython/web_services/widgets.py +++ b/gui/wxpython/web_services/widgets.py @@ -453,20 +453,20 @@ def OnDown(self, event): def _updateLayerOrderList(self, selected=None): """Update order in list.""" - def getlayercaption(l): - if l["title"]: - cap = l["title"] + def getlayercaption(layer): + if layer["title"]: + cap = layer["title"] else: - cap = l["name"] + cap = layer["name"] - if l["style"]: - if l["style"]["title"]: - cap += " / " + l["style"]["title"] + if layer["style"]: + if layer["style"]["title"]: + cap += " / " + layer["style"]["title"] else: - cap += " / " + l["style"]["name"] + cap += " / " + layer["style"]["name"] return cap - layer_capts = [getlayercaption(l) for l in self.sel_layers] + layer_capts = [getlayercaption(sel_layer) for sel_layer in self.sel_layers] self.l_odrder_list.Set(layer_capts) if self.l_odrder_list.IsEmpty(): self.enableButtons(False) @@ -634,7 +634,7 @@ def UpdateWidgetsByCmd(self, cmd): # WMS standard - first layer in params is most bottom... # therefore layers order need to be reversed - l_st_list = [l for l in reversed(l_st_list)] + l_st_list = [layer for layer in reversed(l_st_list)] self.list.SelectLayers(l_st_list) params = {} @@ -744,9 +744,9 @@ def OnListSelChanged(self, event): if sel_l not in curr_sel_ls: self.sel_layers.remove(sel_l) - for l in curr_sel_ls: - if l not in self.sel_layers: - self.sel_layers.append(l) + for curr in curr_sel_ls: + if curr not in self.sel_layers: + self.sel_layers.append(curr) self._updateLayerOrderList() else: @@ -759,8 +759,8 @@ def OnListSelChanged(self, event): intersect_proj = [] first = True - for l in curr_sel_ls: - layer_projs = l["cap_intf_l"].GetLayerData("srs") + for curr in curr_sel_ls: + layer_projs = curr["cap_intf_l"].GetLayerData("srs") if first: projs_list = layer_projs first = False From 373800152d4f3b70639e2a7b1942f03b06c2c51b Mon Sep 17 00:00:00 2001 From: ShubhamDesai <42180509+ShubhamDesai@users.noreply.github.com> Date: Sun, 30 Jun 2024 12:09:39 -0400 Subject: [PATCH 029/293] d.histogram: Fix dead store warnings in bar.c (#3927) * d.histogram: Remove unnecessary assignment of draw variable in bar.c --- display/d.histogram/bar.c | 1 - 1 file changed, 1 deletion(-) diff --git a/display/d.histogram/bar.c b/display/d.histogram/bar.c index 11243582dd3..e02527256f5 100644 --- a/display/d.histogram/bar.c +++ b/display/d.histogram/bar.c @@ -147,7 +147,6 @@ int bar(struct stat_list *dist_stats, /* list of distribution statistics */ for (i = dist_stats->mincat; i <= dist_stats->maxcat; i++) { if (!ptr) break; - draw = NO; /* figure bar color and height * * the cat number determines the color, the corresponding stat, From 386d47844126ddf1a34b1b4a389d87e471224f5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sun, 30 Jun 2024 20:17:30 -0400 Subject: [PATCH 030/293] style: Fixes unnecessary-collection-call (C408) for remaining code (#3948) * style: Fixes unnecessary-collection-call (C408) for remaining code Only applies fixes to non-empty collections by `ruff check --select "C408" --unsafe-fixes --output-format=concise --fix` in order to limit the review scope. * style: Apply black formatting Separated in another commit for easier review of only the original changes without formatting * style: Fixes unnecessary-collection-call (C408) for ctypesgen Only applies fixes to non-empty collections by `ruff check --select "C408" --unsafe-fixes --output-format=concise --fix` in order to limit the review scope. * style: Fixes unnecessary-collection-call (C408) for gui/wxpython/psmap/instructions * style: Adjust comment in gui/wxpython/psmap/instructions * style: Remove comment in gui/wxpython/psmap/instructions --- gui/wxpython/core/menutree.py | 40 +- gui/wxpython/datacatalog/tree.py | 106 ++-- gui/wxpython/dbmgr/base.py | 15 +- gui/wxpython/gui_core/prompt.py | 2 +- gui/wxpython/history/tree.py | 42 +- gui/wxpython/lmgr/pyshell.py | 12 +- gui/wxpython/mapdisp/frame.py | 2 +- gui/wxpython/modules/mcalc_builder.py | 2 +- gui/wxpython/nviz/tools.py | 16 +- gui/wxpython/psmap/frame.py | 24 +- gui/wxpython/psmap/instructions.py | 482 +++++++++--------- gui/wxpython/timeline/frame.py | 4 +- gui/wxpython/tplot/frame.py | 4 +- python/grass/benchmark/results.py | 2 +- python/grass/gunittest/case.py | 6 +- python/grass/gunittest/invoker.py | 2 +- python/grass/script/vector.py | 16 +- .../ctypesgen/libraryloader.py | 2 +- scripts/g.manual/g.manual.py | 4 +- scripts/r.import/r.import.py | 42 +- scripts/r.out.xyz/r.out.xyz.py | 8 +- scripts/v.in.e00/v.in.e00.py | 4 +- utils/g.html2man/ggroff.py | 16 +- 23 files changed, 428 insertions(+), 425 deletions(-) diff --git a/gui/wxpython/core/menutree.py b/gui/wxpython/core/menutree.py index 7e4abe9d902..2b583b5581d 100644 --- a/gui/wxpython/core/menutree.py +++ b/gui/wxpython/core/menutree.py @@ -88,16 +88,16 @@ def _createMenu(self, menu, node): def _createItem(self, item, node): if item.tag == "separator": - data = dict( - label="", - description="", - handler="", - command="", - keywords="", - shortcut="", - wxId="", - icon="", - ) + data = { + "label": "", + "description": "", + "handler": "", + "command": "", + "keywords": "", + "shortcut": "", + "wxId": "", + "icon": "", + } self.model.AppendNode(parent=node, label="", data=data) elif item.tag == "menuitem": origLabel = _(item.find("label").text) @@ -138,16 +138,16 @@ def _createItem(self, item, node): label += " [" + gcmd + "]" elif self.menustyle == 2: label = " [" + gcmd + "]" - data = dict( - label=origLabel, - description=desc, - handler=handler, - command=gcmd, - keywords=keywords, - shortcut=shortcut, - wxId=wxId, - icon=icon, - ) + data = { + "label": origLabel, + "description": desc, + "handler": handler, + "command": gcmd, + "keywords": keywords, + "shortcut": shortcut, + "wxId": wxId, + "icon": icon, + } self.model.AppendNode(parent=node, label=label, data=data) elif item.tag == "menu": self._createMenu(item, node) diff --git a/gui/wxpython/datacatalog/tree.py b/gui/wxpython/datacatalog/tree.py index 2bb3b59894e..be15db70e32 100644 --- a/gui/wxpython/datacatalog/tree.py +++ b/gui/wxpython/datacatalog/tree.py @@ -426,14 +426,14 @@ def _reloadLocationNode(self, location_node): mapset_node = self._model.AppendNode( parent=location_node, - data=dict( - type="mapset", - name=mapset, - current=False, - lock=is_mapset_locked(mapset_path), - is_different_owner=is_different_mapset_owner(mapset_path), - owner=get_mapset_owner(mapset_path), - ), + data={ + "type": "mapset", + "name": mapset, + "current": False, + "lock": is_mapset_locked(mapset_path), + "is_different_owner": is_different_mapset_owner(mapset_path), + "owner": get_mapset_owner(mapset_path), + }, ) self._populateMapsetItem(mapset_node, maps[mapset]) self._model.SortChildren(location_node) @@ -450,7 +450,7 @@ def _lazyReloadGrassDBNode(self, grassdb_node): locations = GetListOfLocations(grassdb_node.data["name"]) for location in locations: loc_node = self._model.AppendNode( - parent=grassdb_node, data=dict(type="location", name=location) + parent=grassdb_node, data={"type": "location", "name": location} ) all_location_nodes.append(loc_node) q = Queue() @@ -464,14 +464,14 @@ def _lazyReloadGrassDBNode(self, grassdb_node): ) mapset_node = self._model.AppendNode( parent=loc_node, - data=dict( - type="mapset", - name=key, - lock=is_mapset_locked(mapset_path), - current=False, - is_different_owner=is_different_mapset_owner(mapset_path), - owner=get_mapset_owner(mapset_path), - ), + data={ + "type": "mapset", + "name": key, + "lock": is_mapset_locked(mapset_path), + "current": False, + "is_different_owner": is_different_mapset_owner(mapset_path), + "owner": get_mapset_owner(mapset_path), + }, ) if ( grassdb_node.data["name"] == genv["GISDBASE"] @@ -511,7 +511,7 @@ def _reloadGrassDBNode(self, grassdb_node): for location in locations: results[location] = dict() varloc = self._model.AppendNode( - parent=grassdb_node, data=dict(type="location", name=location) + parent=grassdb_node, data={"type": "location", "name": location} ) location_nodes.append(varloc) all_location_nodes.append(varloc) @@ -552,16 +552,16 @@ def _reloadGrassDBNode(self, grassdb_node): ) mapset_node = self._model.AppendNode( parent=location_nodes[i], - data=dict( - type="mapset", - name=key, - lock=is_mapset_locked(mapset_path), - current=False, - is_different_owner=is_different_mapset_owner( + data={ + "type": "mapset", + "name": key, + "lock": is_mapset_locked(mapset_path), + "current": False, + "is_different_owner": is_different_mapset_owner( mapset_path ), - owner=get_mapset_owner(mapset_path), - ), + "owner": get_mapset_owner(mapset_path), + }, ) self._populateMapsetItem(mapset_node, maps[key]) @@ -593,7 +593,7 @@ def _reloadTreeItems(self, full=False): if not grassdb_nodes: grassdb_node = self._model.AppendNode( parent=self._model.root, - data=dict(type="grassdb", name=grassdatabase), + data={"type": "grassdb", "name": grassdatabase}, ) else: grassdb_node = grassdb_nodes[0] @@ -722,11 +722,11 @@ def _quickLoading(self): gisenv = gscript.gisenv() for grassdatabase in self.grassdatabases: grassdb_node = self._model.AppendNode( - parent=self._model.root, data=dict(type="grassdb", name=grassdatabase) + parent=self._model.root, data={"type": "grassdb", "name": grassdatabase} ) for location in GetListOfLocations(grassdatabase): self._model.AppendNode( - parent=grassdb_node, data=dict(type="location", name=location) + parent=grassdb_node, data={"type": "location", "name": location} ) self.RefreshItems() if grassdatabase == gisenv["GISDBASE"]: @@ -1450,7 +1450,7 @@ def _removeMapAfterCopy(self, cLayer, cMapset, env): def InsertLayer(self, name, mapset_node, element_name): """Insert layer into model and refresh tree""" self._model.AppendNode( - parent=mapset_node, data=dict(type=element_name, name=name) + parent=mapset_node, data={"type": element_name, "name": name} ) self._model.SortChildren(mapset_node) self.RefreshNode(mapset_node, recursive=True) @@ -1463,14 +1463,14 @@ def InsertMapset(self, name, location_node): ) mapset_node = self._model.AppendNode( parent=location_node, - data=dict( - type="mapset", - name=name, - current=False, - lock=is_mapset_locked(mapset_path), - is_different_owner=is_different_mapset_owner(mapset_path), - owner=get_mapset_owner(mapset_path), - ), + data={ + "type": "mapset", + "name": name, + "current": False, + "lock": is_mapset_locked(mapset_path), + "is_different_owner": is_different_mapset_owner(mapset_path), + "owner": get_mapset_owner(mapset_path), + }, ) self._model.SortChildren(location_node) self.RefreshNode(location_node, recursive=True) @@ -1479,7 +1479,7 @@ def InsertMapset(self, name, location_node): def InsertLocation(self, name, grassdb_node): """Insert new location into model and refresh tree""" location_node = self._model.AppendNode( - parent=grassdb_node, data=dict(type="location", name=name) + parent=grassdb_node, data={"type": "location", "name": name} ) # reload new location since it has a mapset self._reloadLocationNode(location_node) @@ -1495,7 +1495,7 @@ def InsertGrassDb(self, name): grassdb_node = self._model.SearchNodes(name=name, type="grassdb") if not grassdb_node: grassdb_node = self._model.AppendNode( - parent=self._model.root, data=dict(type="grassdb", name=name) + parent=self._model.root, data={"type": "grassdb", "name": name} ) if self._useLazyLoading(): self._lazyReloadGrassDBNode(grassdb_node) @@ -1571,13 +1571,13 @@ def OnDeleteMapset(self, event): ) ) changes.append( - dict( - grassdb=self.selected_grassdb[i].data["name"], - location=self.selected_location[i].data["name"], - mapset=self.selected_mapset[i].data["name"], - action="delete", - element="mapset", - ) + { + "grassdb": self.selected_grassdb[i].data["name"], + "location": self.selected_location[i].data["name"], + "mapset": self.selected_mapset[i].data["name"], + "action": "delete", + "element": "mapset", + } ) if delete_mapsets_interactively(self, mapsets): for change in changes: @@ -1598,12 +1598,12 @@ def OnDeleteLocation(self, event): ) ) changes.append( - dict( - grassdb=self.selected_grassdb[i].data["name"], - location=self.selected_location[i].data["name"], - action="delete", - element="location", - ) + { + "grassdb": self.selected_grassdb[i].data["name"], + "location": self.selected_location[i].data["name"], + "action": "delete", + "element": "location", + } ) if delete_locations_interactively(self, locations): for change in changes: diff --git a/gui/wxpython/dbmgr/base.py b/gui/wxpython/dbmgr/base.py index 3db3bf40918..399298921bf 100644 --- a/gui/wxpython/dbmgr/base.py +++ b/gui/wxpython/dbmgr/base.py @@ -224,24 +224,27 @@ def LoadData(self, layer, columns=None, where=None, sql=None): outFile = tempfile.NamedTemporaryFile(mode="w+b") - cmdParams = dict(quiet=True, parent=self, flags="c", separator=fs) + cmdParams = {"quiet": True, "parent": self, "flags": "c", "separator": fs} if sql: - cmdParams.update(dict(sql=sql, output=outFile.name, overwrite=True)) + cmdParams.update({"sql": sql, "output": outFile.name, "overwrite": True}) ret = RunCommand("db.select", **cmdParams) self.sqlFilter = {"sql": sql} else: cmdParams.update( - dict(map=self.mapDBInfo.map, layer=layer, where=where, stdout=outFile) + { + "map": self.mapDBInfo.map, + "layer": layer, + "where": where, + "stdout": outFile, + } ) self.sqlFilter = {"where": where} if columns: # Enclose column name with SQL standard double quotes - cmdParams.update( - dict(columns=",".join([f'"{col}"' for col in columns])) - ) + cmdParams.update({"columns": ",".join([f'"{col}"' for col in columns])}) ret = RunCommand("v.db.select", **cmdParams) diff --git a/gui/wxpython/gui_core/prompt.py b/gui/wxpython/gui_core/prompt.py index fa5364bbdf0..1faab429da3 100644 --- a/gui/wxpython/gui_core/prompt.py +++ b/gui/wxpython/gui_core/prompt.py @@ -361,7 +361,7 @@ def EntityToComplete(self): """Determines which part of command (flags, parameters) should be completed at current cursor position""" entry = self.GetTextLeft() - toComplete = dict(cmd=None, entity=None) + toComplete = {"cmd": None, "entity": None} try: cmd = entry.split()[0].strip() except IndexError: diff --git a/gui/wxpython/history/tree.py b/gui/wxpython/history/tree.py index 6df28af94dd..db8764e1e91 100644 --- a/gui/wxpython/history/tree.py +++ b/gui/wxpython/history/tree.py @@ -256,17 +256,17 @@ def _initHistoryModel(self): # Prepare it for entries without command info day = self._model.AppendNode( parent=self._model.root, - data=dict(type=TIME_PERIOD, day=self._timestampToDay()), + data={"type": TIME_PERIOD, "day": self._timestampToDay()}, ) else: day = self._model.AppendNode( parent=self._model.root, - data=dict( - type=TIME_PERIOD, - day=self._timestampToDay( + data={ + "type": TIME_PERIOD, + "day": self._timestampToDay( entry["command_info"]["timestamp"] ), - ), + }, ) # Determine status and create command node @@ -280,12 +280,12 @@ def _initHistoryModel(self): # Add command to time period node self._model.AppendNode( parent=day, - data=dict( - type=COMMAND, - name=entry["command"].strip(), - timestamp=timestamp if timestamp else None, - status=status, - ), + data={ + "type": COMMAND, + "name": entry["command"].strip(), + "timestamp": timestamp if timestamp else None, + "status": status, + }, ) # Refresh the tree view @@ -387,10 +387,10 @@ def InsertCommand(self, entry): if not today_nodes: today_node = self._model.AppendNode( parent=self._model.root, - data=dict( - type=TIME_PERIOD, - day=today, - ), + data={ + "type": TIME_PERIOD, + "day": today, + }, ) else: today_node = today_nodes[0] @@ -398,12 +398,12 @@ def InsertCommand(self, entry): # Create the command node under today time period node command_node = self._model.AppendNode( parent=today_node, - data=dict( - type=COMMAND, - name=entry["command"].strip(), - timestamp=entry["command_info"]["timestamp"], - status=entry["command_info"].get("status", Status.UNKNOWN.value), - ), + data={ + "type": COMMAND, + "name": entry["command"].strip(), + "timestamp": entry["command_info"]["timestamp"], + "status": entry["command_info"].get("status", Status.UNKNOWN.value), + }, ) # Refresh the tree diff --git a/gui/wxpython/lmgr/pyshell.py b/gui/wxpython/lmgr/pyshell.py index 06fe51729df..5bb1cb2d0dd 100644 --- a/gui/wxpython/lmgr/pyshell.py +++ b/gui/wxpython/lmgr/pyshell.py @@ -54,12 +54,12 @@ def __init__( + "\n\n" ) - shellargs = dict( - parent=self, - id=wx.ID_ANY, - introText=self.intro, - locals={"gs": grass, "AddLayer": self.AddLayer, "help": self.Help}, - ) + shellargs = { + "parent": self, + "id": wx.ID_ANY, + "introText": self.intro, + "locals": {"gs": grass, "AddLayer": self.AddLayer, "help": self.Help}, + } # useStockId (available since wxPython 4.0.2) should be False on macOS if sys.platform == "darwin" and CheckWxVersion([4, 0, 2]): shellargs["useStockId"] = False diff --git a/gui/wxpython/mapdisp/frame.py b/gui/wxpython/mapdisp/frame.py index 78e0b26157c..c85b2756328 100644 --- a/gui/wxpython/mapdisp/frame.py +++ b/gui/wxpython/mapdisp/frame.py @@ -161,7 +161,7 @@ def __init__( self.Map.GetRenderMgr().renderingFailed.connect( lambda cmd, error: self._giface.WriteError( _("Failed to run command '%(command)s'. Details:\n%(error)s") - % dict(command=" ".join(cmd), error=error) + % {"command": " ".join(cmd), "error": error} ) ) diff --git a/gui/wxpython/modules/mcalc_builder.py b/gui/wxpython/modules/mcalc_builder.py index 47c317fb937..3c2b2526834 100644 --- a/gui/wxpython/modules/mcalc_builder.py +++ b/gui/wxpython/modules/mcalc_builder.py @@ -687,7 +687,7 @@ def OnMCalcRun(self, event): overwrite = True else: overwrite = False - params = dict(expression="%s=%s" % (name, expr), overwrite=overwrite) + params = {"expression": "%s=%s" % (name, expr), "overwrite": overwrite} if seed_flag: params["flags"] = "s" if seed: diff --git a/gui/wxpython/nviz/tools.py b/gui/wxpython/nviz/tools.py index 407e6bb0470..ad6cf9fd99c 100644 --- a/gui/wxpython/nviz/tools.py +++ b/gui/wxpython/nviz/tools.py @@ -3251,14 +3251,14 @@ def _createControl( style = wx.SL_VERTICAL | wx.SL_AUTOTICKS | wx.SL_INVERSE sizeW = (-1, size) - kwargs = dict( - parent=parent, - id=wx.ID_ANY, - minValue=range[0], - maxValue=range[1], - style=style, - size=sizeW, - ) + kwargs = { + "parent": parent, + "id": wx.ID_ANY, + "minValue": range[0], + "maxValue": range[1], + "style": style, + "size": sizeW, + } if floatSlider: slider = FloatSlider(**kwargs) else: diff --git a/gui/wxpython/psmap/frame.py b/gui/wxpython/psmap/frame.py index ef6bc17f59b..ad5ede86739 100644 --- a/gui/wxpython/psmap/frame.py +++ b/gui/wxpython/psmap/frame.py @@ -1098,7 +1098,7 @@ def getInitMap(self): id = NewId() initMap = InitMap(id, env=self.env) self.instruction.AddInstruction(initMap) - self.instruction[id].SetInstruction(dict(rect=mapInitRect, scale=scale)) + self.instruction[id].SetInstruction({"rect": mapInitRect, "scale": scale}) def OnDelete(self, event): if self.canvas.dragId != -1 and self.currentPage == 0: @@ -1925,17 +1925,17 @@ def OnButtonDClick(self, event): } itemArg = { - "text": dict(event=None, id=self.dragId), - "mapinfo": dict(event=None), - "scalebar": dict(event=None), - "image": dict(event=None, id=self.dragId), - "northArrow": dict(event=None, id=self.dragId), - "point": dict(id=self.dragId), - "line": dict(id=self.dragId), - "rectangle": dict(id=self.dragId), - "rasterLegend": dict(event=None), - "vectorLegend": dict(event=None, page=1), - "map": dict(event=None, notebook=True), + "text": {"event": None, "id": self.dragId}, + "mapinfo": {"event": None}, + "scalebar": {"event": None}, + "image": {"event": None, "id": self.dragId}, + "northArrow": {"event": None, "id": self.dragId}, + "point": {"id": self.dragId}, + "line": {"id": self.dragId}, + "rectangle": {"id": self.dragId}, + "rasterLegend": {"event": None}, + "vectorLegend": {"event": None, "page": 1}, + "map": {"event": None, "notebook": True}, } type = self.instruction[self.dragId].type diff --git a/gui/wxpython/psmap/instructions.py b/gui/wxpython/psmap/instructions.py index c527fe65df0..121c0458d69 100644 --- a/gui/wxpython/psmap/instructions.py +++ b/gui/wxpython/psmap/instructions.py @@ -405,45 +405,45 @@ def Read(self, filename): return True def SendToRead(self, instruction, text, **kwargs): - psmapInstrDict = dict( - paper=["page"], - maploc=["map"], - scale=["map"], - border=["map"], - raster=["raster"], - mapinfo=["mapinfo"], - scalebar=["scalebar"], - text=["text"], - eps=["image", "northArrow"], - point=["point"], - line=["line"], - rectangle=["rectangle"], - vpoints=["vector", "vProperties"], - vlines=["vector", "vProperties"], - vareas=["vector", "vProperties"], - colortable=["rasterLegend"], - vlegend=["vectorLegend"], - labels=["labels"], - ) - - myInstrDict = dict( - page=PageSetup, - map=MapFrame, - raster=Raster, - mapinfo=Mapinfo, - scalebar=Scalebar, - text=Text, - image=Image, - northArrow=NorthArrow, - point=Point, - line=Line, - rectangle=Rectangle, - rasterLegend=RasterLegend, - vectorLegend=VectorLegend, - vector=Vector, - vProperties=VProperties, - labels=Labels, - ) + psmapInstrDict = { + "paper": ["page"], + "maploc": ["map"], + "scale": ["map"], + "border": ["map"], + "raster": ["raster"], + "mapinfo": ["mapinfo"], + "scalebar": ["scalebar"], + "text": ["text"], + "eps": ["image", "northArrow"], + "point": ["point"], + "line": ["line"], + "rectangle": ["rectangle"], + "vpoints": ["vector", "vProperties"], + "vlines": ["vector", "vProperties"], + "vareas": ["vector", "vProperties"], + "colortable": ["rasterLegend"], + "vlegend": ["vectorLegend"], + "labels": ["labels"], + } + + myInstrDict = { + "page": PageSetup, + "map": MapFrame, + "raster": Raster, + "mapinfo": Mapinfo, + "scalebar": Scalebar, + "text": Text, + "image": Image, + "northArrow": NorthArrow, + "point": Point, + "line": Line, + "rectangle": Rectangle, + "rasterLegend": RasterLegend, + "vectorLegend": VectorLegend, + "vector": Vector, + "vProperties": VProperties, + "labels": Labels, + } myInstruction = psmapInstrDict[instruction] @@ -593,7 +593,7 @@ def __init__(self, id, env): self.type = "initMap" # default values - self.defaultInstruction = dict(rect=None, scale=None) + self.defaultInstruction = {"rect": None, "scale": None} # current values self.instruction = dict(self.defaultInstruction) @@ -605,20 +605,20 @@ def __init__(self, id, env): InstructionObject.__init__(self, id=id, env=env) self.type = "map" # default values - self.defaultInstruction = dict( - map=None, - mapType=None, - drawMap=True, - region=None, - rect=Rect2D(), - scaleType=0, - scale=None, - center=None, - resolution=300, - border="y", - width=1, - color="0:0:0", - ) + self.defaultInstruction = { + "map": None, + "mapType": None, + "drawMap": True, + "region": None, + "rect": Rect2D(), + "scaleType": 0, + "scale": None, + "center": None, + "resolution": 300, + "border": "y", + "width": 1, + "color": "0:0:0", + } # current values self.instruction = dict(self.defaultInstruction) @@ -784,17 +784,17 @@ def __init__(self, id, env): InstructionObject.__init__(self, id=id, env=env) self.type = "page" # default values - self.defaultInstruction = dict( - Units="inch", - Format="a4", - Orientation="Portrait", - Width=8.268, - Height=11.693, - Left=0.5, - Right=0.5, - Top=1, - Bottom=1, - ) + self.defaultInstruction = { + "Units": "inch", + "Format": "a4", + "Orientation": "Portrait", + "Width": 8.268, + "Height": 11.693, + "Left": 0.5, + "Right": 0.5, + "Top": 1, + "Bottom": 1, + } # current values self.instruction = dict(self.defaultInstruction) @@ -879,16 +879,16 @@ def __init__(self, id, env): InstructionObject.__init__(self, id=id, env=env) self.type = "mapinfo" # default values - self.defaultInstruction = dict( - unit="inch", - where=(0, 0), - font="Helvetica", - fontsize=10, - color="0:0:0", - background="none", - border="none", - rect=None, - ) + self.defaultInstruction = { + "unit": "inch", + "where": (0, 0), + "font": "Helvetica", + "fontsize": 10, + "color": "0:0:0", + "background": "none", + "border": "none", + "rect": None, + } # current values self.instruction = dict(self.defaultInstruction) @@ -953,26 +953,26 @@ def __init__(self, id, env): InstructionObject.__init__(self, id=id, env=env) self.type = "text" # default values - self.defaultInstruction = dict( - text="", - font="Helvetica", - fontsize=10, - color="black", - background="none", - hcolor="none", - hwidth=1, - border="none", - width="1", - XY=True, - where=(0, 0), - unit="inch", - rotate=None, - ref="center center", - xoffset=0, - yoffset=0, - east=None, - north=None, - ) + self.defaultInstruction = { + "text": "", + "font": "Helvetica", + "fontsize": 10, + "color": "black", + "background": "none", + "hcolor": "none", + "hwidth": 1, + "border": "none", + "width": "1", + "XY": True, + "where": (0, 0), + "unit": "inch", + "rotate": None, + "ref": "center center", + "xoffset": 0, + "yoffset": 0, + "east": None, + "north": None, + } # current values self.instruction = dict(self.defaultInstruction) @@ -1076,16 +1076,16 @@ def __init__(self, id, settings, env): self.settings = settings self.type = "image" # default values - self.defaultInstruction = dict( - epsfile="", - XY=True, - where=(0, 0), - unit="inch", - east=None, - north=None, - rotate=None, - scale=1, - ) + self.defaultInstruction = { + "epsfile": "", + "XY": True, + "where": (0, 0), + "unit": "inch", + "east": None, + "north": None, + "rotate": None, + "scale": 1, + } # current values self.instruction = dict(self.defaultInstruction) @@ -1261,18 +1261,18 @@ def __init__(self, id, env): InstructionObject.__init__(self, id=id, env=env) self.type = "point" # default values - self.defaultInstruction = dict( - symbol=os.path.join("basic", "x"), - color="0:0:0", - fcolor="200:200:200", - rotate=0, - size=10, - XY=True, - where=(0, 0), - unit="inch", - east=None, - north=None, - ) + self.defaultInstruction = { + "symbol": os.path.join("basic", "x"), + "color": "0:0:0", + "fcolor": "200:200:200", + "rotate": 0, + "size": 10, + "XY": True, + "where": (0, 0), + "unit": "inch", + "east": None, + "north": None, + } # current values self.instruction = dict(self.defaultInstruction) @@ -1346,15 +1346,15 @@ def __init__(self, id, env): InstructionObject.__init__(self, id=id, env=env) self.type = "line" # default values - self.defaultInstruction = dict( - color="0:0:0", - width=2, - where=[wx.Point2D(), wx.Point2D()], - east1=None, - north1=None, - east2=None, - north2=None, - ) + self.defaultInstruction = { + "color": "0:0:0", + "width": 2, + "where": [wx.Point2D(), wx.Point2D()], + "east1": None, + "north1": None, + "east2": None, + "north2": None, + } # current values self.instruction = dict(self.defaultInstruction) @@ -1421,15 +1421,15 @@ def __init__(self, id, env): InstructionObject.__init__(self, id=id, env=env) self.type = "rectangle" # default values - self.defaultInstruction = dict( - color="0:0:0", - fcolor="none", - width=2, - east1=None, - north1=None, - east2=None, - north2=None, - ) + self.defaultInstruction = { + "color": "0:0:0", + "fcolor": "none", + "width": 2, + "east1": None, + "north1": None, + "east2": None, + "north2": None, + } # current values self.instruction = dict(self.defaultInstruction) @@ -1498,20 +1498,20 @@ def __init__(self, id, env): InstructionObject.__init__(self, id=id, env=env) self.type = "scalebar" # default values - self.defaultInstruction = dict( - unit="inch", - where=(1, 1), - unitsLength="auto", - unitsHeight="inch", - length=None, - height=0.1, - rect=None, - fontsize=10, - background="y", - scalebar="f", - segment=4, - numbers=1, - ) + self.defaultInstruction = { + "unit": "inch", + "where": (1, 1), + "unitsLength": "auto", + "unitsHeight": "inch", + "length": None, + "height": 0.1, + "rect": None, + "fontsize": 10, + "background": "y", + "scalebar": "f", + "segment": 4, + "numbers": 1, + } # current values self.instruction = dict(self.defaultInstruction) @@ -1614,28 +1614,26 @@ def __init__(self, id, env): InstructionObject.__init__(self, id=id, env=env) self.type = "rasterLegend" # default values - self.defaultInstruction = dict( - rLegend=False, - unit="inch", - rasterDefault=True, - raster=None, - discrete=None, - type=None, - where=(0, 0), - width=None, - height=None, - cols=1, - font="Helvetica", - fontsize=10, - # color = '0:0:0', tickbar = False, - # range = False, min = 0, max = 0, - color="black", - tickbar="n", - range=False, - min=0, - max=0, - nodata="n", - ) + self.defaultInstruction = { + "rLegend": False, + "unit": "inch", + "rasterDefault": True, + "raster": None, + "discrete": None, + "type": None, + "where": (0, 0), + "width": None, + "height": None, + "cols": 1, + "font": "Helvetica", + "fontsize": 10, + "color": "black", + "tickbar": "n", + "range": False, + "min": 0, + "max": 0, + "nodata": "n", + } # current values self.instruction = dict(self.defaultInstruction) @@ -1815,18 +1813,18 @@ def __init__(self, id, env): InstructionObject.__init__(self, id=id, env=env) self.type = "vectorLegend" # default values - self.defaultInstruction = dict( - vLegend=False, - unit="inch", - where=(0, 0), - defaultSize=True, - width=0.4, - cols=1, - span=None, - font="Helvetica", - fontsize=10, - border="none", - ) + self.defaultInstruction = { + "vLegend": False, + "unit": "inch", + "where": (0, 0), + "defaultSize": True, + "width": 0.4, + "cols": 1, + "span": None, + "font": "Helvetica", + "fontsize": 10, + "border": "none", + } # current values self.instruction = dict(self.defaultInstruction) @@ -1911,7 +1909,7 @@ def __init__(self, id, env): InstructionObject.__init__(self, id=id, env=env) self.type = "raster" # default values - self.defaultInstruction = dict(isRaster=False, raster=None) + self.defaultInstruction = {"isRaster": False, "raster": None} # current values self.instruction = dict(self.defaultInstruction) @@ -1946,7 +1944,7 @@ def __init__(self, id, env): InstructionObject.__init__(self, id=id, env=env) self.type = "vector" # default values - self.defaultInstruction = dict(list=None) # [vmap, type, id, lpos, label] + self.defaultInstruction = {"list": None} # [vmap, type, id, lpos, label] # current values self.instruction = dict(self.defaultInstruction) @@ -2002,64 +2000,64 @@ def __init__(self, id, subType, env): self.subType = subType # default values if self.subType == "points": - dd = dict( - subType="points", - name=None, - type="point or centroid", - connection=False, - layer="1", - masked="n", - color="0:0:0", - width=1, - fcolor="255:0:0", - rgbcolumn=None, - symbol=os.path.join("basic", "x"), - eps=None, - size=5, - sizecolumn=None, - scale=None, - rotation=False, - rotate=0, - rotatecolumn=None, - label=None, - lpos=None, - ) + dd = { + "subType": "points", + "name": None, + "type": "point or centroid", + "connection": False, + "layer": "1", + "masked": "n", + "color": "0:0:0", + "width": 1, + "fcolor": "255:0:0", + "rgbcolumn": None, + "symbol": os.path.join("basic", "x"), + "eps": None, + "size": 5, + "sizecolumn": None, + "scale": None, + "rotation": False, + "rotate": 0, + "rotatecolumn": None, + "label": None, + "lpos": None, + } elif self.subType == "lines": - dd = dict( - subType="lines", - name=None, - type="line or boundary", - connection=False, - layer="1", - masked="n", - color="0:0:0", - hwidth=1, - hcolor="none", - rgbcolumn=None, - width=1, - cwidth=None, - style="solid", - linecap="butt", - label=None, - lpos=None, - ) + dd = { + "subType": "lines", + "name": None, + "type": "line or boundary", + "connection": False, + "layer": "1", + "masked": "n", + "color": "0:0:0", + "hwidth": 1, + "hcolor": "none", + "rgbcolumn": None, + "width": 1, + "cwidth": None, + "style": "solid", + "linecap": "butt", + "label": None, + "lpos": None, + } else: # areas - dd = dict( - subType="areas", - name=None, - connection=False, - layer="1", - masked="n", - color="0:0:0", - width=1, - fcolor="none", - rgbcolumn=None, - pat=None, - pwidth=1, - scale=1, - label=None, - lpos=None, - ) + dd = { + "subType": "areas", + "name": None, + "connection": False, + "layer": "1", + "masked": "n", + "color": "0:0:0", + "width": 1, + "fcolor": "none", + "rgbcolumn": None, + "pat": None, + "pwidth": 1, + "scale": 1, + "label": None, + "lpos": None, + } self.defaultInstruction = dd # current values self.instruction = dict(self.defaultInstruction) @@ -2262,7 +2260,7 @@ def __init__(self, id, env): InstructionObject.__init__(self, id=id, env=env) self.type = "labels" # default values - self.defaultInstruction = dict(labels=[]) + self.defaultInstruction = {"labels": []} # current values self.instruction = dict(self.defaultInstruction) diff --git a/gui/wxpython/timeline/frame.py b/gui/wxpython/timeline/frame.py index 632d30f233f..584e05a33b0 100644 --- a/gui/wxpython/timeline/frame.py +++ b/gui/wxpython/timeline/frame.py @@ -709,8 +709,8 @@ def annotate(self, ax): xytext=self.offsets, textcoords="offset points", va="bottom", - bbox=dict(boxstyle="round,pad=0.5", fc="yellow", alpha=0.7), - arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=0"), + bbox={"boxstyle": "round,pad=0.5", "fc": "yellow", "alpha": 0.7}, + arrowprops={"arrowstyle": "->", "connectionstyle": "arc3,rad=0"}, annotation_clip=False, multialignment="left", ) diff --git a/gui/wxpython/tplot/frame.py b/gui/wxpython/tplot/frame.py index bfb2feca0c5..8844918e706 100755 --- a/gui/wxpython/tplot/frame.py +++ b/gui/wxpython/tplot/frame.py @@ -1444,8 +1444,8 @@ def annotate(self, ax): xytext=self.offsets, va="bottom", textcoords="offset points", - bbox=dict(boxstyle="round,pad=0.5", fc="yellow", alpha=0.7), - arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=0"), + bbox={"boxstyle": "round,pad=0.5", "fc": "yellow", "alpha": 0.7}, + arrowprops={"arrowstyle": "->", "connectionstyle": "arc3,rad=0"}, annotation_clip=False, multialignment="left", ) diff --git a/python/grass/benchmark/results.py b/python/grass/benchmark/results.py index 9f5a2309a19..c89d8f1c80b 100644 --- a/python/grass/benchmark/results.py +++ b/python/grass/benchmark/results.py @@ -38,7 +38,7 @@ def save_results(data): Returns JSON as str. """ if not hasattr(data, "results"): - data = dict(results=data) + data = {"results": data} return json.dumps(data, cls=ResultsEncoder) diff --git a/python/grass/gunittest/case.py b/python/grass/gunittest/case.py index 3f67f729d14..0a1d8032cdf 100644 --- a/python/grass/gunittest/case.py +++ b/python/grass/gunittest/case.py @@ -485,7 +485,7 @@ def assertVectorFitsUnivar( flag and few other, use `assertModuleKeyValue` for the full interface of arbitrary module. """ - parameters = dict(map=map, column=column, flags="g") + parameters = {"map": map, "column": column, "flags": "g"} if layer: parameters.update(layer=layer) if type: @@ -903,7 +903,7 @@ def assertRastersNoDifference( """ if statistics is None or sorted(statistics.keys()) == ["max", "min"]: if statistics is None: - statistics = dict(min=-precision, max=precision) + statistics = {"min": -precision, "max": precision} diff = self._compute_difference_raster( reference, actual, "assertRastersNoDifference" ) @@ -970,7 +970,7 @@ def assertRasters3dNoDifference( """ if statistics is None or sorted(statistics.keys()) == ["max", "min"]: if statistics is None: - statistics = dict(min=-precision, max=precision) + statistics = {"min": -precision, "max": precision} diff = self._compute_difference_raster3d( reference, actual, "assertRasters3dNoDifference" ) diff --git a/python/grass/gunittest/invoker.py b/python/grass/gunittest/invoker.py index 681fad5ff14..e58127502c7 100644 --- a/python/grass/gunittest/invoker.py +++ b/python/grass/gunittest/invoker.py @@ -305,7 +305,7 @@ def run_in_location(self, gisdbase, location, location_type, results_dir, exclud main_page_name="testfiles.html", ), GrassTestFilesKeyValueReporter( - info=dict(location=location, location_type=location_type) + info={"location": location, "location_type": location_type} ), ] ) diff --git a/python/grass/script/vector.py b/python/grass/script/vector.py index 131a872b1d4..543e9654815 100644 --- a/python/grass/script/vector.py +++ b/python/grass/script/vector.py @@ -409,14 +409,14 @@ def vector_what( flags += "a" if multiple: flags += "m" - cmdParams = dict( - quiet=True, - flags=flags, - map=",".join(map_list), - layer=",".join(layer_list), - coordinates=",".join(coord_list), - distance=float(distance), - ) + cmdParams = { + "quiet": True, + "flags": flags, + "map": ",".join(map_list), + "layer": ",".join(layer_list), + "coordinates": ",".join(coord_list), + "distance": float(distance), + } if ttype: cmdParams["type"] = ",".join(ttype) diff --git a/python/libgrass_interface_generator/ctypesgen/libraryloader.py b/python/libgrass_interface_generator/ctypesgen/libraryloader.py index f54a036b3c0..52fefc343cc 100644 --- a/python/libgrass_interface_generator/ctypesgen/libraryloader.py +++ b/python/libgrass_interface_generator/ctypesgen/libraryloader.py @@ -67,7 +67,7 @@ class Lookup: def __init__(self, path): super(LibraryLoader.Lookup, self).__init__() - self.access = dict(cdecl=ctypes.CDLL(path, self.mode)) + self.access = {"cdecl": ctypes.CDLL(path, self.mode)} def get(self, name, calling_convention="cdecl"): """Return the given name according to the selected calling convention""" diff --git a/scripts/g.manual/g.manual.py b/scripts/g.manual/g.manual.py index 582dd2e7a7c..11cbc9a0338 100755 --- a/scripts/g.manual/g.manual.py +++ b/scripts/g.manual/g.manual.py @@ -94,7 +94,7 @@ def start_browser(entry): grass.verbose( _("Starting browser '%(browser)s' for manual" " entry '%(entry)s'...") - % dict(browser=browser_name, entry=entry) + % {"browser": browser_name, "entry": entry} ) try: @@ -102,7 +102,7 @@ def start_browser(entry): except Exception: grass.fatal( _("Error starting browser '%(browser)s' for HTML file" " '%(path)s'") - % dict(browser=browser, path=path) + % {"browser": browser, "path": path} ) diff --git a/scripts/r.import/r.import.py b/scripts/r.import/r.import.py index fad648b1cde..195b7c79c31 100644 --- a/scripts/r.import/r.import.py +++ b/scripts/r.import/r.import.py @@ -192,12 +192,12 @@ def main(): if options["extent"] == "region": region_flag += "r" if flags["o"] or is_projection_matching(GDALdatasource): - parameters = dict( - input=GDALdatasource, - output=output, - memory=memory, - flags="ak" + additional_flags + region_flag, - ) + parameters = { + "input": GDALdatasource, + "output": output, + "memory": memory, + "flags": "ak" + additional_flags + region_flag, + } if bands: parameters["band"] = bands try: @@ -233,15 +233,15 @@ def main(): # creating a new location with r.in.gdal requires a sanitized env env = os.environ.copy() env = grass.sanitize_mapset_environment(env) - parameters = dict( - input=GDALdatasource, - output=output, - memory=memory, - flags="c", - title=title, - project=TMPLOC, - quiet=True, - ) + parameters = { + "input": GDALdatasource, + "output": output, + "memory": memory, + "flags": "c", + "title": title, + "project": TMPLOC, + "quiet": True, + } if bands: parameters["band"] = bands try: @@ -273,12 +273,12 @@ def main(): # import into temp location grass.verbose(_("Importing <%s> to temporary project...") % GDALdatasource) - parameters = dict( - input=GDALdatasource, - output=output, - memory=memory, - flags="ak" + additional_flags, - ) + parameters = { + "input": GDALdatasource, + "output": output, + "memory": memory, + "flags": "ak" + additional_flags, + } if bands: parameters["band"] = bands if "r" in region_flag: diff --git a/scripts/r.out.xyz/r.out.xyz.py b/scripts/r.out.xyz/r.out.xyz.py index 5b4193f6816..ae8e04de4d6 100755 --- a/scripts/r.out.xyz/r.out.xyz.py +++ b/scripts/r.out.xyz/r.out.xyz.py @@ -52,9 +52,11 @@ def main(): statsflags = "1g" else: statsflags = "1gn" - parameters = dict( - flags=statsflags, input=options["input"], separator=options["separator"] - ) + parameters = { + "flags": statsflags, + "input": options["input"], + "separator": options["separator"], + } if output: parameters.update(output=output) diff --git a/scripts/v.in.e00/v.in.e00.py b/scripts/v.in.e00/v.in.e00.py index 0c7133f1e6b..1c700247c55 100755 --- a/scripts/v.in.e00/v.in.e00.py +++ b/scripts/v.in.e00/v.in.e00.py @@ -141,8 +141,8 @@ def main(): # let's import... gcore.message(_("Importing %ss...") % type) - layer = dict(point="LAB", line="ARC", area=["LAB", "ARC"]) - itype = dict(point="point", line="line", area="centroid") + layer = {"point": "LAB", "line": "ARC", "area": ["LAB", "ARC"]} + itype = {"point": "point", "line": "line", "area": "centroid"} try: gcore.run_command( diff --git a/utils/g.html2man/ggroff.py b/utils/g.html2man/ggroff.py index 714ef717090..2e13bfadc5b 100644 --- a/utils/g.html2man/ggroff.py +++ b/utils/g.html2man/ggroff.py @@ -60,14 +60,14 @@ def clean(content): class Formatter: def __init__(self, filename, stream=sys.stdout): self.stream = stream - self.style = dict( - preformat=False, - in_ul=False, - no_nl=False, - in_table=False, - in_tr=False, - index=[], - ) + self.style = { + "preformat": False, + "in_ul": False, + "no_nl": False, + "in_table": False, + "in_tr": False, + "index": [], + } self.stack = [] self.strip_re = re.compile("^[ \t]+") self.filename = filename From 85b047f439be2b573e96104fb43f3141fa49bd69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sun, 30 Jun 2024 20:18:25 -0400 Subject: [PATCH 031/293] style: Fixes manual-from-import (PLR0402) (#3949) Concerns Pylint rule "consider-using-from-import / R0402" Using `ruff check --output-format=concise --select PLR0402 --fix`. --- gui/wxpython/gmodeler/model.py | 2 +- gui/wxpython/gui_core/gselect.py | 2 +- gui/wxpython/gui_core/preferences.py | 2 +- gui/wxpython/gui_core/toolbars.py | 2 +- gui/wxpython/iclass/plots.py | 2 +- gui/wxpython/iscatt/frame.py | 2 +- gui/wxpython/main_window/frame.py | 2 +- gui/wxpython/main_window/notebook.py | 2 +- gui/wxpython/mapwin/analysis.py | 2 +- gui/wxpython/mapwin/buffered.py | 2 +- gui/wxpython/wxplot/base.py | 2 +- gui/wxpython/wxplot/histogram.py | 2 +- gui/wxpython/wxplot/profile.py | 2 +- gui/wxpython/wxplot/scatter.py | 2 +- python/grass/experimental/tests/conftest.py | 2 +- .../tests/grass_script_mapset_session_test.py | 2 +- .../tests/grass_script_tmp_mapset_session_test.py | 2 +- python/grass/gunittest/reporters.py | 2 +- python/grass/imaging/operations.py | 2 +- python/grass/temporal/datetime_math.py | 2 +- python/grass/temporal/spatio_temporal_relationships.py | 6 +++--- python/grass/temporal/temporal_algebra.py | 4 ++-- python/grass/temporal/temporal_operator.py | 4 ++-- python/grass/temporal/temporal_raster3d_algebra.py | 2 +- python/grass/temporal/temporal_raster_algebra.py | 2 +- python/grass/temporal/temporal_vector_algebra.py | 2 +- python/grass/temporal/unit_tests.py | 8 ++++---- temporal/t.rast.algebra/t.rast.algebra.py | 4 ++-- temporal/t.rast3d.algebra/t.rast3d.algebra.py | 4 ++-- temporal/t.select/t.select.py | 4 ++-- temporal/t.vect.algebra/t.vect.algebra.py | 4 ++-- temporal/t.vect.what.strds/t.vect.what.strds.py | 2 +- 32 files changed, 43 insertions(+), 43 deletions(-) diff --git a/gui/wxpython/gmodeler/model.py b/gui/wxpython/gmodeler/model.py index 9d0f58ec7d4..fc4db81e1f3 100644 --- a/gui/wxpython/gmodeler/model.py +++ b/gui/wxpython/gmodeler/model.py @@ -39,7 +39,7 @@ import time import xml.etree.ElementTree as etree -import xml.sax.saxutils as saxutils +from xml.sax import saxutils import wx from abc import ABC, abstractmethod diff --git a/gui/wxpython/gui_core/gselect.py b/gui/wxpython/gui_core/gselect.py index de9a7d67636..ec64f51f8a3 100644 --- a/gui/wxpython/gui_core/gselect.py +++ b/gui/wxpython/gui_core/gselect.py @@ -50,7 +50,7 @@ import wx from core import globalvar -import wx.lib.buttons as buttons +from wx.lib import buttons import wx.lib.filebrowsebutton as filebrowse diff --git a/gui/wxpython/gui_core/preferences.py b/gui/wxpython/gui_core/preferences.py index 96b7d339664..98a9803e20b 100644 --- a/gui/wxpython/gui_core/preferences.py +++ b/gui/wxpython/gui_core/preferences.py @@ -36,7 +36,7 @@ havePwd = False import wx -import wx.lib.agw.aui as aui +from wx.lib.agw import aui import wx.lib.colourselect as csel import wx.lib.mixins.listctrl as listmix import wx.lib.scrolledpanel as SP diff --git a/gui/wxpython/gui_core/toolbars.py b/gui/wxpython/gui_core/toolbars.py index 9eb64957790..3293574a944 100644 --- a/gui/wxpython/gui_core/toolbars.py +++ b/gui/wxpython/gui_core/toolbars.py @@ -20,7 +20,7 @@ import os import wx -import wx.lib.agw.aui as aui +from wx.lib.agw import aui from core import globalvar from core.debug import Debug diff --git a/gui/wxpython/iclass/plots.py b/gui/wxpython/iclass/plots.py index ec75714473b..786fa920d17 100644 --- a/gui/wxpython/iclass/plots.py +++ b/gui/wxpython/iclass/plots.py @@ -17,7 +17,7 @@ import wx -import wx.lib.plot as plot +from wx.lib import plot import wx.lib.scrolledpanel as scrolled from core.gcmd import GError diff --git a/gui/wxpython/iscatt/frame.py b/gui/wxpython/iscatt/frame.py index 9398ce19349..bf5dfac91e4 100644 --- a/gui/wxpython/iscatt/frame.py +++ b/gui/wxpython/iscatt/frame.py @@ -39,7 +39,7 @@ try: from agw import aui except ImportError: - import wx.lib.agw.aui as aui + from wx.lib.agw import aui class IClassIScattPanel(wx.Panel, ManageBusyCursorMixin): diff --git a/gui/wxpython/main_window/frame.py b/gui/wxpython/main_window/frame.py index dc7ea5edccc..49a58068718 100644 --- a/gui/wxpython/main_window/frame.py +++ b/gui/wxpython/main_window/frame.py @@ -30,7 +30,7 @@ try: from agw import aui except ImportError: - import wx.lib.agw.aui as aui + from wx.lib.agw import aui import wx diff --git a/gui/wxpython/main_window/notebook.py b/gui/wxpython/main_window/notebook.py index 77b37ada1ea..7d57917e23a 100644 --- a/gui/wxpython/main_window/notebook.py +++ b/gui/wxpython/main_window/notebook.py @@ -19,7 +19,7 @@ import os import wx -import wx.lib.agw.aui as aui +from wx.lib.agw import aui from core import globalvar from gui_core.wrap import SimpleTabArt diff --git a/gui/wxpython/mapwin/analysis.py b/gui/wxpython/mapwin/analysis.py index f2cac82e2db..907817335f6 100644 --- a/gui/wxpython/mapwin/analysis.py +++ b/gui/wxpython/mapwin/analysis.py @@ -20,7 +20,7 @@ import math import wx -import core.units as units +from core import units from core.gcmd import RunCommand from core.giface import Notification diff --git a/gui/wxpython/mapwin/buffered.py b/gui/wxpython/mapwin/buffered.py index 92371f8c099..79efe012033 100644 --- a/gui/wxpython/mapwin/buffered.py +++ b/gui/wxpython/mapwin/buffered.py @@ -48,7 +48,7 @@ from core.debug import Debug from core.settings import UserSettings from mapwin.base import MapWindowBase -import core.utils as utils +from core import utils from mapwin.graphics import GraphicsSet from core.gthread import gThread diff --git a/gui/wxpython/wxplot/base.py b/gui/wxpython/wxplot/base.py index 3ae016db748..37e62b1b2c3 100755 --- a/gui/wxpython/wxplot/base.py +++ b/gui/wxpython/wxplot/base.py @@ -20,7 +20,7 @@ import wx from random import randint -import wx.lib.plot as plot +from wx.lib import plot from core.globalvar import ICONDIR from core.settings import UserSettings from wxplot.dialogs import TextDialog, OptDialog diff --git a/gui/wxpython/wxplot/histogram.py b/gui/wxpython/wxplot/histogram.py index b895ba19683..1c5c2b2de4f 100644 --- a/gui/wxpython/wxplot/histogram.py +++ b/gui/wxpython/wxplot/histogram.py @@ -20,7 +20,7 @@ import wx import grass.script as grass -import wx.lib.plot as plot +from wx.lib import plot from gui_core.wrap import StockCursor from gui_core.toolbars import BaseToolbar, BaseIcons from wxplot.base import BasePlotFrame, PlotIcons diff --git a/gui/wxpython/wxplot/profile.py b/gui/wxpython/wxplot/profile.py index 1b55da68919..b61200cfc5c 100644 --- a/gui/wxpython/wxplot/profile.py +++ b/gui/wxpython/wxplot/profile.py @@ -22,7 +22,7 @@ import wx -import wx.lib.plot as plot +from wx.lib import plot import grass.script as grass from wxplot.base import BasePlotFrame, PlotIcons from gui_core.toolbars import BaseToolbar, BaseIcons diff --git a/gui/wxpython/wxplot/scatter.py b/gui/wxpython/wxplot/scatter.py index 6022f7add4b..b0063150560 100644 --- a/gui/wxpython/wxplot/scatter.py +++ b/gui/wxpython/wxplot/scatter.py @@ -20,7 +20,7 @@ import wx import grass.script as grass -import wx.lib.plot as plot +from wx.lib import plot from wxplot.base import BasePlotFrame, PlotIcons from gui_core.toolbars import BaseToolbar, BaseIcons from gui_core.wrap import StockCursor diff --git a/python/grass/experimental/tests/conftest.py b/python/grass/experimental/tests/conftest.py index 69976ce0633..f19d9d39d9e 100644 --- a/python/grass/experimental/tests/conftest.py +++ b/python/grass/experimental/tests/conftest.py @@ -6,7 +6,7 @@ import pytest import grass.script as gs -import grass.experimental as experimental +from grass import experimental @pytest.fixture diff --git a/python/grass/experimental/tests/grass_script_mapset_session_test.py b/python/grass/experimental/tests/grass_script_mapset_session_test.py index b38c6f1dc57..3877e61576e 100644 --- a/python/grass/experimental/tests/grass_script_mapset_session_test.py +++ b/python/grass/experimental/tests/grass_script_mapset_session_test.py @@ -5,7 +5,7 @@ import pytest import grass.script as gs -import grass.experimental as experimental +from grass import experimental def test_simple_create(xy_session): diff --git a/python/grass/experimental/tests/grass_script_tmp_mapset_session_test.py b/python/grass/experimental/tests/grass_script_tmp_mapset_session_test.py index 60019ebc5b7..744becc1e7b 100644 --- a/python/grass/experimental/tests/grass_script_tmp_mapset_session_test.py +++ b/python/grass/experimental/tests/grass_script_tmp_mapset_session_test.py @@ -3,7 +3,7 @@ import os import grass.script as gs -import grass.experimental as experimental +from grass import experimental def test_with_context_manager(xy_session): diff --git a/python/grass/gunittest/reporters.py b/python/grass/gunittest/reporters.py index 252331840d2..794eb73193c 100644 --- a/python/grass/gunittest/reporters.py +++ b/python/grass/gunittest/reporters.py @@ -11,7 +11,7 @@ import os import datetime -import xml.sax.saxutils as saxutils +from xml.sax import saxutils import xml.etree.ElementTree as et import subprocess import collections diff --git a/python/grass/imaging/operations.py b/python/grass/imaging/operations.py index 8c60c4c599e..3bd0949670e 100644 --- a/python/grass/imaging/operations.py +++ b/python/grass/imaging/operations.py @@ -59,7 +59,7 @@ from PIL import Image try: - import PIL.ImageOps as ImageOps + from PIL import ImageOps except ImportError: ImageOps = None except ImportError: diff --git a/python/grass/temporal/datetime_math.py b/python/grass/temporal/datetime_math.py index 29bcc8907de..68d742a1d03 100644 --- a/python/grass/temporal/datetime_math.py +++ b/python/grass/temporal/datetime_math.py @@ -14,7 +14,7 @@ import copy try: - import dateutil.parser as parser + from dateutil import parser has_dateutil = True except: diff --git a/python/grass/temporal/spatio_temporal_relationships.py b/python/grass/temporal/spatio_temporal_relationships.py index ab876d45ede..5e06b368962 100644 --- a/python/grass/temporal/spatio_temporal_relationships.py +++ b/python/grass/temporal/spatio_temporal_relationships.py @@ -22,9 +22,9 @@ from .core import init_dbif from .abstract_dataset import AbstractDatasetComparisonKeyStartTime from .datetime_math import time_delta_to_relative_time_seconds -import grass.lib.vector as vector -import grass.lib.rtree as rtree -import grass.lib.gis as gis +from grass.lib import vector +from grass.lib import rtree +from grass.lib import gis ############################################################################### diff --git a/python/grass/temporal/temporal_algebra.py b/python/grass/temporal/temporal_algebra.py index db35f03f071..fa91a3a71b4 100644 --- a/python/grass/temporal/temporal_algebra.py +++ b/python/grass/temporal/temporal_algebra.py @@ -440,8 +440,8 @@ """ try: - import ply.lex as lex - import ply.yacc as yacc + from ply import lex + from ply import yacc except: pass diff --git a/python/grass/temporal/temporal_operator.py b/python/grass/temporal/temporal_operator.py index 0bf001ee0c5..59c2bafa4f5 100644 --- a/python/grass/temporal/temporal_operator.py +++ b/python/grass/temporal/temporal_operator.py @@ -141,8 +141,8 @@ """ # noqa: E501 try: - import ply.lex as lex - import ply.yacc as yacc + from ply import lex + from ply import yacc except ImportError: pass diff --git a/python/grass/temporal/temporal_raster3d_algebra.py b/python/grass/temporal/temporal_raster3d_algebra.py index 88d44768b6a..dc136bc7fcd 100644 --- a/python/grass/temporal/temporal_raster3d_algebra.py +++ b/python/grass/temporal/temporal_raster3d_algebra.py @@ -12,7 +12,7 @@ """ try: - import ply.yacc as yacc + from ply import yacc except ImportError: pass diff --git a/python/grass/temporal/temporal_raster_algebra.py b/python/grass/temporal/temporal_raster_algebra.py index 7811ec90416..dddbab87ed5 100644 --- a/python/grass/temporal/temporal_raster_algebra.py +++ b/python/grass/temporal/temporal_raster_algebra.py @@ -53,7 +53,7 @@ """ try: - import ply.yacc as yacc + from ply import yacc except ImportError: pass diff --git a/python/grass/temporal/temporal_vector_algebra.py b/python/grass/temporal/temporal_vector_algebra.py index 7b3f838d461..c42a3bac885 100644 --- a/python/grass/temporal/temporal_vector_algebra.py +++ b/python/grass/temporal/temporal_vector_algebra.py @@ -43,7 +43,7 @@ """ try: - import ply.yacc as yacc + from ply import yacc except ImportError: pass diff --git a/python/grass/temporal/unit_tests.py b/python/grass/temporal/unit_tests.py index 8083680ef3f..0a4360ae1a3 100644 --- a/python/grass/temporal/unit_tests.py +++ b/python/grass/temporal/unit_tests.py @@ -11,7 +11,7 @@ import copy from datetime import datetime -import grass.script.core as core +from grass.script import core from .abstract_dataset import ( AbstractDatasetComparisonKeyStartTime, AbstractDatasetComparisonKeyEndTime, @@ -26,9 +26,9 @@ compute_absolute_time_granularity, ) -import grass.lib.vector as vector -import grass.lib.rtree as rtree -import grass.lib.gis as gis +from grass.lib import vector +from grass.lib import rtree +from grass.lib import gis from ctypes import byref # Uncomment this to detect the error diff --git a/temporal/t.rast.algebra/t.rast.algebra.py b/temporal/t.rast.algebra/t.rast.algebra.py index 0bb81453f0a..e58c006bef9 100644 --- a/temporal/t.rast.algebra/t.rast.algebra.py +++ b/temporal/t.rast.algebra/t.rast.algebra.py @@ -103,8 +103,8 @@ def main(): # Check for PLY istallation try: # Intentionally unused imports - import ply.lex as lex # noqa: F401 - import ply.yacc as yacc # noqa: F401 + from ply import lex # noqa: F401 + from ply import yacc # noqa: F401 except ImportError: grass.script.fatal( _( diff --git a/temporal/t.rast3d.algebra/t.rast3d.algebra.py b/temporal/t.rast3d.algebra/t.rast3d.algebra.py index 2d80e90a9db..227fa18261d 100644 --- a/temporal/t.rast3d.algebra/t.rast3d.algebra.py +++ b/temporal/t.rast3d.algebra/t.rast3d.algebra.py @@ -89,8 +89,8 @@ def main(): # Check for PLY istallation try: # Intentionally unused imports - import ply.lex as lex # noqa: F401 - import ply.yacc as yacc # noqa: F401 + from ply import lex # noqa: F401 + from ply import yacc # noqa: F401 except ImportError: grass.script.fatal( _( diff --git a/temporal/t.select/t.select.py b/temporal/t.select/t.select.py index bf7d0f0e53a..ab2341d277c 100644 --- a/temporal/t.select/t.select.py +++ b/temporal/t.select/t.select.py @@ -70,8 +70,8 @@ def main(): # Check for PLY istallation try: # Intentionally unused imports - import ply.lex as lex # noqa: F401 - import ply.yacc as yacc # noqa: F401 + from ply import lex # noqa: F401 + from ply import yacc # noqa: F401 except ImportError: grass.fatal( _( diff --git a/temporal/t.vect.algebra/t.vect.algebra.py b/temporal/t.vect.algebra/t.vect.algebra.py index b68ef19f9a1..7b38f9219b7 100644 --- a/temporal/t.vect.algebra/t.vect.algebra.py +++ b/temporal/t.vect.algebra/t.vect.algebra.py @@ -68,8 +68,8 @@ def main(): # Check for PLY istallation try: # Intentionally unused imports - import ply.lex as lex # noqa: F401 - import ply.yacc as yacc # noqa: F401 + from ply import lex # noqa: F401 + from ply import yacc # noqa: F401 except ImportError: grass.script.fatal( _( diff --git a/temporal/t.vect.what.strds/t.vect.what.strds.py b/temporal/t.vect.what.strds/t.vect.what.strds.py index b7c923615b2..40bc2fd7177 100755 --- a/temporal/t.vect.what.strds/t.vect.what.strds.py +++ b/temporal/t.vect.what.strds/t.vect.what.strds.py @@ -66,7 +66,7 @@ import os import grass.script as grass -import grass.script.raster as raster +from grass.script import raster from grass.exceptions import CalledModuleError ############################################################################ From 837b7967f5d98d5043b469862b6083620ae04070 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sun, 30 Jun 2024 20:21:51 -0400 Subject: [PATCH 032/293] style: Fixes unnecessary-collection-call (C408) for testsuite (#3947) * style: Fixes unnecessary-collection-call (C408) for testsuite Only applies fixes to non-empty collections by `ruff check --select "C408" --unsafe-fixes --output-format=concise --fix --config 'include = ["*/testsuite/**.py"]'` in order to limit the review scope. * style: Apply black formatting Separated in another commit for easier review of only the original changes without formatting --- .../benchmark/testsuite/test_benchmark.py | 40 ++-- .../gunittest/testsuite/test_assertions.py | 14 +- .../testsuite/test_assertions_rast3d.py | 68 +++---- .../testsuite/test_assertions_vect.py | 48 ++--- .../modules/interface/testsuite/test_flag.py | 6 +- .../interface/testsuite/test_parameter.py | 174 +++++++++++------- python/grass/script/testsuite/test_utils.py | 2 +- raster/r.mapcalc/testsuite/test_r_mapcalc.py | 46 ++--- .../testsuite/test_row_above_below_bug.py | 2 +- .../testsuite/test_random_cells.py | 30 +-- raster/r.random/testsuite/test_r_random.py | 10 +- raster/r.random/testsuite/testrandom.py | 4 +- raster/r.sun/testsuite/test_rsun.py | 4 +- .../testsuite/test_r_surf_gauss.py | 6 +- .../r.surf.random/testsuite/test_min_max.py | 4 +- raster/r.to.vect/testsuite/test_r_to_vect.py | 8 +- .../r3.to.rast/testsuite/test_a_b_coeff.py | 4 +- .../testsuite/test_integer_rounding.py | 4 +- raster3d/r3.to.rast/testsuite/test_nulls.py | 4 +- .../r3.to.rast/testsuite/test_small_data.py | 4 +- scripts/r.import/testsuite/test_r_import.py | 28 +-- scripts/v.clip/testsuite/test_v_clip.py | 12 +- scripts/v.import/testsuite/test_v_import.py | 100 +++++----- .../v.in.lidar/testsuite/decimation_test.py | 16 +- vector/v.in.lidar/testsuite/mask_test.py | 6 +- .../testsuite/test_v_in_lidar_filter.py | 14 +- .../testsuite/test_v_in_pdal_filter.py | 14 +- vector/v.info/testsuite/test_vinfo.py | 100 +++++----- vector/v.net/testsuite/test_v_net.py | 8 +- vector/v.random/testsuite/test_v_random.py | 4 +- vector/v.select/testsuite/test_v_select.py | 10 +- vector/v.surf.rst/testsuite/test_vsurfrst.py | 4 +- vector/v.to.3d/testsuite/test_vto3d.py | 4 +- 33 files changed, 426 insertions(+), 376 deletions(-) diff --git a/python/grass/benchmark/testsuite/test_benchmark.py b/python/grass/benchmark/testsuite/test_benchmark.py index eaeb073c658..4eaf766859a 100644 --- a/python/grass/benchmark/testsuite/test_benchmark.py +++ b/python/grass/benchmark/testsuite/test_benchmark.py @@ -38,16 +38,18 @@ class TestBenchmarksRun(TestCase): def test_resolutions(self): """Test that resolution tests runs without nprocs and plots to file""" benchmarks = [ - dict( - module=Module("r.univar", map="elevation", stdout_=DEVNULL, run_=False), - label="Standard output", - ), - dict( - module=Module( + { + "module": Module( + "r.univar", map="elevation", stdout_=DEVNULL, run_=False + ), + "label": "Standard output", + }, + { + "module": Module( "r.univar", map="elevation", flags="g", stdout_=DEVNULL, run_=False ), - label="Standard output", - ), + "label": "Standard output", + }, ] resolutions = [300, 200, 100] results = [] @@ -67,10 +69,12 @@ def test_single(self): label = "Standard output" repeat = 4 benchmarks = [ - dict( - module=Module("r.univar", map="elevation", stdout_=DEVNULL, run_=False), - label=label, - ) + { + "module": Module( + "r.univar", map="elevation", stdout_=DEVNULL, run_=False + ), + "label": label, + } ] results = [] for benchmark in benchmarks: @@ -88,11 +92,13 @@ def test_nprocs(self): label = "Standard output" repeat = 4 benchmarks = [ - dict( - module=Module("r.univar", map="elevation", stdout_=DEVNULL, run_=False), - label=label, - max_nprocs=4, - ) + { + "module": Module( + "r.univar", map="elevation", stdout_=DEVNULL, run_=False + ), + "label": label, + "max_nprocs": 4, + } ] results = [] for benchmark in benchmarks: diff --git a/python/grass/gunittest/testsuite/test_assertions.py b/python/grass/gunittest/testsuite/test_assertions.py index bf0f946d37d..51720a56b76 100644 --- a/python/grass/gunittest/testsuite/test_assertions.py +++ b/python/grass/gunittest/testsuite/test_assertions.py @@ -132,14 +132,14 @@ def test_pygrass_module(self): """Test syntax with Module and required parameters as module""" module = Module("r.info", map="elevation", flags="gr", run_=False, finish_=True) self.assertModuleKeyValue( - module, reference=dict(min=55.58, max=156.33), precision=0.01, sep="=" + module, reference={"min": 55.58, "max": 156.33}, precision=0.01, sep="=" ) def test_pygrass_simple_module(self): """Test syntax with SimpleModule as module""" module = SimpleModule("r.info", map="elevation", flags="gr") self.assertModuleKeyValue( - module, reference=dict(min=55.58, max=156.33), precision=0.01, sep="=" + module, reference={"min": 55.58, "max": 156.33}, precision=0.01, sep="=" ) def test_direct_parameters(self): @@ -148,7 +148,7 @@ def test_direct_parameters(self): "r.info", map="elevation", flags="gr", - reference=dict(min=55.58, max=156.33), + reference={"min": 55.58, "max": 156.33}, precision=0.01, sep="=", ) @@ -157,8 +157,8 @@ def test_parameters_parameter(self): """Test syntax with module parameters in one parameters dictionary""" self.assertModuleKeyValue( module="r.info", - parameters=dict(map="elevation", flags="gr"), - reference=dict(min=55.58, max=156.33), + parameters={"map": "elevation", "flags": "gr"}, + reference={"min": 55.58, "max": 156.33}, precision=0.01, sep="=", ) @@ -237,7 +237,7 @@ def test_assertRastersNoDifference_mean(self): actual="elevation", reference="elevation", precision=0, # this might need to be increased - statistics=dict(mean=0), + statistics={"mean": 0}, msg="The difference of same maps should have small mean", ) self.assertRaises( @@ -246,7 +246,7 @@ def test_assertRastersNoDifference_mean(self): actual="elevation", reference="geology", precision=1, - statistics=dict(mean=0), + statistics={"mean": 0}, msg="The difference of different maps should have huge mean", ) diff --git a/python/grass/gunittest/testsuite/test_assertions_rast3d.py b/python/grass/gunittest/testsuite/test_assertions_rast3d.py index 686afb74807..ca71e5a5837 100644 --- a/python/grass/gunittest/testsuite/test_assertions_rast3d.py +++ b/python/grass/gunittest/testsuite/test_assertions_rast3d.py @@ -35,20 +35,20 @@ def tearDownClass(cls): ) def test_assertRaster3dFitsUnivar(self): - reference = dict( - n=1000000, - null_cells=0, - cells=1000000, - min=155, - max=155, - range=0, - mean=155, - mean_of_abs=155, - stddev=0, - variance=0, - coeff_var=0, - sum=155000000, - ) + reference = { + "n": 1000000, + "null_cells": 0, + "cells": 1000000, + "min": 155, + "max": 155, + "range": 0, + "mean": 155, + "mean_of_abs": 155, + "stddev": 0, + "variance": 0, + "coeff_var": 0, + "sum": 155000000, + } self.assertRaster3dFitsUnivar( self.constant_map, reference=reference, precision=0.000001 ) @@ -63,30 +63,30 @@ def test_assertRaster3dFitsUnivar(self): ValueError, self.assertRaster3dFitsUnivar, self.constant_map, - reference=dict(a=4, b=5, c=6), + reference={"a": 4, "b": 5, "c": 6}, ) self.assertRaises( CalledModuleError, self.assertRaster3dFitsUnivar, "does_not_exists", - reference=dict(a=4, b=5, c=6), + reference={"a": 4, "b": 5, "c": 6}, ) def test_assertRaster3dFitsInfo(self): - reference = dict( - north=200, - south=100, - east=400, - west=200, - bottom=450, - top=500, - nsres=1, - ewres=1, - tbres=1, - rows=100, - cols=200, - depths=50, - ) + reference = { + "north": 200, + "south": 100, + "east": 400, + "west": 200, + "bottom": 450, + "top": 500, + "nsres": 1, + "ewres": 1, + "tbres": 1, + "rows": 100, + "cols": 200, + "depths": 50, + } self.assertRaster3dFitsInfo(self.constant_map, reference=reference) reference["north"] = 500 @@ -100,11 +100,11 @@ def test_assertRaster3dFitsInfo(self): ValueError, self.assertRaster3dFitsInfo, self.constant_map, - reference=dict(a=5), + reference={"a": 5}, ) def test_common_values_info_univar(self): - minmax = dict(min=3, max=350) + minmax = {"min": 3, "max": 350} self.assertRaster3dFitsUnivar(self.rcd_increasing_map, minmax, precision=0.01) self.assertRaster3dFitsInfo(self.rcd_increasing_map, minmax, precision=0.01) @@ -138,7 +138,7 @@ def test_assertRasters3dNoDifference_mean(self): actual=self.rcd_increasing_map, reference=self.rcd_increasing_map, precision=0, # this might need to be increased - statistics=dict(mean=0), + statistics={"mean": 0}, msg="The difference of same maps should have small mean", ) self.assertRaises( @@ -147,7 +147,7 @@ def test_assertRasters3dNoDifference_mean(self): actual=self.constant_map, reference=self.rcd_increasing_map, precision=1, - statistics=dict(mean=0), + statistics={"mean": 0}, msg="The difference of different maps should have huge mean", ) diff --git a/python/grass/gunittest/testsuite/test_assertions_vect.py b/python/grass/gunittest/testsuite/test_assertions_vect.py index 4f888ad8156..8b3288ad2ca 100644 --- a/python/grass/gunittest/testsuite/test_assertions_vect.py +++ b/python/grass/gunittest/testsuite/test_assertions_vect.py @@ -24,34 +24,34 @@ """ # v.info schools -t -V_UNIVAR_SCHOOLS_TOPO = dict( - nodes=0, - points=167, - lines=0, - boundaries=0, - centroids=0, - areas=0, - islands=0, - primitives=167, - map3d=0, -) +V_UNIVAR_SCHOOLS_TOPO = { + "nodes": 0, + "points": 167, + "lines": 0, + "boundaries": 0, + "centroids": 0, + "areas": 0, + "islands": 0, + "primitives": 167, + "map3d": 0, +} # v.info schools -g and rounded -V_UNIVAR_SCHOOLS_REGION = dict( - north=248160, - south=203560, - east=671715, - west=619215, - top=0, - bottom=0, -) +V_UNIVAR_SCHOOLS_REGION = { + "north": 248160, + "south": 203560, + "east": 671715, + "west": 619215, + "top": 0, + "bottom": 0, +} # v.info schools -g and reduced to minimum -V_UNIVAR_SCHOOLS_EXTENDED = dict( - name="schools", - level=2, - num_dblinks=1, -) +V_UNIVAR_SCHOOLS_EXTENDED = { + "name": "schools", + "level": 2, + "num_dblinks": 1, +} class TestVectorInfoAssertions(TestCase): diff --git a/python/grass/pygrass/modules/interface/testsuite/test_flag.py b/python/grass/pygrass/modules/interface/testsuite/test_flag.py index 909c6d00d55..f10a94061ab 100644 --- a/python/grass/pygrass/modules/interface/testsuite/test_flag.py +++ b/python/grass/pygrass/modules/interface/testsuite/test_flag.py @@ -13,7 +13,7 @@ class TestFlag(TestCase): def test_get_bash(self): """Test get_bash method""" - flag = Flag(diz=dict(name="a")) + flag = Flag(diz={"name": "a"}) self.assertFalse(flag.value) self.assertEqual("", flag.get_bash()) flag.special = True @@ -25,7 +25,7 @@ def test_get_bash(self): def test_get_python(self): """Test get_python method""" - flag = Flag(diz=dict(name="a")) + flag = Flag(diz={"name": "a"}) self.assertFalse(flag.value) self.assertEqual("", flag.get_python()) flag.special = True @@ -37,7 +37,7 @@ def test_get_python(self): def test_bool(self): """Test magic __bool__ method""" - flag = Flag(diz=dict(name="a")) + flag = Flag(diz={"name": "a"}) flag.value = True self.assertTrue(True if flag else False) flag.value = False diff --git a/python/grass/pygrass/modules/interface/testsuite/test_parameter.py b/python/grass/pygrass/modules/interface/testsuite/test_parameter.py index 57f17cb3027..82b609f0fe2 100644 --- a/python/grass/pygrass/modules/interface/testsuite/test_parameter.py +++ b/python/grass/pygrass/modules/interface/testsuite/test_parameter.py @@ -22,7 +22,12 @@ class TestCheckValueFunction(TestCase): def test_single_all(self): param = Parameter( - diz=dict(name="int_number", required="yes", multiple="no", type="all") + diz={ + "name": "int_number", + "required": "yes", + "multiple": "no", + "type": "all", + } ) value = 1 self.assertTupleEqual((value, value), _check_value(param, value)) @@ -38,7 +43,12 @@ def test_single_all(self): def test_single_float_double(self): for ptype in ("float", "double"): param = Parameter( - diz=dict(name="int_number", required="yes", multiple="no", type=ptype) + diz={ + "name": "int_number", + "required": "yes", + "multiple": "no", + "type": ptype, + } ) value = 1 self.assertTupleEqual((float(value), value), _check_value(param, value)) @@ -58,7 +68,12 @@ def test_single_float_double(self): def test_multiple_float_double(self): for ptype in ("float", "double"): param = Parameter( - diz=dict(name="number", required="yes", multiple="yes", type=ptype) + diz={ + "name": "number", + "required": "yes", + "multiple": "yes", + "type": ptype, + } ) value = (1.4, 2.3) self.assertTupleEqual((list(value), value), _check_value(param, value)) @@ -104,15 +119,15 @@ def test_multiple_float_double(self): def test_range_float_double(self): for ptype in ("float", "double"): param = Parameter( - diz=dict( - name="int_number", - required="yes", - multiple="no", - type=ptype, - values=[ + diz={ + "name": "int_number", + "required": "yes", + "multiple": "no", + "type": ptype, + "values": [ "0.0-2.5", ], - ) + } ) value = 1 self.assertTupleEqual((float(value), value), _check_value(param, value)) @@ -141,15 +156,15 @@ def test_positive_min_float_double(self): name = "number" for ptype in ("float", "double"): param = Parameter( - diz=dict( - name=name, - required="yes", - multiple="no", - type=ptype, - values=[ + diz={ + "name": name, + "required": "yes", + "multiple": "no", + "type": ptype, + "values": [ "2-", ], - ) + } ) value = 2 self.assertTupleEqual((float(value), value), _check_value(param, value)) @@ -180,15 +195,15 @@ def test_positive_max_float_double(self): name = "number" for ptype in ("float", "double"): param = Parameter( - diz=dict( - name=name, - required="yes", - multiple="no", - type=ptype, - values=[ + diz={ + "name": name, + "required": "yes", + "multiple": "no", + "type": ptype, + "values": [ "-100", ], - ) + } ) value = 1 self.assertTupleEqual((float(value), value), _check_value(param, value)) @@ -213,7 +228,12 @@ def test_positive_max_float_double(self): def test_single_integer(self): param = Parameter( - diz=dict(name="int_number", required="yes", multiple="no", type="integer") + diz={ + "name": "int_number", + "required": "yes", + "multiple": "no", + "type": "integer", + } ) value = 1 self.assertTupleEqual((value, value), _check_value(param, value)) @@ -232,7 +252,12 @@ def test_single_integer(self): def test_multiple_integer(self): param = Parameter( - diz=dict(name="int_number", required="yes", multiple="yes", type="integer") + diz={ + "name": "int_number", + "required": "yes", + "multiple": "yes", + "type": "integer", + } ) value = (1, 2) # import ipdb; ipdb.set_trace() @@ -285,13 +310,13 @@ def test_multiple_integer(self): def test_keydescvalues(self): for ptype in ("integer", "float"): param = Parameter( - diz=dict( - name="int_number", - required="yes", - multiple="yes", - keydesc=("range", "(min, max)"), - type="integer", - ) + diz={ + "name": "int_number", + "required": "yes", + "multiple": "yes", + "keydesc": ("range", "(min, max)"), + "type": "integer", + } ) value = (1, 2) self.assertTupleEqual( @@ -311,15 +336,15 @@ def test_keydescvalues(self): def test_range_integer(self): param = Parameter( - diz=dict( - name="int_number", - required="yes", - multiple="no", - type="integer", - values=[ + diz={ + "name": "int_number", + "required": "yes", + "multiple": "no", + "type": "integer", + "values": [ "0-10", ], - ) + } ) value = 1 self.assertTupleEqual((value, value), _check_value(param, value)) @@ -346,13 +371,13 @@ def test_range_integer(self): def test_choice_integer(self): param = Parameter( - diz=dict( - name="int_number", - required="yes", - multiple="no", - type="integer", - values=[2, 4, 6, 8], - ) + diz={ + "name": "int_number", + "required": "yes", + "multiple": "no", + "type": "integer", + "values": [2, 4, 6, 8], + } ) value = 4 self.assertTupleEqual((value, value), _check_value(param, value)) @@ -374,7 +399,7 @@ def test_choice_integer(self): def test_single_string_file(self): for ptype in ("string", "file"): param = Parameter( - diz=dict(name="name", required="yes", multiple="no", type=ptype) + diz={"name": "name", "required": "yes", "multiple": "no", "type": ptype} ) value = "elev" self.assertTupleEqual((value, value), _check_value(param, value)) @@ -389,7 +414,12 @@ def test_single_string_file(self): def test_multiple_strings(self): param = Parameter( - diz=dict(name="rastnames", required="yes", multiple="yes", type="string") + diz={ + "name": "rastnames", + "required": "yes", + "multiple": "yes", + "type": "string", + } ) value = ["elev", "slope", "aspect"] self.assertTupleEqual((value, value), _check_value(param, value)) @@ -423,13 +453,13 @@ def test_multiple_strings(self): def test_choice_string(self): values = ["elev", "asp", "slp"] param = Parameter( - diz=dict( - name="rastname", - required="yes", - multiple="no", - type="string", - values=values, - ) + diz={ + "name": "rastname", + "required": "yes", + "multiple": "no", + "type": "string", + "values": values, + } ) value = "asp" self.assertTupleEqual((value, value), _check_value(param, value)) @@ -449,7 +479,12 @@ class TestParameterGetBash(TestCase): def test_single_float_double(self): for ptype in ("float", "double"): param = Parameter( - diz=dict(name="number", required="yes", multiple="no", type=ptype) + diz={ + "name": "number", + "required": "yes", + "multiple": "no", + "type": ptype, + } ) # set private attributes to skip the check function param._value = 1.0 @@ -462,7 +497,12 @@ def test_single_float_double(self): def test_multiple_float_double(self): for ptype in ("float", "double"): param = Parameter( - diz=dict(name="number", required="yes", multiple="yes", type=ptype) + diz={ + "name": "number", + "required": "yes", + "multiple": "yes", + "type": ptype, + } ) # set private attributes to skip the check function param._value = [ @@ -484,7 +524,7 @@ def test_multiple_float_double(self): def test_single_string(self): param = Parameter( - diz=dict(name="rast", required="yes", multiple="no", type="string") + diz={"name": "rast", "required": "yes", "multiple": "no", "type": "string"} ) # set private attributes to skip the check function param._value = "elev" @@ -493,7 +533,7 @@ def test_single_string(self): def test_multiple_strings(self): param = Parameter( - diz=dict(name="rast", required="yes", multiple="yes", type="string") + diz={"name": "rast", "required": "yes", "multiple": "yes", "type": "string"} ) # set private attributes to skip the check function param._value = ["elev", "asp", "slp"] @@ -507,13 +547,13 @@ def test_multiple_strings(self): def test_keydescvalues(self): param = Parameter( - diz=dict( - name="range", - required="yes", - multiple="yes", - keydesc=("range", "(min, max)"), - type="integer", - ) + diz={ + "name": "range", + "required": "yes", + "multiple": "yes", + "keydesc": ("range", "(min, max)"), + "type": "integer", + } ) # set private attributes to skip the check function param._value = [ diff --git a/python/grass/script/testsuite/test_utils.py b/python/grass/script/testsuite/test_utils.py index f55f1ca86d0..0dce79eb001 100644 --- a/python/grass/script/testsuite/test_utils.py +++ b/python/grass/script/testsuite/test_utils.py @@ -27,7 +27,7 @@ def tearDown(self): class LcAllC(EnvironChange): - env = dict(LC_ALL="C") + env = {"LC_ALL": "C"} class TestEncode(TestCase): diff --git a/raster/r.mapcalc/testsuite/test_r_mapcalc.py b/raster/r.mapcalc/testsuite/test_r_mapcalc.py index 9ed7332f267..377e1af7f6a 100644 --- a/raster/r.mapcalc/testsuite/test_r_mapcalc.py +++ b/raster/r.mapcalc/testsuite/test_r_mapcalc.py @@ -165,7 +165,7 @@ def test_auto_seed(self): self.assertRastersDifference( "rand_auto_1", "rand_auto_2", - statistics=dict(min=-1, max=1, mean=0), + statistics={"min": -1, "max": 1, "mean": 0}, precision=0.5, ) # low precision, we have few cells @@ -279,17 +279,17 @@ def test_union(self): "r.info", map="test_region_4", flags="gr", - reference=dict( - min=6, - max=6, - cells=625, - north=30, - south=5, - west=5, - east=30, - nsres=1, - ewres=1, - ), + reference={ + "min": 6, + "max": 6, + "cells": 625, + "north": 30, + "south": 5, + "west": 5, + "east": 30, + "nsres": 1, + "ewres": 1, + }, precision=0.01, sep="=", ) @@ -308,17 +308,17 @@ def test_intersect(self): "r.info", map="test_region_5", flags="gr", - reference=dict( - min=6, - max=6, - cells=25, - north=20, - south=15, - west=15, - east=20, - nsres=1, - ewres=1, - ), + reference={ + "min": 6, + "max": 6, + "cells": 25, + "north": 20, + "south": 15, + "west": 15, + "east": 20, + "nsres": 1, + "ewres": 1, + }, precision=0.01, sep="=", ) diff --git a/raster/r.mapcalc/testsuite/test_row_above_below_bug.py b/raster/r.mapcalc/testsuite/test_row_above_below_bug.py index 7d944e361cb..58a5fa566b9 100644 --- a/raster/r.mapcalc/testsuite/test_row_above_below_bug.py +++ b/raster/r.mapcalc/testsuite/test_row_above_below_bug.py @@ -89,7 +89,7 @@ def r_mapcalc_with_test(self, expression): self.assertModule("r.mapcalc", expression=expression, overwrite=True) self.assertRasterExists(self.output) self.to_remove.append(self.output) - ref_univar = dict(null_cells=6, cells=9) + ref_univar = {"null_cells": 6, "cells": 9} self.assertRasterFitsUnivar( raster=self.output, reference=ref_univar, precision=0 ) diff --git a/raster/r.random.cells/testsuite/test_random_cells.py b/raster/r.random.cells/testsuite/test_random_cells.py index 0d4ca4379e5..963add103de 100644 --- a/raster/r.random.cells/testsuite/test_random_cells.py +++ b/raster/r.random.cells/testsuite/test_random_cells.py @@ -49,13 +49,13 @@ def test_fill_all(self): self.to_remove.append(self.all_rast) self.assertRasterFitsUnivar( self.all_rast, - reference=dict( - cells=self.n_cells, - n=self.n_cells, - null_cells=0, - min=1, - max=self.n_cells, - ), + reference={ + "cells": self.n_cells, + "n": self.n_cells, + "null_cells": 0, + "min": 1, + "max": self.n_cells, + }, ) def test_fill_some(self): @@ -64,7 +64,7 @@ def test_fill_some(self): ) self.to_remove.append(self.some_rast) self.assertRasterFitsUnivar( - self.some_rast, reference=dict(cells=self.n_cells, min=1) + self.some_rast, reference={"cells": self.n_cells, "min": 1} ) # it is hard to say how much but it will be less than half self.assertRasterMinMax(self.some_rast, 1, self.n_cells / 2) @@ -77,13 +77,13 @@ def test_fill_count(self): self.to_remove.append(self.count_rast) self.assertRasterFitsUnivar( self.count_rast, - reference=dict( - cells=self.n_cells, - n=count, - null_cells=self.n_cells - count, - min=1, - max=count, - ), + reference={ + "cells": self.n_cells, + "n": count, + "null_cells": self.n_cells - count, + "min": 1, + "max": count, + }, ) # it is hard to say how much but it will be less than half self.assertRasterMinMax(self.count_rast, 1, count) diff --git a/raster/r.random/testsuite/test_r_random.py b/raster/r.random/testsuite/test_r_random.py index b662e4cc48a..ce12c719c8e 100644 --- a/raster/r.random/testsuite/test_r_random.py +++ b/raster/r.random/testsuite/test_r_random.py @@ -81,7 +81,7 @@ def test_random_vector(self): self.assertVectorExists( self.vector, msg="landcover_1m_vector_random was not created" ) - topology = dict(points=20, primitives=20) + topology = {"points": 20, "primitives": 20} self.assertVectorFitsTopoInfo(vector=self.vector, reference=topology) def test_random_raster_flag_z(self): @@ -113,7 +113,7 @@ def test_vector_random_flag_z(self): self.assertVectorExists( self.vector + "_null", msg="landcover_1m_vector_random_null was not created" ) - topology = dict(points=20, primitives=20) + topology = {"points": 20, "primitives": 20} self.assertVectorFitsTopoInfo(vector=self.vector + "_null", reference=topology) def test_random_raster_flag_b(self): @@ -147,7 +147,7 @@ def test_vector_random_flag_b(self): self.vector + "_without_topology", msg="landcover_1m_vector_random_without_topology was not created", ) - topology = dict(points=20, primitives=20) + topology = {"points": 20, "primitives": 20} self.assertVectorFitsTopoInfo( vector=self.vector + "_without_topology", reference=topology ) @@ -181,7 +181,7 @@ def test_vector_random_flag_z(self): self.assertVectorExists( self.vector + "_3D", msg="landcover_1m_vector_random_3D was not created" ) - topology = dict(points=20, primitives=20) + topology = {"points": 20, "primitives": 20} self.assertVectorFitsTopoInfo(vector=self.vector + "_3D", reference=topology) def test_random_raster_cover(self): @@ -215,7 +215,7 @@ def test_random_vector_cover(self): self.vector + "_cover_landcover_1m", msg="landcover_1m_vector_cover_landcover_1m was not created", ) - topology = dict(points=20, primitives=20) + topology = {"points": 20, "primitives": 20} self.assertVectorFitsTopoInfo( vector=self.vector + "_cover_landcover_1m", reference=topology ) diff --git a/raster/r.random/testsuite/testrandom.py b/raster/r.random/testsuite/testrandom.py index e2bc2e413a3..ed86e5f4266 100644 --- a/raster/r.random/testsuite/testrandom.py +++ b/raster/r.random/testsuite/testrandom.py @@ -73,7 +73,7 @@ def test_flag_z(self): seed=1, ) self.assertModule("v.info", map=self.vector, flags="t") - topology = dict(points=100, lines=0, areas=0, map3d=1) + topology = {"points": 100, "lines": 0, "areas": 0, "map3d": 1} self.assertVectorFitsTopoInfo(self.vector, topology) def test_flag_b(self): @@ -88,7 +88,7 @@ def test_flag_b(self): seed=1, ) self.assertModule("v.info", map=self.vector, flags="t") - topology = dict(points=36011, lines=0, areas=0) + topology = {"points": 36011, "lines": 0, "areas": 0} self.assertVectorFitsTopoInfo(self.vector, topology) diff --git a/raster/r.sun/testsuite/test_rsun.py b/raster/r.sun/testsuite/test_rsun.py index fb95e92c551..e75d1d9aead 100644 --- a/raster/r.sun/testsuite/test_rsun.py +++ b/raster/r.sun/testsuite/test_rsun.py @@ -83,7 +83,7 @@ def test_more_threads(self): "r.info", map=self.incidout, flags="e", - reference=dict(comments=self.metadata.format(nprocs=4)), + reference={"comments": self.metadata.format(nprocs=4)}, precision=0, sep="=", ) @@ -96,7 +96,7 @@ def test_run_outputs(self): "r.info", map=self.incidout, flags="e", - reference=dict(comments=self.metadata.format(nprocs=1)), + reference={"comments": self.metadata.format(nprocs=1)}, precision=0, sep="=", ) diff --git a/raster/r.surf.gauss/testsuite/test_r_surf_gauss.py b/raster/r.surf.gauss/testsuite/test_r_surf_gauss.py index deb29e086e9..6b043a4bd24 100644 --- a/raster/r.surf.gauss/testsuite/test_r_surf_gauss.py +++ b/raster/r.surf.gauss/testsuite/test_r_surf_gauss.py @@ -47,7 +47,7 @@ def test_defaut_settings(self): self.assertModule("r.surf.gauss", output=self.output) self.assertRasterFitsUnivar( self.output, - reference=dict(mean=-0.044860, stddev=1.019485), + reference={"mean": -0.044860, "stddev": 1.019485}, precision=1e-6, ) @@ -64,7 +64,7 @@ def test_mean_sigma_params(self): self.assertRasterExists(self.output, msg="Output was not created") self.assertRasterFitsUnivar( self.output, - reference=dict(mean=2.739812, stddev=5.913014), + reference={"mean": 2.739812, "stddev": 5.913014}, precision=1e-6, ) @@ -82,7 +82,7 @@ def test_random_seed_option(self): self.assertRasterExists(self.output, msg="Output was not created") self.assertRasterFitsUnivar( self.output, - reference=dict(mean=3.183532, stddev=6.050756), + reference={"mean": 3.183532, "stddev": 6.050756}, precision=1e-6, ) diff --git a/raster/r.surf.random/testsuite/test_min_max.py b/raster/r.surf.random/testsuite/test_min_max.py index 063f7965771..55fedc406c9 100644 --- a/raster/r.surf.random/testsuite/test_min_max.py +++ b/raster/r.surf.random/testsuite/test_min_max.py @@ -65,7 +65,7 @@ def test_min_max_double(self): ) self.assertRasterFitsInfo( raster=self.output, - reference=dict(min=-3.20423, max=5.68621), + reference={"min": -3.20423, "max": 5.68621}, precision=precision, msg="Output min and max too far from parameters", ) @@ -94,7 +94,7 @@ def test_min_max_int(self): ) self.assertRasterFitsInfo( raster=self.output, - reference=dict(min=min_value, max=max_value), + reference={"min": min_value, "max": max_value}, precision=precision, msg="Output min and max too far from parameters", ) 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 7ba284d0261..fbd27af6a33 100644 --- a/raster/r.to.vect/testsuite/test_r_to_vect.py +++ b/raster/r.to.vect/testsuite/test_r_to_vect.py @@ -40,7 +40,7 @@ def test_flags(self): type=self.point, flags="s", ) - topology = dict(points=36011, lines=0, areas=0) + topology = {"points": 36011, "lines": 0, "areas": 0} self.assertVectorFitsTopoInfo(self.output, topology) def test_flagz(self): @@ -52,7 +52,7 @@ def test_flagz(self): type=self.point, flags="z", ) - topology = dict(points=36011, lines=0, areas=0) + topology = {"points": 36011, "lines": 0, "areas": 0} self.assertVectorFitsTopoInfo(self.output, topology) def test_flagb(self): @@ -60,7 +60,7 @@ def test_flagb(self): self.assertModule( "r.to.vect", input=self.input, output=self.output, type=self.area, flags="b" ) - topology = dict(points=0, lines=0, areas=0) + topology = {"points": 0, "lines": 0, "areas": 0} self.assertVectorFitsTopoInfo(self.output, topology) def test_flagt(self): @@ -68,7 +68,7 @@ def test_flagt(self): self.assertModule( "r.to.vect", input=self.input, output=self.output, type=self.area, flags="t" ) - topology = dict(points=0, lines=0, areas=33) + topology = {"points": 0, "lines": 0, "areas": 33} self.assertVectorFitsTopoInfo(self.output, topology) diff --git a/raster3d/r3.to.rast/testsuite/test_a_b_coeff.py b/raster3d/r3.to.rast/testsuite/test_a_b_coeff.py index 04fb217c78e..94742f33755 100644 --- a/raster3d/r3.to.rast/testsuite/test_a_b_coeff.py +++ b/raster3d/r3.to.rast/testsuite/test_a_b_coeff.py @@ -150,8 +150,8 @@ def test_a_b_coeff(self): self.assertEqual( len(rasts), 4, msg="Wrong number of 2D rasters present" " in the mapset" ) - ref_info = dict(cells=9) - ref_univar = dict(cells=9, null_cells=0) + ref_info = {"cells": 9} + ref_univar = {"cells": 9, "null_cells": 0} for rast in rasts: self.assertRasterExists(rast) # the following doesn't make much sense because we just listed them diff --git a/raster3d/r3.to.rast/testsuite/test_integer_rounding.py b/raster3d/r3.to.rast/testsuite/test_integer_rounding.py index 0f053cf923e..b6d388a0253 100644 --- a/raster3d/r3.to.rast/testsuite/test_integer_rounding.py +++ b/raster3d/r3.to.rast/testsuite/test_integer_rounding.py @@ -152,8 +152,8 @@ def test_rounding(self): self.assertEqual( len(rasts), 4, msg="Wrong number of 2D rasters present" " in the mapset" ) - ref_info = dict(cells=9) - ref_univar = dict(cells=9, null_cells=0) + ref_info = {"cells": 9} + ref_univar = {"cells": 9, "null_cells": 0} for rast in rasts: self.assertRasterExists(rast) # the following doesn't make much sense because we just listed them diff --git a/raster3d/r3.to.rast/testsuite/test_nulls.py b/raster3d/r3.to.rast/testsuite/test_nulls.py index 16131380172..1bb026e069c 100644 --- a/raster3d/r3.to.rast/testsuite/test_nulls.py +++ b/raster3d/r3.to.rast/testsuite/test_nulls.py @@ -151,9 +151,9 @@ def test_b(self): self.assertEqual( len(rasts), 4, msg="Wrong number of 2D rasters present" " in the mapset" ) - ref_info = dict(cells=9) + ref_info = {"cells": 9} # only this tests the presence of nulls - ref_univar = dict(cells=9, null_cells=2) + ref_univar = {"cells": 9, "null_cells": 2} for rast in rasts: self.assertRasterExists(rast) # the following doesn't make much sense because we just listed them diff --git a/raster3d/r3.to.rast/testsuite/test_small_data.py b/raster3d/r3.to.rast/testsuite/test_small_data.py index 8d9056969e5..ecc918d4c9b 100644 --- a/raster3d/r3.to.rast/testsuite/test_small_data.py +++ b/raster3d/r3.to.rast/testsuite/test_small_data.py @@ -148,8 +148,8 @@ def test_a(self): self.assertEqual( len(rasts), 4, msg="Wrong number of 2D rasters present" " in the mapset" ) - ref_info = dict(cells=9) - ref_univar = dict(cells=9, null_cells=0) + ref_info = {"cells": 9} + ref_univar = {"cells": 9, "null_cells": 0} for rast in rasts: self.assertRasterExists(rast) # the following doesn't make much sense because we just listed them diff --git a/scripts/r.import/testsuite/test_r_import.py b/scripts/r.import/testsuite/test_r_import.py index c7839a3f1d4..0e4ec3839e5 100755 --- a/scripts/r.import/testsuite/test_r_import.py +++ b/scripts/r.import/testsuite/test_r_import.py @@ -36,15 +36,15 @@ def test_import_same_proj_tif(self): output=self.imported, resample="bilinear", ) - reference = dict( - north=223490, - south=223390, - east=636820, - west=636710, - nsres=10, - ewres=10, - datatype="FCELL", - ) + reference = { + "north": 223490, + "south": 223390, + "east": 636820, + "west": 636710, + "nsres": 10, + "ewres": 10, + "datatype": "FCELL", + } self.assertRasterFitsInfo( raster=self.imported, reference=reference, precision=1e-6 ) @@ -59,7 +59,7 @@ def test_import_asc_custom_res(self): resolution="value", resolution_value=30, ) - reference = dict(rows=3, cols=4, nsres=30, ewres=30, datatype="CELL") + reference = {"rows": 3, "cols": 4, "nsres": 30, "ewres": 30, "datatype": "CELL"} self.assertRasterFitsInfo( raster=self.imported, reference=reference, precision=1.1 ) @@ -75,7 +75,7 @@ def test_import_asc_region_extent(self): extent="region", resolution="region", ) - reference = dict(north=223655, south=223600) + reference = {"north": 223655, "south": 223600} self.assertRasterFitsInfo( raster=self.imported, reference=reference, precision=1e-6 ) @@ -93,7 +93,7 @@ def test_import_use_temp_region(self): extent="region", resolution="region", ) - reference = dict(north=223630, south=223600, nsres=10, ewres=10) + reference = {"north": 223630, "south": 223600, "nsres": 10, "ewres": 10} self.assertRasterFitsInfo( raster=self.imported, reference=reference, precision=1e-6 ) @@ -107,7 +107,7 @@ def test_import_use_temp_region(self): extent="region", overwrite=True, ) - reference = dict(south=223390, north=223450, nsres=10, ewres=10) + reference = {"south": 223390, "north": 223450, "nsres": 10, "ewres": 10} self.assertRasterFitsInfo( raster=self.imported, reference=reference, precision=1e-6 ) @@ -122,7 +122,7 @@ def test_import_use_temp_region(self): resolution="region", overwrite=True, ) - reference = dict(north=223660, south=223600, nsres=10, ewres=10) + reference = {"north": 223660, "south": 223600, "nsres": 10, "ewres": 10} self.assertRasterFitsInfo( raster=self.imported, reference=reference, precision=1e-6 ) diff --git a/scripts/v.clip/testsuite/test_v_clip.py b/scripts/v.clip/testsuite/test_v_clip.py index c987c9e2dfe..576430aad5f 100644 --- a/scripts/v.clip/testsuite/test_v_clip.py +++ b/scripts/v.clip/testsuite/test_v_clip.py @@ -62,7 +62,7 @@ def test_points(self): "v.clip", input=self.inpoint, clip=self.inpclip, output=self.outclip ) self.assertVectorExists(self.outclip) - topology = dict(points=8) + topology = {"points": 8} self.assertVectorFitsTopoInfo(self.outclip, topology) def test_region(self): @@ -75,7 +75,7 @@ def test_region(self): flags="r", ) self.assertVectorExists(self.outreg) - topology = dict(points=13) + topology = {"points": 13} self.assertVectorFitsTopoInfo(self.outreg, topology) def test_lines(self): @@ -84,7 +84,7 @@ def test_lines(self): "v.clip", input=self.inlines, clip=self.garner, output=self.outline ) self.assertVectorExists(self.outline) - topology = dict(lines=13, nodes=16) + topology = {"lines": 13, "nodes": 16} self.assertVectorFitsTopoInfo(self.outline, topology) def test_poly(self): @@ -93,7 +93,7 @@ def test_poly(self): "v.clip", input=self.inpoly, clip=self.inpclip, output=self.outpoly ) self.assertVectorExists(self.outpoly) - topology = dict(areas=275) + topology = {"areas": 275} self.assertVectorFitsTopoInfo(self.outpoly, topology) def test_poly_diss(self): @@ -106,7 +106,7 @@ def test_poly_diss(self): flags="d", ) self.assertVectorExists(self.outdiss) - topology = dict(areas=276) + topology = {"areas": 276} self.assertVectorFitsTopoInfo(self.outdiss, topology) def test_poly_notable(self): @@ -126,7 +126,7 @@ def run_poly_notable(vmaps): for vmap in vmaps: self.runModule("g.remove", flags="f", type="vector", name=vmap) self.assertVectorExists(self.outpoly) - topology = dict(areas=275) + topology = {"areas": 275} self.assertVectorFitsTopoInfo(self.outpoly, topology) for vmaps in ([self.inpoly], [self.inpclip], [self.inpoly, self.inpclip]): diff --git a/scripts/v.import/testsuite/test_v_import.py b/scripts/v.import/testsuite/test_v_import.py index f44666a4379..d8dd6b85887 100755 --- a/scripts/v.import/testsuite/test_v_import.py +++ b/scripts/v.import/testsuite/test_v_import.py @@ -28,39 +28,39 @@ def test_import_same_proj_gpkg(self): self.assertVectorExists(self.imported) self.assertVectorFitsExtendedInfo( vector=self.imported, - reference=dict( - name=self.imported, - level=2, - num_dblinks=1, - attribute_table=self.imported, - ), + reference={ + "name": self.imported, + "level": 2, + "num_dblinks": 1, + "attribute_table": self.imported, + }, ) self.assertVectorFitsRegionInfo( vector=self.imported, # Values rounded to one decimal point. - reference=dict( - north=227744.8, - south=215259.6, - east=644450.6, - west=631257.4, - top=0, - bottom=0, - ), + reference={ + "north": 227744.8, + "south": 215259.6, + "east": 644450.6, + "west": 631257.4, + "top": 0, + "bottom": 0, + }, precision=0.2, ) self.assertVectorFitsTopoInfo( vector=self.imported, - reference=dict( - nodes=5, - points=2, - lines=1, - boundaries=3, - centroids=3, - areas=3, - islands=3, - primitives=9, - map3d=0, - ), + reference={ + "nodes": 5, + "points": 2, + "lines": 1, + "boundaries": 3, + "centroids": 3, + "areas": 3, + "islands": 3, + "primitives": 9, + "map3d": 0, + }, ) def test_import_gpkg_wgs84(self): @@ -71,39 +71,39 @@ def test_import_gpkg_wgs84(self): self.assertVectorExists(self.imported) self.assertVectorFitsExtendedInfo( vector=self.imported, - reference=dict( - name=self.imported, - level=2, - num_dblinks=1, - attribute_table=self.imported, - ), + reference={ + "name": self.imported, + "level": 2, + "num_dblinks": 1, + "attribute_table": self.imported, + }, ) self.assertVectorFitsRegionInfo( vector=self.imported, # Values rounded to one decimal point. - reference=dict( - north=227744.8, - south=215259.6, - east=644450.6, - west=631257.4, - top=0, - bottom=0, - ), + reference={ + "north": 227744.8, + "south": 215259.6, + "east": 644450.6, + "west": 631257.4, + "top": 0, + "bottom": 0, + }, precision=0.2, ) self.assertVectorFitsTopoInfo( vector=self.imported, - reference=dict( - nodes=5, - points=2, - lines=1, - boundaries=3, - centroids=3, - areas=3, - islands=3, - primitives=9, - map3d=0, - ), + reference={ + "nodes": 5, + "points": 2, + "lines": 1, + "boundaries": 3, + "centroids": 3, + "areas": 3, + "islands": 3, + "primitives": 9, + "map3d": 0, + }, ) diff --git a/vector/v.in.lidar/testsuite/decimation_test.py b/vector/v.in.lidar/testsuite/decimation_test.py index 2ca462b1ca1..7fb596343b9 100644 --- a/vector/v.in.lidar/testsuite/decimation_test.py +++ b/vector/v.in.lidar/testsuite/decimation_test.py @@ -64,7 +64,7 @@ def test_identical(self): ) self.assertVectorExists(self.imported_points) self.assertVectorFitsTopoInfo( - vector=self.imported_points, reference=dict(points=self.npoints) + vector=self.imported_points, reference={"points": self.npoints} ) def skip_number(self, number, expect): @@ -78,7 +78,7 @@ def skip_number(self, number, expect): ) self.assertVectorExists(self.imported_points) self.assertVectorFitsTopoInfo( - vector=self.imported_points, reference=dict(points=expect) + vector=self.imported_points, reference={"points": expect} ) def preserve_number(self, number, expect): @@ -92,7 +92,7 @@ def preserve_number(self, number, expect): ) self.assertVectorExists(self.imported_points) self.assertVectorFitsTopoInfo( - vector=self.imported_points, reference=dict(points=expect) + vector=self.imported_points, reference={"points": expect} ) def offset_number(self, number, expect): @@ -106,7 +106,7 @@ def offset_number(self, number, expect): ) self.assertVectorExists(self.imported_points) self.assertVectorFitsTopoInfo( - vector=self.imported_points, reference=dict(points=expect) + vector=self.imported_points, reference={"points": expect} ) def limit_number(self, number, expect): @@ -120,7 +120,7 @@ def limit_number(self, number, expect): ) self.assertVectorExists(self.imported_points) self.assertVectorFitsTopoInfo( - vector=self.imported_points, reference=dict(points=expect) + vector=self.imported_points, reference={"points": expect} ) def test_decimated_skip_2(self): @@ -164,7 +164,7 @@ def test_offset_preserve(self): self.assertVectorExists(self.imported_points) self.assertVectorFitsTopoInfo( vector=self.imported_points, - reference=dict(points=int((self.npoints - 105) / 10)), + reference={"points": int((self.npoints - 105) / 10)}, ) def test_limit_skip(self): @@ -179,7 +179,7 @@ def test_limit_skip(self): ) self.assertVectorExists(self.imported_points) self.assertVectorFitsTopoInfo( - vector=self.imported_points, reference=dict(points=105) + vector=self.imported_points, reference={"points": 105} ) def test_offset_limit_skip(self): @@ -196,7 +196,7 @@ def test_offset_limit_skip(self): self.assertVectorExists(self.imported_points) self.assertVectorFitsTopoInfo( vector=self.imported_points, - reference=dict(points=0.8 * (self.npoints - 50)), + reference={"points": 0.8 * (self.npoints - 50)}, ) diff --git a/vector/v.in.lidar/testsuite/mask_test.py b/vector/v.in.lidar/testsuite/mask_test.py index 28600c27014..407c02224a8 100644 --- a/vector/v.in.lidar/testsuite/mask_test.py +++ b/vector/v.in.lidar/testsuite/mask_test.py @@ -136,7 +136,7 @@ def test_no_mask(self): ) self.assertVectorExists(self.imported_points) self.assertVectorFitsTopoInfo( - vector=self.imported_points, reference=dict(points=19) + vector=self.imported_points, reference={"points": 19} ) def test_mask(self): @@ -150,7 +150,7 @@ def test_mask(self): ) self.assertVectorExists(self.imported_points) self.assertVectorFitsTopoInfo( - vector=self.imported_points, reference=dict(points=11) + vector=self.imported_points, reference={"points": 11} ) def test_inverted_mask(self): @@ -164,7 +164,7 @@ def test_inverted_mask(self): ) self.assertVectorExists(self.imported_points) self.assertVectorFitsTopoInfo( - vector=self.imported_points, reference=dict(points=8) + vector=self.imported_points, reference={"points": 8} ) diff --git a/vector/v.in.lidar/testsuite/test_v_in_lidar_filter.py b/vector/v.in.lidar/testsuite/test_v_in_lidar_filter.py index 769b2ae6c4d..95264940d0d 100644 --- a/vector/v.in.lidar/testsuite/test_v_in_lidar_filter.py +++ b/vector/v.in.lidar/testsuite/test_v_in_lidar_filter.py @@ -102,7 +102,7 @@ def test_no_filter(self): ) self.assertVectorExists(self.imported_points) self.assertVectorFitsTopoInfo( - vector=self.imported_points, reference=dict(points=19) + vector=self.imported_points, reference={"points": 19} ) def return_filter(self, name, npoints): @@ -116,7 +116,7 @@ def return_filter(self, name, npoints): ) self.assertVectorExists(self.imported_points) self.assertVectorFitsTopoInfo( - vector=self.imported_points, reference=dict(points=npoints) + vector=self.imported_points, reference={"points": npoints} ) def test_first_return_filter(self): @@ -142,7 +142,7 @@ def class_filter(self, class_n, npoints): ) self.assertVectorExists(self.imported_points) self.assertVectorFitsTopoInfo( - vector=self.imported_points, reference=dict(points=npoints) + vector=self.imported_points, reference={"points": npoints} ) def test_class_2_filter(self): @@ -173,7 +173,7 @@ def return_and_class_filter(self, return_name, class_n, npoints): ) self.assertVectorExists(self.imported_points) self.assertVectorFitsTopoInfo( - vector=self.imported_points, reference=dict(points=npoints) + vector=self.imported_points, reference={"points": npoints} ) def test_first_return_and_class_filter(self): @@ -195,7 +195,7 @@ def zrange_filter(self, zrange, npoints): ) self.assertVectorExists(self.imported_points) self.assertVectorFitsTopoInfo( - vector=self.imported_points, reference=dict(points=npoints) + vector=self.imported_points, reference={"points": npoints} ) def test_zrange_filter(self): @@ -221,7 +221,7 @@ def test_zrange_and_class_filter(self): ) self.assertVectorExists(self.imported_points) self.assertVectorFitsTopoInfo( - vector=self.imported_points, reference=dict(points=4) + vector=self.imported_points, reference={"points": 4} ) def test_zrange_and_return_filter(self): @@ -236,7 +236,7 @@ def test_zrange_and_return_filter(self): ) self.assertVectorExists(self.imported_points) self.assertVectorFitsTopoInfo( - vector=self.imported_points, reference=dict(points=2) + vector=self.imported_points, reference={"points": 2} ) diff --git a/vector/v.in.pdal/testsuite/test_v_in_pdal_filter.py b/vector/v.in.pdal/testsuite/test_v_in_pdal_filter.py index 7acc25a60ac..3c10c9b8331 100644 --- a/vector/v.in.pdal/testsuite/test_v_in_pdal_filter.py +++ b/vector/v.in.pdal/testsuite/test_v_in_pdal_filter.py @@ -102,7 +102,7 @@ def test_no_filter(self): self.assertModule("v.in.pdal", input=self.las_file, output=self.imported_points) self.assertVectorExists(self.imported_points) self.assertVectorFitsTopoInfo( - vector=self.imported_points, reference=dict(points=19) + vector=self.imported_points, reference={"points": 19} ) @unittest.skipIf(shutil.which("v.in.pdal") is None, "Cannot find v.in.pdal") @@ -116,7 +116,7 @@ def return_filter(self, name, npoints): ) self.assertVectorExists(self.imported_points) self.assertVectorFitsTopoInfo( - vector=self.imported_points, reference=dict(points=npoints) + vector=self.imported_points, reference={"points": npoints} ) @unittest.skipIf(shutil.which("v.in.pdal") is None, "Cannot find v.in.pdal") @@ -145,7 +145,7 @@ def class_filter(self, class_n, npoints): ) self.assertVectorExists(self.imported_points) self.assertVectorFitsTopoInfo( - vector=self.imported_points, reference=dict(points=npoints) + vector=self.imported_points, reference={"points": npoints} ) @unittest.skipIf(shutil.which("v.in.pdal") is None, "Cannot find v.in.pdal") @@ -180,7 +180,7 @@ def return_and_class_filter(self, return_name, class_n, npoints): ) self.assertVectorExists(self.imported_points) self.assertVectorFitsTopoInfo( - vector=self.imported_points, reference=dict(points=npoints) + vector=self.imported_points, reference={"points": npoints} ) @unittest.skipIf(shutil.which("v.in.pdal") is None, "Cannot find v.in.pdal") @@ -201,7 +201,7 @@ def zrange_filter(self, zrange, npoints): ) self.assertVectorExists(self.imported_points) self.assertVectorFitsTopoInfo( - vector=self.imported_points, reference=dict(points=npoints) + vector=self.imported_points, reference={"points": npoints} ) @unittest.skipIf(shutil.which("v.in.pdal") is None, "Cannot find v.in.pdal") @@ -229,7 +229,7 @@ def test_zrange_and_class_filter(self): ) self.assertVectorExists(self.imported_points) self.assertVectorFitsTopoInfo( - vector=self.imported_points, reference=dict(points=4) + vector=self.imported_points, reference={"points": 4} ) @unittest.skipIf(shutil.which("v.in.pdal") is None, "Cannot find v.in.pdal") @@ -244,7 +244,7 @@ def test_zrange_and_return_filter(self): ) self.assertVectorExists(self.imported_points) self.assertVectorFitsTopoInfo( - vector=self.imported_points, reference=dict(points=2) + vector=self.imported_points, reference={"points": 2} ) diff --git a/vector/v.info/testsuite/test_vinfo.py b/vector/v.info/testsuite/test_vinfo.py index b5b3aa17b82..c8183f76412 100644 --- a/vector/v.info/testsuite/test_vinfo.py +++ b/vector/v.info/testsuite/test_vinfo.py @@ -14,19 +14,19 @@ class TestVInfo(TestCase): test_vinfo_with_db_3d = "test_vinfo_with_db_3d" # All maps should be tested against these references - reference = dict( - format="native", - level=2, - nodes=0, - points=5, - lines=0, - boundaries=0, - centroids=0, - areas=0, - islands=0, - primitives=5, - scale="1:1", - ) + reference = { + "format": "native", + "level": 2, + "nodes": 0, + "points": 5, + "lines": 0, + "boundaries": 0, + "centroids": 0, + "areas": 0, + "islands": 0, + "primitives": 5, + "scale": "1:1", + } @classmethod def setUpClass(cls): @@ -117,9 +117,13 @@ def test_info_no_db(self): flags="etg", sep="=", precision=0.1, - reference=dict( - name=self.test_vinfo_no_db, map3d=0, num_dblinks=0, bottom=0.0, top=0.0 - ), + reference={ + "name": self.test_vinfo_no_db, + "map3d": 0, + "num_dblinks": 0, + "bottom": 0.0, + "top": 0.0, + }, ) def test_info_with_db(self): @@ -131,19 +135,19 @@ def test_info_with_db(self): sep="=", precision=0.1, layer="1", - reference=dict( - name=self.test_vinfo_with_db, - num_dblinks=1, - attribute_layer_name=self.test_vinfo_with_db, - attribute_layer_number=1, - attribute_database_driver="sqlite", - attribute_table=self.test_vinfo_with_db, - attribute_primary_key="cat", - timestamp="none", - map3d=0, - bottom=0.0, - top=0.0, - ), + reference={ + "name": self.test_vinfo_with_db, + "num_dblinks": 1, + "attribute_layer_name": self.test_vinfo_with_db, + "attribute_layer_number": 1, + "attribute_database_driver": "sqlite", + "attribute_table": self.test_vinfo_with_db, + "attribute_primary_key": "cat", + "timestamp": "none", + "map3d": 0, + "bottom": 0.0, + "top": 0.0, + }, ) def test_info_with_db_wrong_layer(self): @@ -155,14 +159,14 @@ def test_info_with_db_wrong_layer(self): sep="=", precision=0.1, layer="2", - reference=dict( - name=self.test_vinfo_with_db, - num_dblinks=1, - timestamp="none", - map3d=0, - bottom=0.0, - top=0.0, - ), + reference={ + "name": self.test_vinfo_with_db, + "num_dblinks": 1, + "timestamp": "none", + "map3d": 0, + "bottom": 0.0, + "top": 0.0, + }, ) def test_info_with_db_3d(self): @@ -174,17 +178,17 @@ def test_info_with_db_3d(self): sep="=", precision=0.1, layer="1", - reference=dict( - name=self.test_vinfo_with_db_3d, - num_dblinks=1, - attribute_layer_name=self.test_vinfo_with_db_3d, - attribute_layer_number=1, - attribute_database_driver="sqlite", - attribute_table=self.test_vinfo_with_db_3d, - attribute_primary_key="cat", - map3d=1, - timestamp="15 Jan 1994", - ), + reference={ + "name": self.test_vinfo_with_db_3d, + "num_dblinks": 1, + "attribute_layer_name": self.test_vinfo_with_db_3d, + "attribute_layer_number": 1, + "attribute_database_driver": "sqlite", + "attribute_table": self.test_vinfo_with_db_3d, + "attribute_primary_key": "cat", + "map3d": 1, + "timestamp": "15 Jan 1994", + }, ) def test_json(self): diff --git a/vector/v.net/testsuite/test_v_net.py b/vector/v.net/testsuite/test_v_net.py index c5275dc30b7..469ac127f47 100644 --- a/vector/v.net/testsuite/test_v_net.py +++ b/vector/v.net/testsuite/test_v_net.py @@ -16,7 +16,7 @@ def test_nodes(self): self.assertModule( "v.net", input="streets", output=self.network, operation="nodes" ) - topology = dict(points=41813, nodes=41813, lines=49746) + topology = {"points": 41813, "nodes": 41813, "lines": 49746} self.assertVectorFitsTopoInfo(vector=self.network, reference=topology) layers = read_command("v.category", input=self.network, option="layers").strip() self.assertEqual(first="1", second=layers, msg="Layers do not match") @@ -26,7 +26,7 @@ def test_nodes_layers(self): self.assertModule( "v.net", input="streets", output=self.network, operation="nodes", flags="c" ) - topology = dict(points=41813, nodes=41813, lines=49746) + topology = {"points": 41813, "nodes": 41813, "lines": 49746} self.assertVectorFitsTopoInfo(vector=self.network, reference=topology) layers = read_command("v.category", input=self.network, option="layers").strip() self.assertEqual(first="1\n2", second=layers, msg="Layers do not match") @@ -41,7 +41,7 @@ def test_connect(self): operation="connect", threshold=1000, ) - topology = dict(points=167, nodes=42136, lines=50069) + topology = {"points": 167, "nodes": 42136, "lines": 50069} self.assertVectorFitsTopoInfo(vector=self.network, reference=topology) layers = read_command("v.category", input=self.network, option="layers").strip() self.assertEqual(first="1\n2", second=layers, msg="Layers do not match") @@ -57,7 +57,7 @@ def test_connect_snap(self): operation="connect", threshold=1000, ) - topology = dict(points=167, nodes=41969, lines=49902) + topology = {"points": 167, "nodes": 41969, "lines": 49902} self.assertVectorFitsTopoInfo(vector=self.network, reference=topology) diff --git a/vector/v.random/testsuite/test_v_random.py b/vector/v.random/testsuite/test_v_random.py index 73b421ea7aa..e5cc95a93db 100644 --- a/vector/v.random/testsuite/test_v_random.py +++ b/vector/v.random/testsuite/test_v_random.py @@ -37,7 +37,7 @@ def tearDown(cls): def test_num_points(self): """Checking if number of points equals 100""" self.assertModule("v.random", output=self.output, npoints=self.npoints) - topology = dict(points=self.npoints) + topology = {"points": self.npoints} self.assertVectorFitsTopoInfo(vector=self.output, reference=topology) def test_num_points_3D(self): @@ -50,7 +50,7 @@ def test_num_points_3D(self): zmax=self.zmax, flags="z", ) - topology = dict(points=self.npoints, map3d=1) + topology = {"points": self.npoints, "map3d": 1} self.assertVectorFitsTopoInfo(vector=self.output, reference=topology) def test_restrict(self): diff --git a/vector/v.select/testsuite/test_v_select.py b/vector/v.select/testsuite/test_v_select.py index ae95d9c5607..c74cdb53761 100644 --- a/vector/v.select/testsuite/test_v_select.py +++ b/vector/v.select/testsuite/test_v_select.py @@ -42,7 +42,7 @@ def test_opo(self): output=self.output, operator="overlap", ) - topology = dict(points=1088, lines=0, areas=0) + topology = {"points": 1088, "lines": 0, "areas": 0} self.assertVectorFitsTopoInfo(self.overlap, topology) def test_opd(self): @@ -54,7 +54,7 @@ def test_opd(self): output=self.output, operator="disjoint", ) - topology = dict(points=167, lines=0, areas=0) + topology = {"points": 167, "lines": 0, "areas": 0} self.assertVectorFitsTopoInfo(self.disjoint, topology) def test_ope(self): @@ -66,7 +66,7 @@ def test_ope(self): output=self.output, operator="equals", ) - topology = dict(points=0, lines=49746, areas=0) + topology = {"points": 0, "lines": 49746, "areas": 0} self.assertVectorFitsTopoInfo(self.equals, topology) def test_opt(self): @@ -78,7 +78,7 @@ def test_opt(self): output=self.output, operator="touches", ) - topology = dict(points=0, lines=0, areas=48) + topology = {"points": 0, "lines": 0, "areas": 48} self.assertVectorFitsTopoInfo(self.touches, topology) def test_opw(self): @@ -90,7 +90,7 @@ def test_opw(self): output=self.output, operator="within", ) - topology = dict(points=1088, lines=0, areas=0) + topology = {"points": 1088, "lines": 0, "areas": 0} self.assertVectorFitsTopoInfo(self.within, topology) diff --git a/vector/v.surf.rst/testsuite/test_vsurfrst.py b/vector/v.surf.rst/testsuite/test_vsurfrst.py index a1d3c654112..7d46eacb2f0 100644 --- a/vector/v.surf.rst/testsuite/test_vsurfrst.py +++ b/vector/v.surf.rst/testsuite/test_vsurfrst.py @@ -177,10 +177,10 @@ def test_run_outputs(self): map=self.deviations, column="flt1", reference=values, precision=1e-8 ) # treeseg - topology = dict(primitives=256) + topology = {"primitives": 256} self.assertVectorFitsTopoInfo(vector=self.treeseg, reference=topology) # overwin - topology = dict(primitives=256) + topology = {"primitives": 256} self.assertVectorFitsTopoInfo(vector=self.overwin, reference=topology) # test 3D versus attribute diff --git a/vector/v.to.3d/testsuite/test_vto3d.py b/vector/v.to.3d/testsuite/test_vto3d.py index 2151c46344a..bfa982ddd87 100644 --- a/vector/v.to.3d/testsuite/test_vto3d.py +++ b/vector/v.to.3d/testsuite/test_vto3d.py @@ -35,9 +35,9 @@ def test_contours(self): column="z", flags="r", ) - is3d = dict(map3d=0) + is3d = {"map3d": 0} self.assertVectorFitsTopoInfo(vector=self.contours2d, reference=is3d) - missing = dict(nmissing=0, nnull=0) + missing = {"nmissing": 0, "nnull": 0} self.assertVectorFitsUnivar(map=self.contours2d, column="z", reference=missing) From ce1a43cb8b45e70f41540887a8a073e32227a517 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sun, 30 Jun 2024 20:26:19 -0400 Subject: [PATCH 033/293] style: Fixes if-stmt-min-max (PLR1730) (#3950) Concerns Pylint rules "consider-using-min-builtin / R1730" and "consider-using-max-builtin / R1731" Using `ruff check --output-format=concise --select PLR1730 --preview --fix`. --- gui/wxpython/core/gcmd.py | 6 ++-- gui/wxpython/dbmgr/base.py | 6 ++-- gui/wxpython/gcp/manager.py | 18 ++++------ gui/wxpython/gcp/statusbar.py | 3 +- gui/wxpython/gmodeler/canvas.py | 3 +- gui/wxpython/gmodeler/panels.py | 12 +++---- gui/wxpython/gui_core/prompt.py | 6 ++-- gui/wxpython/image2target/ii2t_manager.py | 18 ++++------ gui/wxpython/image2target/ii2t_statusbar.py | 3 +- gui/wxpython/location_wizard/dialogs.py | 6 ++-- gui/wxpython/modules/colorrules.py | 12 +++---- gui/wxpython/nviz/tools.py | 3 +- gui/wxpython/photo2image/ip2i_manager.py | 18 ++++------ gui/wxpython/photo2image/ip2i_statusbar.py | 3 +- gui/wxpython/vnet/dialogs.py | 6 ++-- python/grass/benchmark/runners.py | 3 +- python/grass/imaging/images2swf.py | 3 +- python/grass/jupyter/region.py | 12 +++---- python/grass/temporal/temporal_granularity.py | 36 +++++++------------ scripts/d.correlate/d.correlate.py | 12 +++---- scripts/d.rast.edit/d.rast.edit.py | 18 ++++------ scripts/d.rast.leg/d.rast.leg.py | 6 ++-- scripts/r.tileset/r.tileset.py | 12 +++---- temporal/t.rast.what/t.rast.what.py | 3 +- 24 files changed, 76 insertions(+), 152 deletions(-) diff --git a/gui/wxpython/core/gcmd.py b/gui/wxpython/core/gcmd.py index 138936fb4f5..993a1edb6b7 100644 --- a/gui/wxpython/core/gcmd.py +++ b/gui/wxpython/core/gcmd.py @@ -238,8 +238,7 @@ def _recv(self, which, maxsize): try: x = msvcrt.get_osfhandle(conn.fileno()) (read, nAvail, nMessage) = PeekNamedPipe(x, 0) - if maxsize < nAvail: - nAvail = maxsize + nAvail = min(maxsize, nAvail) if nAvail > 0: (errCode, read) = ReadFile(x, nAvail, None) except ValueError: @@ -301,8 +300,7 @@ def _recv(self, which, maxsize): def recv_some(p, t=0.1, e=1, tr=5, stderr=0): - if tr < 1: - tr = 1 + tr = max(tr, 1) x = time.time() + t y = [] r = "" diff --git a/gui/wxpython/dbmgr/base.py b/gui/wxpython/dbmgr/base.py index 399298921bf..3ce82188587 100644 --- a/gui/wxpython/dbmgr/base.py +++ b/gui/wxpython/dbmgr/base.py @@ -360,10 +360,8 @@ def LoadData(self, layer, columns=None, where=None, sql=None): i = 0 for col in columns: width = self.columns[col]["length"] * 6 # FIXME - if width < 60: - width = 60 - if width > 300: - width = 300 + width = max(width, 60) + width = min(width, 300) self.SetColumnWidth(col=i, width=width) i += 1 diff --git a/gui/wxpython/gcp/manager.py b/gui/wxpython/gcp/manager.py index 3e948dc3c91..580f0e4c938 100644 --- a/gui/wxpython/gcp/manager.py +++ b/gui/wxpython/gcp/manager.py @@ -2236,14 +2236,10 @@ def GetNewExtent(self, region, map=None): e, n = errlist[i].split() fe = float(e) fn = float(n) - if fe < newreg["w"]: - newreg["w"] = fe - if fe > newreg["e"]: - newreg["e"] = fe - if fn < newreg["s"]: - newreg["s"] = fn - if fn > newreg["n"]: - newreg["n"] = fn + newreg["w"] = min(fe, newreg["w"]) + newreg["e"] = max(fe, newreg["e"]) + newreg["s"] = min(fn, newreg["s"]) + newreg["n"] = max(fn, newreg["n"]) return newreg @@ -2292,10 +2288,8 @@ def AdjustMap(self, newreg): # LL locations if self.Map.projinfo["proj"] == "ll": - if newreg["n"] > 90.0: - newreg["n"] = 90.0 - if newreg["s"] < -90.0: - newreg["s"] = -90.0 + newreg["n"] = min(newreg["n"], 90.0) + newreg["s"] = max(newreg["s"], -90.0) ce = newreg["w"] + (newreg["e"] - newreg["w"]) / 2 cn = newreg["s"] + (newreg["n"] - newreg["s"]) / 2 diff --git a/gui/wxpython/gcp/statusbar.py b/gui/wxpython/gcp/statusbar.py index 1424f7f4846..3c5ee378a1e 100644 --- a/gui/wxpython/gcp/statusbar.py +++ b/gui/wxpython/gcp/statusbar.py @@ -97,8 +97,7 @@ def Update(self): and sets the spin limits accordingly.""" self.statusbar.SetStatusText("") maximum = self.mapFrame.GetListCtrl().GetItemCount() - if maximum < 1: - maximum = 1 + maximum = max(maximum, 1) self.widget.SetRange(0, maximum) self.Show() diff --git a/gui/wxpython/gmodeler/canvas.py b/gui/wxpython/gmodeler/canvas.py index 9742df75d73..51976e443ad 100644 --- a/gui/wxpython/gmodeler/canvas.py +++ b/gui/wxpython/gmodeler/canvas.py @@ -103,8 +103,7 @@ def GetNewShapePos(self, yoffset=50): ymax = 20 for item in self.GetDiagram().GetShapeList(): y = item.GetY() + item.GetBoundingBoxMin()[1] - if y > ymax: - ymax = y + ymax = max(y, ymax) return (self.GetSize()[0] // 2, ymax + yoffset) diff --git a/gui/wxpython/gmodeler/panels.py b/gui/wxpython/gmodeler/panels.py index 71a243cfc59..b198dace36a 100644 --- a/gui/wxpython/gmodeler/panels.py +++ b/gui/wxpython/gmodeler/panels.py @@ -989,14 +989,10 @@ def OnExportImage(self, event): xmax = x + w / 2 ymin = y - h / 2 ymax = y + h / 2 - if xmin < xminImg: - xminImg = xmin - if xmax > xmaxImg: - xmaxImg = xmax - if ymin < yminImg: - yminImg = ymin - if ymax > ymaxImg: - ymaxImg = ymax + xminImg = min(xmin, xminImg) + xmaxImg = max(xmax, xmaxImg) + yminImg = min(ymin, yminImg) + ymaxImg = max(ymax, ymaxImg) size = wx.Size(int(xmaxImg - xminImg) + 50, int(ymaxImg - yminImg) + 50) bitmap = EmptyBitmap(width=size.width, height=size.height) diff --git a/gui/wxpython/gui_core/prompt.py b/gui/wxpython/gui_core/prompt.py index 1faab429da3..8f7c02999d8 100644 --- a/gui/wxpython/gui_core/prompt.py +++ b/gui/wxpython/gui_core/prompt.py @@ -480,10 +480,8 @@ def OnKeyPressed(self, event): self.cmdindex = self.cmdindex - 1 if event.GetKeyCode() == wx.WXK_DOWN: self.cmdindex = self.cmdindex + 1 - if self.cmdindex < 0: - self.cmdindex = 0 - if self.cmdindex > len(self.cmdbuffer) - 1: - self.cmdindex = len(self.cmdbuffer) - 1 + self.cmdindex = max(self.cmdindex, 0) + self.cmdindex = min(self.cmdindex, len(self.cmdbuffer) - 1) try: # without strip causes problem on windows diff --git a/gui/wxpython/image2target/ii2t_manager.py b/gui/wxpython/image2target/ii2t_manager.py index 2828a3b0a58..2c1487ebf63 100644 --- a/gui/wxpython/image2target/ii2t_manager.py +++ b/gui/wxpython/image2target/ii2t_manager.py @@ -2174,14 +2174,10 @@ def GetNewExtent(self, region, map=None): e, n = errlist[i].split() fe = float(e) fn = float(n) - if fe < newreg["w"]: - newreg["w"] = fe - if fe > newreg["e"]: - newreg["e"] = fe - if fn < newreg["s"]: - newreg["s"] = fn - if fn > newreg["n"]: - newreg["n"] = fn + newreg["w"] = min(fe, newreg["w"]) + newreg["e"] = max(fe, newreg["e"]) + newreg["s"] = min(fn, newreg["s"]) + newreg["n"] = max(fn, newreg["n"]) return newreg @@ -2230,10 +2226,8 @@ def AdjustMap(self, newreg): # LL locations if self.Map.projinfo["proj"] == "ll": - if newreg["n"] > 90.0: - newreg["n"] = 90.0 - if newreg["s"] < -90.0: - newreg["s"] = -90.0 + newreg["n"] = min(newreg["n"], 90.0) + newreg["s"] = max(newreg["s"], -90.0) ce = newreg["w"] + (newreg["e"] - newreg["w"]) / 2 cn = newreg["s"] + (newreg["n"] - newreg["s"]) / 2 diff --git a/gui/wxpython/image2target/ii2t_statusbar.py b/gui/wxpython/image2target/ii2t_statusbar.py index 1424f7f4846..3c5ee378a1e 100644 --- a/gui/wxpython/image2target/ii2t_statusbar.py +++ b/gui/wxpython/image2target/ii2t_statusbar.py @@ -97,8 +97,7 @@ def Update(self): and sets the spin limits accordingly.""" self.statusbar.SetStatusText("") maximum = self.mapFrame.GetListCtrl().GetItemCount() - if maximum < 1: - maximum = 1 + maximum = max(maximum, 1) self.widget.SetRange(0, maximum) self.Show() diff --git a/gui/wxpython/location_wizard/dialogs.py b/gui/wxpython/location_wizard/dialogs.py index 8c48d75cf18..da706b96599 100644 --- a/gui/wxpython/location_wizard/dialogs.py +++ b/gui/wxpython/location_wizard/dialogs.py @@ -703,11 +703,9 @@ def __init__( width = max(width, w) height = height + 5 - if height > 400: - height = 400 + height = min(height, 400) width = width + 5 - if width > 400: - width = 400 + width = min(width, 400) # # VListBox for displaying and selecting transformations diff --git a/gui/wxpython/modules/colorrules.py b/gui/wxpython/modules/colorrules.py index 85d36bcd00b..21dc93476f6 100644 --- a/gui/wxpython/modules/colorrules.py +++ b/gui/wxpython/modules/colorrules.py @@ -728,10 +728,8 @@ def ReadColorTable(self, ctable): self.rulesPanel.mainPanel.FindWindowById(count + 2000).SetColour(rgb) # range try: - if float(value) < minim: - minim = float(value) - if float(value) > maxim: - maxim = float(value) + minim = min(float(value), minim) + maxim = max(float(value), maxim) except ValueError: # nv, default pass count += 1 @@ -1619,10 +1617,8 @@ def LoadRulesFromColumn(self): else: col1, col2 = record.split(sep) - if float(col1) < minim: - minim = float(col1) - if float(col1) > maxim: - maxim = float(col1) + minim = min(float(col1), minim) + maxim = max(float(col1), maxim) # color rules list should only have unique values of col1, not all # records diff --git a/gui/wxpython/nviz/tools.py b/gui/wxpython/nviz/tools.py index ad6cf9fd99c..8bbbc3791c7 100644 --- a/gui/wxpython/nviz/tools.py +++ b/gui/wxpython/nviz/tools.py @@ -2790,8 +2790,7 @@ def UpdateFrameIndex( frameCount = anim.GetFrameCount() if index >= frameCount: index = frameCount - 1 - if index < 0: - index = 0 + index = max(index, 0) if sliderWidget: slider = self.FindWindowById(self.win["anim"]["frameIndex"]["slider"]) diff --git a/gui/wxpython/photo2image/ip2i_manager.py b/gui/wxpython/photo2image/ip2i_manager.py index fc89994607a..34346f39cae 100644 --- a/gui/wxpython/photo2image/ip2i_manager.py +++ b/gui/wxpython/photo2image/ip2i_manager.py @@ -1455,14 +1455,10 @@ def GetNewExtent(self, region, map=None): e, n = errlist[i].split() fe = float(e) fn = float(n) - if fe < newreg["w"]: - newreg["w"] = fe - if fe > newreg["e"]: - newreg["e"] = fe - if fn < newreg["s"]: - newreg["s"] = fn - if fn > newreg["n"]: - newreg["n"] = fn + newreg["w"] = min(fe, newreg["w"]) + newreg["e"] = max(fe, newreg["e"]) + newreg["s"] = min(fn, newreg["s"]) + newreg["n"] = max(fn, newreg["n"]) return newreg @@ -1511,10 +1507,8 @@ def AdjustMap(self, newreg): # LL locations if self.Map.projinfo["proj"] == "ll": - if newreg["n"] > 90.0: - newreg["n"] = 90.0 - if newreg["s"] < -90.0: - newreg["s"] = -90.0 + newreg["n"] = min(newreg["n"], 90.0) + newreg["s"] = max(newreg["s"], -90.0) ce = newreg["w"] + (newreg["e"] - newreg["w"]) / 2 cn = newreg["s"] + (newreg["n"] - newreg["s"]) / 2 diff --git a/gui/wxpython/photo2image/ip2i_statusbar.py b/gui/wxpython/photo2image/ip2i_statusbar.py index 3415906aeee..336dcd6903f 100644 --- a/gui/wxpython/photo2image/ip2i_statusbar.py +++ b/gui/wxpython/photo2image/ip2i_statusbar.py @@ -97,8 +97,7 @@ def Update(self): and sets the spin limits accordingly.""" self.statusbar.SetStatusText("") maximum = self.mapFrame.GetListCtrl().GetItemCount() - if maximum < 1: - maximum = 1 + maximum = max(maximum, 1) self.widget.SetRange(0, maximum) self.Show() diff --git a/gui/wxpython/vnet/dialogs.py b/gui/wxpython/vnet/dialogs.py index 03b48b698bb..e8bbd4520a7 100644 --- a/gui/wxpython/vnet/dialogs.py +++ b/gui/wxpython/vnet/dialogs.py @@ -1743,8 +1743,7 @@ def AddStatusItem(self, text, key, priority): if item["key"] == statusTextItem["key"]: self.statusItems.remove(item) self.statusItems.append(statusTextItem) - if self.maxPriority < statusTextItem["priority"]: - self.maxPriority = statusTextItem["priority"] + self.maxPriority = max(self.maxPriority, statusTextItem["priority"]) self._updateStatus() def _updateStatus(self): @@ -1770,8 +1769,7 @@ def RemoveStatusItem(self, key): if update: for item in self.statusItems: self.maxPriority = 0 - if self.maxPriority < item["priority"]: - self.maxPriority = item["priority"] + self.maxPriority = max(self.maxPriority, item["priority"]) self._updateStatus() diff --git a/python/grass/benchmark/runners.py b/python/grass/benchmark/runners.py index d436349063c..e733e9b64de 100644 --- a/python/grass/benchmark/runners.py +++ b/python/grass/benchmark/runners.py @@ -55,8 +55,7 @@ def benchmark_single(module, label, repeat=5): measured_times.append(module.time) avg = time_sum / repeat - if avg < min_avg: - min_avg = avg + min_avg = min(avg, min_avg) print(f"\nResult - {avg}s") print("\u2500" * term_size.columns) diff --git a/python/grass/imaging/images2swf.py b/python/grass/imaging/images2swf.py index da03c40f354..72354e751a6 100644 --- a/python/grass/imaging/images2swf.py +++ b/python/grass/imaging/images2swf.py @@ -341,8 +341,7 @@ def twitsToBits(arr): maxlen = 1 for i in arr: tmp = len(signedIntToBits(i * 20)) - if tmp > maxlen: - maxlen = tmp + maxlen = max(tmp, maxlen) # build array bits = intToBits(maxlen, 5) diff --git a/python/grass/jupyter/region.py b/python/grass/jupyter/region.py index 30e71385343..67eb64104a9 100644 --- a/python/grass/jupyter/region.py +++ b/python/grass/jupyter/region.py @@ -114,14 +114,10 @@ def _set_bbox(self, env): west = float(bbox["ll_w"]) north = float(bbox["ll_n"]) east = float(bbox["ll_e"]) - if self._bbox[0][0] > south: - self._bbox[0][0] = south - if self._bbox[0][1] > west: - self._bbox[0][1] = west - if self._bbox[1][0] < north: - self._bbox[1][0] = north - if self._bbox[1][1] < east: - self._bbox[1][1] = east + self._bbox[0][0] = min(self._bbox[0][0], south) + self._bbox[0][1] = min(self._bbox[0][1], west) + self._bbox[1][0] = max(self._bbox[1][0], north) + self._bbox[1][1] = max(self._bbox[1][1], east) class RegionManagerFor2D: diff --git a/python/grass/temporal/temporal_granularity.py b/python/grass/temporal/temporal_granularity.py index 3b1b005d533..207404cf3d2 100644 --- a/python/grass/temporal/temporal_granularity.py +++ b/python/grass/temporal/temporal_granularity.py @@ -948,55 +948,43 @@ def compute_common_absolute_time_granularity_simple(gran_list): if gran in ["seconds", "second"]: has_seconds = True - if min_gran > 0: - min_gran = 0 - if max_gran < 0: - max_gran = 0 + min_gran = min(min_gran, 0) + max_gran = max(max_gran, 0) seconds.append(int(num)) if gran in ["minutes", "minute"]: has_minutes = True - if min_gran > 1: - min_gran = 1 - if max_gran < 1: - max_gran = 1 + min_gran = min(min_gran, 1) + max_gran = max(max_gran, 1) minutes.append(int(num)) if gran in ["hours", "hour"]: has_hours = True - if min_gran > 2: - min_gran = 2 - if max_gran < 2: - max_gran = 2 + min_gran = min(min_gran, 2) + max_gran = max(max_gran, 2) hours.append(int(num)) if gran in ["days", "day"]: has_days = True - if min_gran > 3: - min_gran = 3 - if max_gran < 3: - max_gran = 3 + min_gran = min(min_gran, 3) + max_gran = max(max_gran, 3) days.append(int(num)) if gran in ["months", "month"]: has_months = True - if min_gran > 4: - min_gran = 4 - if max_gran < 4: - max_gran = 4 + min_gran = min(min_gran, 4) + max_gran = max(max_gran, 4) months.append(int(num)) if gran in ["years", "year"]: has_years = True - if min_gran > 5: - min_gran = 5 - if max_gran < 5: - max_gran = 5 + min_gran = min(min_gran, 5) + max_gran = max(max_gran, 5) years.append(int(num)) diff --git a/scripts/d.correlate/d.correlate.py b/scripts/d.correlate/d.correlate.py index 596d5ae6bf2..af681cce545 100755 --- a/scripts/d.correlate/d.correlate.py +++ b/scripts/d.correlate/d.correlate.py @@ -83,14 +83,10 @@ def main(): minx = maxx = x miny = maxy = y first = False - if minx > x: - minx = x - if maxx < x: - maxx = x - if miny > y: - miny = y - if maxy < y: - maxy = y + minx = min(minx, x) + maxx = max(maxx, x) + miny = min(miny, y) + maxy = max(maxy, y) ifile.close() kx = 100.0 / (maxx - minx + 1) diff --git a/scripts/d.rast.edit/d.rast.edit.py b/scripts/d.rast.edit/d.rast.edit.py index 4824a17a81c..2a55a1dde50 100755 --- a/scripts/d.rast.edit/d.rast.edit.py +++ b/scripts/d.rast.edit/d.rast.edit.py @@ -483,10 +483,8 @@ def initialize(self): for k, f in wind_keys.values(): self.total[k] = (f)(reg[k]) - if self.cols > self.total["cols"]: - self.cols = self.total["cols"] - if self.rows > self.total["rows"]: - self.rows = self.total["rows"] + self.cols = min(self.cols, self.total["cols"]) + self.rows = min(self.rows, self.total["rows"]) tempbase = grass.tempfile() grass.try_remove(tempbase) @@ -627,14 +625,10 @@ def change_window(self): del wait def force_window(self): - if self.origin_x < 0: - self.origin_x = 0 - if self.origin_x > self.total["cols"] - self.cols: - self.origin_x = self.total["cols"] - self.cols - if self.origin_y < 0: - self.origin_y = 0 - if self.origin_y > self.total["rows"] - self.rows: - self.origin_y = self.total["rows"] - self.rows + self.origin_x = max(self.origin_x, 0) + self.origin_x = min(self.origin_x, self.total["cols"] - self.cols) + self.origin_y = max(self.origin_y, 0) + self.origin_y = min(self.origin_y, self.total["rows"] - self.rows) def update_status(self, row, col): self.status["row"] = row diff --git a/scripts/d.rast.leg/d.rast.leg.py b/scripts/d.rast.leg/d.rast.leg.py index 7f29ec12e29..855bf85e684 100755 --- a/scripts/d.rast.leg/d.rast.leg.py +++ b/scripts/d.rast.leg/d.rast.leg.py @@ -126,10 +126,8 @@ def main(): ncats = len(cats.strip().split("\n")) # Only need to adjust legend size if number of categories is between 1 and 10 - if ncats < 2: - ncats = 2 - if ncats > 10: - ncats = 10 + ncats = max(ncats, 2) + ncats = min(ncats, 10) VSpacing = 100 - (ncats * 10) + 10 diff --git a/scripts/r.tileset/r.tileset.py b/scripts/r.tileset/r.tileset.py index 13e970aeaab..ac5cd5c07a0 100644 --- a/scripts/r.tileset/r.tileset.py +++ b/scripts/r.tileset/r.tileset.py @@ -137,14 +137,10 @@ def pointsToBbox(points): if not min_y: min_y = max_y = point[1] - if min_x > point[0]: - min_x = point[0] - if max_x < point[0]: - max_x = point[0] - if min_y > point[1]: - min_y = point[1] - if max_y < point[1]: - max_y = point[1] + min_x = min(min_x, point[0]) + max_x = max(max_x, point[0]) + min_y = min(min_y, point[1]) + max_y = max(max_y, point[1]) bbox["n"] = max_y bbox["s"] = min_y diff --git a/temporal/t.rast.what/t.rast.what.py b/temporal/t.rast.what/t.rast.what.py index ffb1c884893..799d34582bc 100755 --- a/temporal/t.rast.what/t.rast.what.py +++ b/temporal/t.rast.what/t.rast.what.py @@ -248,8 +248,7 @@ def main(options, flags): else: gscript.error(_("Please specify points or coordinates")) - if len(maps) < nprocs: - nprocs = len(maps) + nprocs = min(len(maps), nprocs) # The module queue for parallel execution process_queue = pymod.ParallelModuleQueue(int(nprocs)) From 2ae07bc17e275949c7963eac3659f883bffb70da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sun, 30 Jun 2024 20:30:47 -0400 Subject: [PATCH 034/293] style(gui): Fixes literal-membership (PLR6201) (#3952) * style(gui/wxpython): Fixes literal-membership (PLR6201) Concerns Pylint rule "use-set-for-membership / R6201" Using `ruff check --output-format=concise --select PLR6201 --preview --unsafe-fixes --fix gui/`. * style: Apply single element in set formatting --- gui/wxpython/animation/controller.py | 4 +- gui/wxpython/animation/data.py | 4 +- gui/wxpython/animation/dialogs.py | 16 +++--- gui/wxpython/core/gcmd.py | 4 +- gui/wxpython/core/gconsole.py | 8 +-- gui/wxpython/core/globalvar.py | 6 +-- gui/wxpython/core/menutree.py | 4 +- gui/wxpython/core/render.py | 10 ++-- gui/wxpython/core/settings.py | 2 +- gui/wxpython/core/treemodel.py | 2 +- gui/wxpython/core/utils.py | 24 ++++----- gui/wxpython/core/workspace.py | 20 ++++---- gui/wxpython/core/ws.py | 4 +- gui/wxpython/datacatalog/catalog.py | 2 +- gui/wxpython/datacatalog/tree.py | 16 +++--- gui/wxpython/dbmgr/sqlbuilder.py | 8 +-- gui/wxpython/gmodeler/canvas.py | 4 +- gui/wxpython/gmodeler/model.py | 12 ++--- gui/wxpython/gmodeler/panels.py | 6 +-- gui/wxpython/gui_core/forms.py | 52 +++++++++---------- gui/wxpython/gui_core/goutput.py | 2 +- gui/wxpython/gui_core/gselect.py | 26 +++++----- gui/wxpython/gui_core/preferences.py | 4 +- gui/wxpython/gui_core/widgets.py | 2 +- gui/wxpython/history/browser.py | 2 +- gui/wxpython/iscatt/controllers.py | 6 +-- gui/wxpython/iscatt/core_c.py | 2 +- gui/wxpython/iscatt/iscatt_core.py | 4 +- gui/wxpython/iscatt/toolbars.py | 6 +-- gui/wxpython/lmgr/frame.py | 6 +-- gui/wxpython/lmgr/layertree.py | 20 ++++---- gui/wxpython/lmgr/workspace.py | 2 +- gui/wxpython/location_wizard/wizard.py | 2 +- gui/wxpython/main_window/frame.py | 6 +-- gui/wxpython/mapdisp/frame.py | 4 +- gui/wxpython/mapdisp/main.py | 2 +- gui/wxpython/mapdisp/statusbar.py | 8 +-- gui/wxpython/mapwin/buffered.py | 6 +-- gui/wxpython/modules/colorrules.py | 2 +- gui/wxpython/modules/import_export.py | 4 +- gui/wxpython/nviz/mapwindow.py | 30 +++++------ gui/wxpython/nviz/preferences.py | 4 +- gui/wxpython/nviz/tools.py | 28 +++++------ gui/wxpython/nviz/workspace.py | 2 +- gui/wxpython/psmap/dialogs.py | 14 +++--- gui/wxpython/psmap/frame.py | 52 +++++++++---------- gui/wxpython/psmap/instructions.py | 58 +++++++++++----------- gui/wxpython/rlisetup/sampling_frame.py | 14 +++--- gui/wxpython/rlisetup/wizard.py | 24 ++++----- gui/wxpython/startup/locdownload.py | 2 +- gui/wxpython/tplot/frame.py | 12 ++--- gui/wxpython/vdigit/mapwindow.py | 56 ++++++++++----------- gui/wxpython/vdigit/wxdisplay.py | 4 +- gui/wxpython/vnet/dialogs.py | 8 +-- gui/wxpython/vnet/vnet_core.py | 2 +- gui/wxpython/vnet/vnet_data.py | 12 ++--- gui/wxpython/web_services/cap_interface.py | 4 +- gui/wxpython/wxgui.py | 4 +- gui/wxpython/wxplot/base.py | 6 +-- 59 files changed, 330 insertions(+), 330 deletions(-) diff --git a/gui/wxpython/animation/controller.py b/gui/wxpython/animation/controller.py index ed03f7dadb5..3e914e68112 100644 --- a/gui/wxpython/animation/controller.py +++ b/gui/wxpython/animation/controller.py @@ -460,7 +460,7 @@ def EvaluateInput(self, animationData): for anim in animationData: for layer in anim.layerList: if layer.active and hasattr(layer, "maps"): - if layer.mapType in ("strds", "stvds", "str3ds"): + if layer.mapType in {"strds", "stvds", "str3ds"}: stds += 1 else: maps += 1 @@ -672,5 +672,5 @@ def export_avi_callback(event): del self.busy GError(parent=self.frame, message=str(e)) return - if exportInfo["method"] in ("sequence", "gif", "swf"): + if exportInfo["method"] in {"sequence", "gif", "swf"}: del self.busy diff --git a/gui/wxpython/animation/data.py b/gui/wxpython/animation/data.py index 5c2cdf35ef1..ee4e51cbef1 100644 --- a/gui/wxpython/animation/data.py +++ b/gui/wxpython/animation/data.py @@ -85,7 +85,7 @@ def SetLayerList(self, layerList): timeseriesList = [] for layer in layerList: if layer.active and hasattr(layer, "maps"): - if layer.mapType in ("strds", "stvds", "str3ds"): + if layer.mapType in {"strds", "stvds", "str3ds"}: timeseriesList.append((layer.name, layer.mapType)) self._firstStdsNameType = layer.name, layer.mapType else: @@ -298,7 +298,7 @@ def SetName(self, name): raise ValueError( "To set layer name, the type of layer must be specified." ) - if self._mapType in ("strds", "stvds", "str3ds"): + if self._mapType in {"strds", "stvds", "str3ds"}: try: name = validateTimeseriesName(name, self._mapType) self._maps = getRegisteredMaps(name, self._mapType) diff --git a/gui/wxpython/animation/dialogs.py b/gui/wxpython/animation/dialogs.py index 26bd38fedfe..732a01b68d4 100644 --- a/gui/wxpython/animation/dialogs.py +++ b/gui/wxpython/animation/dialogs.py @@ -1399,7 +1399,7 @@ def OnSelectionChanged(self, event): self._hideAll() return cdata = self.listbox.GetClientData(index) - self.hidevbox.Show(self.fontBox, (cdata["name"] in ("time", "text"))) + self.hidevbox.Show(self.fontBox, (cdata["name"] in {"time", "text"})) self.hidevbox.Show(self.imageBox, (cdata["name"] == "image")) self.hidevbox.Show(self.textBox, (cdata["name"] == "text")) self.hidevbox.Show(self.posBox, True) @@ -1409,7 +1409,7 @@ def OnSelectionChanged(self, event): self.spinY.SetValue(cdata["pos"][1]) if cdata["name"] == "image": self.browse.SetValue(cdata["file"]) - elif cdata["name"] in ("time", "text"): + elif cdata["name"] in {"time", "text"}: self.sampleLabel.SetFont(cdata["font"]) if cdata["name"] == "text": self.textCtrl.SetValue(cdata["text"]) @@ -1750,7 +1750,7 @@ def _setType(self, typeName=None): if typeName: self.tchoice.SetStringSelection(self._types[typeName]) self.tselect.SetType(typeName) - if typeName in ("strds", "stvds", "str3ds"): + if typeName in {"strds", "stvds", "str3ds"}: self.tselect.SetType(typeName, multiple=False) self.addManyMapsButton.Disable() else: @@ -1760,7 +1760,7 @@ def _setType(self, typeName=None): self.tselect.SetValue("") else: typeName = self.tchoice.GetClientData(self.tchoice.GetSelection()) - if typeName in ("strds", "stvds", "str3ds"): + if typeName in {"strds", "stvds", "str3ds"}: self.tselect.SetType(typeName, multiple=False) self.addManyMapsButton.Disable() else: @@ -1773,14 +1773,14 @@ def _setType(self, typeName=None): def _createDefaultCommand(self): cmd = [] - if self._mapType in ("raster", "strds"): + if self._mapType in {"raster", "strds"}: cmd.append("d.rast") - elif self._mapType in ("vector", "stvds"): + elif self._mapType in {"vector", "stvds"}: cmd.append("d.vect") - elif self._mapType in ("raster_3d", "str3ds"): + elif self._mapType in {"raster_3d", "str3ds"}: cmd.append("d.rast3d") if self._name: - if self._mapType in ("raster", "vector", "raster_3d"): + if self._mapType in {"raster", "vector", "raster_3d"}: cmd.append("map={name}".format(name=self._name.split(",")[0])) else: try: diff --git a/gui/wxpython/core/gcmd.py b/gui/wxpython/core/gcmd.py index 993a1edb6b7..5d845193938 100644 --- a/gui/wxpython/core/gcmd.py +++ b/gui/wxpython/core/gcmd.py @@ -222,7 +222,7 @@ def send(self, input): except ValueError: return self._close("stdin") except (pywintypes.error, Exception) as why: - if why.winerror in (109, errno.ESHUTDOWN): + if why.winerror in {109, errno.ESHUTDOWN}: return self._close("stdin") raise @@ -244,7 +244,7 @@ def _recv(self, which, maxsize): except ValueError: return self._close(which) except (pywintypes.error, Exception) as why: - if why.winerror in (109, errno.ESHUTDOWN): + if why.winerror in {109, errno.ESHUTDOWN}: return self._close(which) raise diff --git a/gui/wxpython/core/gconsole.py b/gui/wxpython/core/gconsole.py index 4705d639297..ff566cdef88 100644 --- a/gui/wxpython/core/gconsole.py +++ b/gui/wxpython/core/gconsole.py @@ -661,7 +661,7 @@ def RunCmd( return skipInterface = True - if os.path.splitext(command[0])[1] in (".py", ".sh"): + if os.path.splitext(command[0])[1] in {".py", ".sh"}: try: with open(command[0], "r") as sfile: for line in sfile.readlines(): @@ -805,14 +805,14 @@ def OnCmdDone(self, event): name = task.get_name() for p in task.get_options()["params"]: prompt = p.get("prompt", "") - if prompt in ("raster", "vector", "raster_3d") and p.get("value", None): - if p.get("age", "old") == "new" or name in ( + if prompt in {"raster", "vector", "raster_3d"} and p.get("value", None): + if p.get("age", "old") == "new" or name in { "r.colors", "r3.colors", "v.colors", "v.proj", "r.proj", - ): + }: # if multiple maps (e.g. r.series.interp), we need add each if p.get("multiple", False): lnames = p.get("value").split(",") diff --git a/gui/wxpython/core/globalvar.py b/gui/wxpython/core/globalvar.py index 08d61b6b3d7..f7fdfea4e36 100644 --- a/gui/wxpython/core/globalvar.py +++ b/gui/wxpython/core/globalvar.py @@ -167,7 +167,7 @@ def CheckForWx(): # use UBUNTU_MENUPROXY=0 to disable global menu on ubuntu but in the same time # to get smaller lmgr # [1] https://wiki.ubuntu.com/DesktopExperienceTeam/ApplicationMenu#Troubleshooting -if sys.platform in ("win32", "darwin") or os.environ.get("UBUNTU_MENUPROXY"): +if sys.platform in {"win32", "darwin"} or os.environ.get("UBUNTU_MENUPROXY"): GM_WINDOW_SIZE = (GM_WINDOW_MIN_SIZE[0], 600) else: GM_WINDOW_SIZE = (625, 600) @@ -213,12 +213,12 @@ def UpdateGRASSAddOnCommands(eList=None): os.environ["PATH"] = path + os.pathsep + os.environ["PATH"] for fname in os.listdir(path): - if fname in ["docs", "modules.xml"]: + if fname in {"docs", "modules.xml"}: continue if grassScripts: # win32 name, ext = os.path.splitext(fname) if name not in grassCmd: - if ext not in [BIN_EXT, SCT_EXT]: + if ext not in {BIN_EXT, SCT_EXT}: continue if name not in grassCmd: grassCmd.add(name) diff --git a/gui/wxpython/core/menutree.py b/gui/wxpython/core/menutree.py index 2b583b5581d..c0c2770c124 100644 --- a/gui/wxpython/core/menutree.py +++ b/gui/wxpython/core/menutree.py @@ -242,9 +242,9 @@ def collectParents(node, parents): menu = "manager" for arg in sys.argv: - if arg in ("strings", "tree", "commands", "dump"): + if arg in {"strings", "tree", "commands", "dump"}: action = arg - elif arg in ("manager", "module_tree", "modeler", "psmap"): + elif arg in {"manager", "module_tree", "modeler", "psmap"}: menu = arg # FIXME: cross-dependencies diff --git a/gui/wxpython/core/render.py b/gui/wxpython/core/render.py index ab7f7ebd9a5..4f8479aaca7 100644 --- a/gui/wxpython/core/render.py +++ b/gui/wxpython/core/render.py @@ -368,7 +368,7 @@ class MapLayer(Layer): def __init__(self, *args, **kwargs): """Represents map layer in the map canvas""" Layer.__init__(self, *args, **kwargs) - if self.type in ("vector", "thememap"): + if self.type in {"vector", "thememap"}: self._legrow = get_tempfile_name(suffix=".legrow", create=True) else: self._legrow = "" @@ -439,7 +439,7 @@ def Render(self, cmd, env): env_cmd = env.copy() env_cmd.update(self._render_env) env_cmd["GRASS_RENDER_FILE"] = self.layer.mapfile - if self.layer.GetType() in ("vector", "thememap"): + if self.layer.GetType() in {"vector", "thememap"}: if not self.layer._legrow: self.layer._legrow = grass.tempfile(create=True) if os.path.isfile(self.layer._legrow): @@ -721,7 +721,7 @@ def OnRenderDone(self, env): new_legend = [] with open(self.Map.legfile, "w") as outfile: for layer in reversed(self.layers): - if layer.GetType() not in ("vector", "thememap"): + if layer.GetType() not in {"vector", "thememap"}: continue if os.path.isfile(layer._legrow) and not layer.hidden: @@ -883,7 +883,7 @@ def _projInfo(self): for line in ret.splitlines(): if ":" in line: key, val = map(lambda x: x.strip(), line.split(":", 1)) - if key in ["units"]: + if key in {"units"}: val = val.lower() projinfo[key] = val elif "XY location (unprojected)" in line: @@ -1452,7 +1452,7 @@ def DeleteLayer(self, layer, overlay=False): for f in glob.glob(basefile): os.remove(f) - if layer.GetType() in ("vector", "thememap"): + if layer.GetType() in {"vector", "thememap"}: if os.path.isfile(layer._legrow): os.remove(layer._legrow) diff --git a/gui/wxpython/core/settings.py b/gui/wxpython/core/settings.py index ea3c2c1c738..0a6d11256d7 100644 --- a/gui/wxpython/core/settings.py +++ b/gui/wxpython/core/settings.py @@ -73,7 +73,7 @@ def colorhex2tuple(hexcode): return tuple(int(hexcode[i : i + 2], 16) for i in range(0, len(hexcode), 2)) for k, v in obj.items(): - if isinstance(v, str) and v.startswith("#") and len(v) in [7, 9]: + if isinstance(v, str) and v.startswith("#") and len(v) in {7, 9}: obj[k] = colorhex2tuple(v) return obj diff --git a/gui/wxpython/core/treemodel.py b/gui/wxpython/core/treemodel.py index 4191f0cc6c8..4c5de36f701 100644 --- a/gui/wxpython/core/treemodel.py +++ b/gui/wxpython/core/treemodel.py @@ -310,7 +310,7 @@ def match(self, key, value, case_sensitive=False): keys = key for key in keys: - if key not in ("command", "keywords", "description"): + if key not in {"command", "keywords", "description"}: return False try: text = self.data[key] diff --git a/gui/wxpython/core/utils.py b/gui/wxpython/core/utils.py index 9c42a77487e..86d73b1e954 100644 --- a/gui/wxpython/core/utils.py +++ b/gui/wxpython/core/utils.py @@ -121,7 +121,7 @@ def GetLayerNameFromCmd(dcmd, fullyQualified=False, param=None, layerType=None): break # this does not use types, just some (incomplete subset of?) names - if p in ( + if p in { "map", "input", "layer", @@ -133,7 +133,7 @@ def GetLayerNameFromCmd(dcmd, fullyQualified=False, param=None, layerType=None): "intensity", "shade", "labels", - ): + }: params.append((idx, p, v)) if len(params) < 1: @@ -161,9 +161,9 @@ def GetLayerNameFromCmd(dcmd, fullyQualified=False, param=None, layerType=None): mapname = v mapset = "" if fullyQualified and "@" not in mapname: - if layerType in ("raster", "vector", "raster_3d", "rgb", "his"): + if layerType in {"raster", "vector", "raster_3d", "rgb", "his"}: try: - if layerType in ("raster", "rgb", "his"): + if layerType in {"raster", "rgb", "his"}: findType = "cell" elif layerType == "raster_3d": findType = "grid3" @@ -477,11 +477,11 @@ def __ll_parts(value, reverse=False, precision=3): except ValueError: raise ValueError - if hs not in ("N", "S", "E", "W"): + if hs not in {"N", "S", "E", "W"}: raise ValueError coef = 1.0 - if hs in ("S", "W"): + if hs in {"S", "W"}: coef = -1.0 fm = int(m) / 60.0 @@ -552,7 +552,7 @@ def ReprojectCoordinates(coord, projOut, projIn=None, flags=""): proj = projOut.split(" ")[0].split("=")[1] except IndexError: proj = "" - if proj in ("ll", "latlong", "longlat") and "d" not in flags: + if proj in {"ll", "latlong", "longlat"} and "d" not in flags: return (proj, (e, n)) else: try: @@ -666,9 +666,9 @@ def _parseFormats(output, writableOnly=False): if writableOnly and not patt.search(key): continue - if name in ("Memory", "Virtual Raster", "In Memory Raster"): + if name in {"Memory", "Virtual Raster", "In Memory Raster"}: continue - if name in ( + if name in { "PostgreSQL", "PostgreSQL/PostGIS", "SQLite", @@ -681,16 +681,16 @@ def _parseFormats(output, writableOnly=False): "CouchDB", "MSSQLSpatial", "FileGDB", - ): + }: formats["database"][key.split(" ")[0]] = name - elif name in ( + elif name in { "GeoJSON", "OGC Web Coverage Service", "OGC Web Map Service", "WFS", "GeoRSS", "HTTP Fetching Wrapper", - ): + }: formats["protocol"][key.split(" ")[0]] = name else: formats["file"][key.split(" ")[0]] = name diff --git a/gui/wxpython/core/workspace.py b/gui/wxpython/core/workspace.py index 6ff1345c884..b9efe693951 100644 --- a/gui/wxpython/core/workspace.py +++ b/gui/wxpython/core/workspace.py @@ -1102,7 +1102,7 @@ def __writeLayer(self, mapTree, item): self.file.write( '%s\n' % (" " * self.indent, f) ) - elif val in (True, False): + elif val in {True, False}: self.file.write( '%s\n' % (" " * self.indent, key) ) @@ -1369,7 +1369,7 @@ def __writeNvizVolume(self, data): self.indent += 4 self.file.write("%s<%s>\n" % (" " * self.indent, name)) for att in slice_[name].keys(): - if att in ("map", "update"): + if att in {"map", "update"}: continue val = slice_[name][att] self.indent += 4 @@ -1382,7 +1382,7 @@ def __writeNvizVolume(self, data): self.file.write("%s\n" % (" " * self.indent, name)) self.indent -= 4 self.file.write("%s\n" % (" " * self.indent, attrb)) - if attrb not in ("attribute", "isosurface", "slice"): + if attrb not in {"attribute", "isosurface", "slice"}: # end tag self.file.write("%s\n" % (" " * self.indent, attrb)) @@ -1417,7 +1417,7 @@ def __writeNvizVector(self, data): ) self.indent += 4 for name in data[attrb].keys(): - if name in ("object", "marker"): + if name in {"object", "marker"}: continue if name == "mode": self.file.write( @@ -1871,7 +1871,7 @@ def process_line(self, line, line_id): elif ( element - in ( + in { "display_shape", "display_cat", "display_topo", @@ -1883,7 +1883,7 @@ def process_line(self, line, line_id): "type_centroid", "type_area", "type_face", - ) + } and self.inVector ): if int(self._get_value(line)) == 1: @@ -1895,7 +1895,7 @@ def process_line(self, line, line_id): else: self.layers[-1]["cmd"][paramId] += ",%s" % type - elif element in ("color", "fcolor", "lcolor") and self.inVector: + elif element in {"color", "fcolor", "lcolor"} and self.inVector: value = self._get_value(line) if value != "": self.layers[-1]["cmd"].append( @@ -1912,7 +1912,7 @@ def process_line(self, line, line_id): elif ( element - in ( + in { "icon", "size", "layer", @@ -1922,7 +1922,7 @@ def process_line(self, line, line_id): "where", "minreg", "maxreg", - ) + } and self.inVector ): value = self._get_value(line) @@ -1985,7 +1985,7 @@ def process_line(self, line, line_id): "textcolor=%s" % self._color_name_to_rgb(value) ) - elif element in ("gridsize", "gridorigin"): + elif element in {"gridsize", "gridorigin"}: value = self._get_value(line) if value != "": self.layers[-1]["cmd"].append("%s=%s" % (element[4:], value)) diff --git a/gui/wxpython/core/ws.py b/gui/wxpython/core/ws.py index f705962f9b2..156cd0166f9 100644 --- a/gui/wxpython/core/ws.py +++ b/gui/wxpython/core/ws.py @@ -215,10 +215,10 @@ def _getRegionDict(self, env): if len(r) < 2: continue try: - if r[0] in ["e-w resol3", "n-s resol3", "rows3", "cols3", "depths"]: + if r[0] in {"e-w resol3", "n-s resol3", "rows3", "cols3", "depths"}: # ignore 3D region values (causing problems in latlong locations) continue - if r[0] in ["cols", "rows", "zone", "proj"]: + if r[0] in {"cols", "rows", "zone", "proj"}: region[r[0]] = int(r[1]) else: region[r[0]] = float(r[1]) diff --git a/gui/wxpython/datacatalog/catalog.py b/gui/wxpython/datacatalog/catalog.py index 4c2c1712433..880190b0943 100644 --- a/gui/wxpython/datacatalog/catalog.py +++ b/gui/wxpython/datacatalog/catalog.py @@ -98,7 +98,7 @@ def __init__( # get reason why last used mapset is not usable last_mapset_path = gisenv()["LAST_MAPSET_PATH"] self.reason_id = get_reason_id_mapset_not_usable(last_mapset_path) - if self.reason_id in ("non-existent", "invalid", "different-owner"): + if self.reason_id in {"non-existent", "invalid", "different-owner"}: # show non-standard situation info wx.CallLater(delay, self.showFallbackSessionInfo) elif self.reason_id == "locked": diff --git a/gui/wxpython/datacatalog/tree.py b/gui/wxpython/datacatalog/tree.py index be15db70e32..c1e97579734 100644 --- a/gui/wxpython/datacatalog/tree.py +++ b/gui/wxpython/datacatalog/tree.py @@ -786,7 +786,7 @@ def DefineItems(self, selected): mixed = [] for item in selected: type = item.data["type"] - if type in ("raster", "raster_3d", "vector"): + if type in {"raster", "raster_3d", "vector"}: self.selected_layer.append(item) self.selected_mapset.append(item.parent) self.selected_location.append(item.parent.parent) @@ -901,7 +901,7 @@ def OnDoubleClick(self, node): if node.data["type"] == "mapset" and not node.children: self._reloadMapsetNode(node) self.RefreshNode(node, recursive=True) - if node.data["type"] in ("mapset", "location", "grassdb"): + if node.data["type"] in {"mapset", "location", "grassdb"}: # expand/collapse location/mapset... if self.IsNodeExpanded(node): self.CollapseNode(node, recursive=False) @@ -967,12 +967,12 @@ def OnGetItemFont(self, index): Used to highlight current db/loc/mapset.""" node = self._model.GetNodeByIndex(index) font = self.GetFont() - if node.data["type"] in ("grassdb", "location", "mapset"): - if node in ( + if node.data["type"] in {"grassdb", "location", "mapset"}: + if node in { self.current_grassdb_node, self.current_location_node, self.current_mapset_node, - ): + }: font.SetWeight(wx.FONTWEIGHT_BOLD) else: font.SetWeight(wx.FONTWEIGHT_NORMAL) @@ -1156,7 +1156,7 @@ def OnStartEditLabel(self, node, event): self.selected_location[0].data["name"], ): event.Veto() - elif node.data["type"] in ("raster", "raster_3d", "vector"): + elif node.data["type"] in {"raster", "raster_3d", "vector"}: currentGrassDb, currentLocation, currentMapset = self._isCurrent(gisenv()) if not currentMapset: event.Veto() @@ -1170,7 +1170,7 @@ def OnEditLabel(self, node, event): Debug.msg(1, "End label edit {name}".format(name=old_name)) new_name = event.GetLabel() - if node.data["type"] in ("raster", "raster_3d", "vector"): + if node.data["type"] in {"raster", "raster_3d", "vector"}: self.Rename(old_name, new_name) elif node.data["type"] == "mapset": @@ -1810,7 +1810,7 @@ def _updateAfterGrassdbChanged( node = self.GetDbNode(grassdb=grassdb) if node: self.RemoveGrassDB(node) - elif element in ("raster", "vector", "raster_3d"): + elif element in {"raster", "vector", "raster_3d"}: # when watchdog is used, it watches current mapset, # so we don't process any signals here, # instead the watchdog handler takes care of refreshing tree diff --git a/gui/wxpython/dbmgr/sqlbuilder.py b/gui/wxpython/dbmgr/sqlbuilder.py index eaada441652..44542c90ded 100644 --- a/gui/wxpython/dbmgr/sqlbuilder.py +++ b/gui/wxpython/dbmgr/sqlbuilder.py @@ -382,7 +382,7 @@ def OnUniqueValues(self, event, justsample=False): i = 0 items = [] for item in data: # sorted(set(map(lambda x: desc['ctype'](x[0]), data))): - if desc["type"] not in ("character", "text"): + if desc["type"] not in {"character", "text"}: items.append(str(item[0])) else: items.append("'{}'".format(GetUnicodeValue(item[0]))) @@ -605,7 +605,7 @@ def _add(self, element, value): curspos = self.text_sql.GetLastPosition() + len(newsqlstr) newsqlstr = sqlstr + newsqlstr - elif element in ["value", "mark"]: + elif element in {"value", "mark"}: addstr = " " + value + " " newsqlstr = sqlstr[:curspos] + addstr + sqlstr[curspos:] curspos += len(addstr) @@ -820,7 +820,7 @@ def _add(self, element, value): curspos = self.text_sql.GetInsertionPoint() newsqlstr = "" - if element in ["value", "mark", "func"] or ( + if element in {"value", "mark", "func"} or ( element == "column" and self.mode.GetSelection() == 2 ): addstr = " " + value + " " @@ -925,7 +925,7 @@ def _add(self, element, value): if __name__ == "__main__": - if len(sys.argv) not in [3, 4]: + if len(sys.argv) not in {3, 4}: print(__doc__, file=sys.stderr) sys.exit() diff --git a/gui/wxpython/gmodeler/canvas.py b/gui/wxpython/gmodeler/canvas.py index 51976e443ad..eeaa6909731 100644 --- a/gui/wxpython/gmodeler/canvas.py +++ b/gui/wxpython/gmodeler/canvas.py @@ -197,7 +197,7 @@ def OnProperties(self, event=None): ) elif isinstance(shape, ModelData): - if shape.GetPrompt() in ( + if shape.GetPrompt() in { "raster", "vector", "raster_3d", @@ -205,7 +205,7 @@ def OnProperties(self, event=None): "strds", "stvds", "str3ds", - ): + }: dlg = ModelDataDialog(parent=self.frame, shape=shape) shape.SetPropDialog(dlg) dlg.CentreOnParent() diff --git a/gui/wxpython/gmodeler/model.py b/gui/wxpython/gmodeler/model.py index fc4db81e1f3..0c162fe0038 100644 --- a/gui/wxpython/gmodeler/model.py +++ b/gui/wxpython/gmodeler/model.py @@ -838,7 +838,7 @@ def Parameterize(self): result["variables"] = {"flags": list(), "params": params, "idx": idx} for name, values in self.variables.items(): gtype = values.get("type", "string") - if gtype in ("raster", "vector", "mapset", "file", "region", "dir"): + if gtype in {"raster", "vector", "mapset", "file", "region", "dir"}: gisprompt = True prompt = gtype if gtype == "raster": @@ -1487,15 +1487,15 @@ def SetPropDialog(self, win): def _getBrush(self): """Get brush""" - if self.prompt in ("raster", "strds"): + if self.prompt in {"raster", "strds"}: color = UserSettings.Get( group="modeler", key="data", subkey=("color", "raster") ) - elif self.prompt in ("raster_3d", "str3ds"): + elif self.prompt in {"raster_3d", "str3ds"}: color = UserSettings.Get( group="modeler", key="data", subkey=("color", "raster3d") ) - elif self.prompt in ("vector", "stvds"): + elif self.prompt in {"vector", "stvds"}: color = UserSettings.Get( group="modeler", key="data", subkey=("color", "vector") ) @@ -1926,7 +1926,7 @@ def SetItems(self, items, branch="if"): :param items: list of items :param branch: 'if' / 'else' """ - if branch in ["if", "else"]: + if branch in {"if", "else"}: self.itemIds[branch] = items @@ -3289,7 +3289,7 @@ def _getParamDesc(self, param): def _getParamValue(self, param): if param["value"] and "output" not in param["name"]: - if param["type"] in ["float", "integer"]: + if param["type"] in {"float", "integer"}: value = param["value"] else: value = '"{}"'.format(param["value"]) diff --git a/gui/wxpython/gmodeler/panels.py b/gui/wxpython/gmodeler/panels.py index b198dace36a..75e3654bd62 100644 --- a/gui/wxpython/gmodeler/panels.py +++ b/gui/wxpython/gmodeler/panels.py @@ -455,7 +455,7 @@ def GetOptData(self, dcmd, layer, params, propwin): y = layer.GetY() for p in params["params"]: - if p.get("prompt", "") not in ( + if p.get("prompt", "") not in { "raster", "vector", "raster_3d", @@ -464,7 +464,7 @@ def GetOptData(self, dcmd, layer, params, propwin): "strds", "stvds", "str3ds", - ): + }: continue # add new data item if defined or required @@ -1844,7 +1844,7 @@ def OnChangeScriptType(self, event): if self.body.script_type == "Python": self.btnRun.Enable() self.btnRun.SetToolTip(_("Run script")) - elif self.body.script_type in ("PyWPS", "actinia"): + elif self.body.script_type in {"PyWPS", "actinia"}: self.btnRun.Disable() self.btnRun.SetToolTip( _("Run script - enabled only for basic Python scripts") diff --git a/gui/wxpython/gui_core/forms.py b/gui/wxpython/gui_core/forms.py index 937b31321a9..96fad876eba 100644 --- a/gui/wxpython/gui_core/forms.py +++ b/gui/wxpython/gui_core/forms.py @@ -170,7 +170,7 @@ def run(self): prompt = p.get("element", "") if prompt == "vector": name = p.get("name", "") - if name in ("map", "input"): + if name in {"map", "input"}: self.eventId = p["wxId"][0] if self.eventId is None: return @@ -254,7 +254,7 @@ def run(self): map = layer = None driver = db = None - if name in ("LayerSelect", "ColumnSelect", "SqlWhereSelect"): + if name in {"LayerSelect", "ColumnSelect", "SqlWhereSelect"}: if p.get("element", "") == "vector": # -> vector # get map name map = p.get("value", "") @@ -265,7 +265,7 @@ def run(self): if not p: continue - if p.get("element", "") in ["layer", "layer_all"]: + if p.get("element", "") in {"layer", "layer_all"}: layer = p.get("value", "") if layer != "": layer = p.get("value", "") @@ -273,7 +273,7 @@ def run(self): layer = p.get("default", "") break - elif p.get("element", "") in ["layer", "layer_all"]: # -> layer + elif p.get("element", "") in {"layer", "layer_all"}: # -> layer # get layer layer = p.get("value", "") if layer != "": @@ -665,11 +665,11 @@ def __init__( if self._giface and self._giface.GetLayerTree(): addLayer = False for p in self.task.params: - if p.get("age", "old") == "new" and p.get("prompt", "") in ( + if p.get("age", "old") == "new" and p.get("prompt", "") in { "raster", "vector", "raster_3d", - ): + }: addLayer = True if addLayer: @@ -917,21 +917,21 @@ def OnCancel(self, event): if ( self.get_dcmd and self.parent - and self.parent.GetName() in ("LayerTree", "MapWindow") + and self.parent.GetName() in {"LayerTree", "MapWindow"} ): Debug.msg(1, "TaskFrame.OnCancel(): known parent") # display decorations and # pressing OK or cancel after setting layer properties if ( self.task.name - in [ + in { "d.barscale", "d.legend", "d.northarrow", "d.histogram", "d.text", "d.legend.vect", - ] + } or len(self.parent.GetLayerInfo(self.layer, key="cmd")) >= 1 ): # TODO: do this through policy @@ -1109,7 +1109,7 @@ def __init__(self, parent, giface, task, id=wx.ID_ANY, frame=None, *args, **kwar parChk.Bind(wx.EVT_CHECKBOX, self.OnSetValue) which_sizer.Add(parChk, proportion=0, flag=wx.LEFT, border=20) - if f["name"] in ("verbose", "quiet"): + if f["name"] in {"verbose", "quiet"}: chk.Bind(wx.EVT_CHECKBOX, self.OnVerbosity) vq = UserSettings.Get(group="cmd", key="verbosity", subkey="selection") if f["name"] == vq: @@ -1301,7 +1301,7 @@ def __init__(self, parent, giface, task, id=wx.ID_ANY, frame=None, *args, **kwar title_txt.SetLabel(title + ":") value = self._getValue(p) - if p["name"] in ("icon", "icon_area", "icon_line"): # symbols + if p["name"] in {"icon", "icon_area", "icon_line"}: # symbols bitmap = wx.Bitmap( os.path.join(globalvar.SYMBDIR, value) + ".png" ) @@ -1363,7 +1363,7 @@ def __init__(self, parent, giface, task, id=wx.ID_ANY, frame=None, *args, **kwar # text entry if ( - p.get("type", "string") in ("string", "integer", "float") + p.get("type", "string") in {"string", "integer", "float"} and len(p.get("values", [])) == 0 and p.get("gisprompt", False) is False and p.get("prompt", "") != "color" @@ -1446,7 +1446,7 @@ def __init__(self, parent, giface, task, id=wx.ID_ANY, frame=None, *args, **kwar if p.get("gisprompt", False): title_txt.SetLabel(title + ":") # GIS element entry - if p.get("prompt", "") not in ( + if p.get("prompt", "") not in { "color", "cat", "cats", @@ -1471,7 +1471,7 @@ def __init__(self, parent, giface, task, id=wx.ID_ANY, frame=None, *args, **kwar "datasource", "datasource_layer", "sql_query", - ): + }: multiple = p.get("multiple", False) if p.get("age", "") == "new": mapsets = [ @@ -1480,7 +1480,7 @@ def __init__(self, parent, giface, task, id=wx.ID_ANY, frame=None, *args, **kwar else: mapsets = None if ( - self.task.name in ("r.proj", "v.proj") + self.task.name in {"r.proj", "v.proj"} and p.get("name", "") == "input" ): selection = gselect.ProjSelect( @@ -1494,7 +1494,7 @@ def __init__(self, parent, giface, task, id=wx.ID_ANY, frame=None, *args, **kwar else: elem = p.get("element", None) # hack for t.* modules - if elem in ("stds", "map"): + if elem in {"stds", "map"}: orig_elem = elem type_param = self.task.get_param( "type", element="name", raiseError=False @@ -1655,7 +1655,7 @@ def __init__(self, parent, giface, task, id=wx.ID_ANY, frame=None, *args, **kwar border=5, ) else: - if prompt in ("stds", "strds", "stvds", "str3ds"): + if prompt in {"stds", "strds", "stvds", "str3ds"}: showButton = True try: # if matplotlib is there @@ -1800,7 +1800,7 @@ def __init__(self, parent, giface, task, id=wx.ID_ANY, frame=None, *args, **kwar ) # layer, dbdriver, dbname, dbcolumn, dbtable entry - elif prompt in ( + elif prompt in { "dbdriver", "dbname", "dbtable", @@ -1809,7 +1809,7 @@ def __init__(self, parent, giface, task, id=wx.ID_ANY, frame=None, *args, **kwar "location", "mapset", "dbase", - ): + }: if p.get("multiple", "no") == "yes": win = TextCtrl( parent=which_panel, @@ -2195,7 +2195,7 @@ def __init__(self, parent, giface, task, id=wx.ID_ANY, frame=None, *args, **kwar border=5, ) - elif prompt in ("cat", "cats"): + elif prompt in {"cat", "cats"}: # interactive selection of vector categories if layer # manager is accessible if self._giface: @@ -2224,7 +2224,7 @@ def __init__(self, parent, giface, task, id=wx.ID_ANY, frame=None, *args, **kwar border=5, ) - elif prompt in ("colortable", "barscale", "northarrow"): + elif prompt in {"colortable", "barscale", "northarrow"}: if prompt == "colortable": cb = ColorTablesComboBox( parent=which_panel, @@ -2411,9 +2411,9 @@ def OnCheckItem(index=None, flag=None, event=None): continue prompt = p.get("prompt", "") - if prompt in ("raster", "vector"): + if prompt in {"raster", "vector"}: name = p.get("name", "") - if name in ("map", "input"): + if name in {"map", "input"}: pMap = p elif prompt == "layer": pLayer.append(p) @@ -3231,14 +3231,14 @@ def GetCommandInputMapParamKey(self, cmd): self.grass_task = gtask.processTask(tree).get_task() for p in self.grass_task.params: - if p.get("name", "") in ("input", "map"): + if p.get("name", "") in {"input", "map"}: age = p.get("age", "") prompt = p.get("prompt", "") element = p.get("element", "") if ( age == "old" - and element in ("cell", "grid3", "vector") - and prompt in ("raster", "raster_3d", "vector") + and element in {"cell", "grid3", "vector"} + and prompt in {"raster", "raster_3d", "vector"} ): return p.get("name", None) return None diff --git a/gui/wxpython/gui_core/goutput.py b/gui/wxpython/gui_core/goutput.py index 705da4689cc..b26f6611f3d 100644 --- a/gui/wxpython/gui_core/goutput.py +++ b/gui/wxpython/gui_core/goutput.py @@ -392,7 +392,7 @@ def OnCmdOutput(self, event): self.cmdOutput.AddStyledMessage(message, type) - if event.type in ("warning", "error"): + if event.type in {"warning", "error"}: self.contentChanged.emit(notification=Notification.MAKE_VISIBLE) else: self.contentChanged.emit(notification=Notification.HIGHLIGHT) diff --git a/gui/wxpython/gui_core/gselect.py b/gui/wxpython/gui_core/gselect.py index ec64f51f8a3..2a8c8547657 100644 --- a/gui/wxpython/gui_core/gselect.py +++ b/gui/wxpython/gui_core/gselect.py @@ -525,7 +525,7 @@ def _getElementList(self, element, mapsets=None, elements=None, exclude=False): else: renamed_elements.append(elementdict[elem]) - if element in ("stds", "strds", "str3ds", "stvds"): + if element in {"stds", "strds", "str3ds", "stvds"}: if not self.tgis_error: import grass.temporal as tgis @@ -603,7 +603,7 @@ def _getElementList(self, element, mapsets=None, elements=None, exclude=False): collapse = True if sel == 0: # collapse all except PERMANENT and current - if mapset in ("PERMANENT", curr_mapset): + if mapset in {"PERMANENT", curr_mapset}: collapse = False elif sel == 1: # collapse all except PERMANENT if mapset == "PERMANENT": @@ -776,7 +776,7 @@ def SetData(self, **kargs): ListCtrlComboPopup.SetData(self, **kargs) if "type" in kargs: self.type = kargs["type"] - if self.type in ("stds", "strds", "str3ds", "stvds"): + if self.type in {"stds", "strds", "str3ds", "stvds"}: # Initiate the temporal framework. Catch database error # and set the error flag for the stds listing. try: @@ -1541,7 +1541,7 @@ def __init__( } for name, ext in sorted(extList.items()): - if name in ("ESRI Shapefile", "GeoTIFF"): + if name in {"ESRI Shapefile", "GeoTIFF"}: continue fileMask += "%(name)s (*.%(low)s;*.%(up)s)|*.%(low)s;*.%(up)s|" % { "name": name, @@ -1726,7 +1726,7 @@ def _postInit(self, sourceType, data): break optList = list() for k, v in data.items(): - if k in ("format", "conninfo", "topology"): + if k in {"format", "conninfo", "topology"}: continue optList.append("%s=%s" % (k, v)) options = ",".join(optList) @@ -2097,11 +2097,11 @@ def _getCurrentDbWidgetName(self): def GetDsn(self): """Get datasource name""" if self._sourceType == "db": - if self.dbWidgets["format"].GetStringSelection() in ( + if self.dbWidgets["format"].GetStringSelection() in { "PostgreSQL", "PostgreSQL/PostGIS", "PostGIS Raster driver", - ): + }: ret = RunCommand("db.login", read=True, quiet=True, flags="p") message = _( "PostgreSQL/PostGIS login was not set." @@ -2157,7 +2157,7 @@ def GetDsn(self): dsn = "/vsizip/" + dsn elif ext == ".gzip": dsn = "/vsigzip/" + dsn - elif ext in (".tar", ".tar.gz", ".tgz"): + elif ext in {".tar", ".tar.gz", ".tgz"}: dsn = "/vsitar/" + dsn return dsn @@ -2165,22 +2165,22 @@ def GetDsn(self): def SetDatabase(self, db): """Update database panel.""" sizer = self.dbPanel.GetSizer() - showBrowse = db in ("SQLite", "SQLite / Spatialite", "Rasterlite") + showBrowse = db in {"SQLite", "SQLite / Spatialite", "Rasterlite"} showDirbrowse = db in ("FileGDB") - showChoice = db in ( + showChoice = db in { "PostgreSQL", "PostgreSQL/PostGIS", "PostGIS WKT Raster driver", "PostGIS Raster driver", - ) + } enableFeatType = ( self.dest and self.ogr and db - in ( + in { "PostgreSQL", "PostgreSQL/PostGIS", - ) + } ) showText = not (showBrowse or showChoice or showDirbrowse) diff --git a/gui/wxpython/gui_core/preferences.py b/gui/wxpython/gui_core/preferences.py index 98a9803e20b..5b91d3f37e1 100644 --- a/gui/wxpython/gui_core/preferences.py +++ b/gui/wxpython/gui_core/preferences.py @@ -1137,10 +1137,10 @@ def _createDisplayPage(self, notebook): # # see initialization of nviz GLWindow - if globalvar.CheckWxVersion(version=[2, 8, 11]) and sys.platform not in ( + if globalvar.CheckWxVersion(version=[2, 8, 11]) and sys.platform not in { "win32", "darwin", - ): + }: box = StaticBox( parent=panel, id=wx.ID_ANY, diff --git a/gui/wxpython/gui_core/widgets.py b/gui/wxpython/gui_core/widgets.py index 7630c6f03ad..0f0905e756c 100644 --- a/gui/wxpython/gui_core/widgets.py +++ b/gui/wxpython/gui_core/widgets.py @@ -538,7 +538,7 @@ def __init__(self, parent, usage, label, **kwargs): elif usage == "pause": self.DrawPause(dc, size) - if sys.platform not in ("win32", "darwin"): + if sys.platform not in {"win32", "darwin"}: buffer.SetMaskColour(maskColor) self.SetBitmapLabel(buffer) dc.SelectObject(wx.NullBitmap) diff --git a/gui/wxpython/history/browser.py b/gui/wxpython/history/browser.py index e1d1ce37efe..9bf5620d12f 100644 --- a/gui/wxpython/history/browser.py +++ b/gui/wxpython/history/browser.py @@ -63,7 +63,7 @@ def get_translated_value(key, value): return _("{} sec".format(value)) elif key == "status": return _(value.capitalize()) - elif key in ("mask2d", "mask3d"): + elif key in {"mask2d", "mask3d"}: return _(str(value)) diff --git a/gui/wxpython/iscatt/controllers.py b/gui/wxpython/iscatt/controllers.py index d823dfb6e1a..3ebfa1a875b 100644 --- a/gui/wxpython/iscatt/controllers.py +++ b/gui/wxpython/iscatt/controllers.py @@ -421,7 +421,7 @@ def ActivateSelectionPolygonMode(self, activate): if not scatt["scatt"]: continue scatt["scatt"].SetSelectionPolygonMode(activate) - if not activate and self.plot_mode not in ["zoom", "pan", "zoom_extend"]: + if not activate and self.plot_mode not in {"zoom", "pan", "zoom_extend"}: self.SetPlotsMode(None) self.render_mgr.RunningProcessDone() @@ -740,9 +740,9 @@ def SetCategoryAttrs(self, cat_id, attrs_dict): update_cat_rast = [] for k, v in attrs_dict.items(): - if not render and k in ["color", "opacity", "show", "nstd"]: + if not render and k in {"color", "opacity", "show", "nstd"}: render = True - if k in ["color", "name"]: + if k in {"color", "name"}: update_cat_rast.append(k) self.cats[cat_id][k] = v diff --git a/gui/wxpython/iscatt/core_c.py b/gui/wxpython/iscatt/core_c.py index 96fcb90cb5c..cda449b0dd5 100644 --- a/gui/wxpython/iscatt/core_c.py +++ b/gui/wxpython/iscatt/core_c.py @@ -195,7 +195,7 @@ def _regionToCellHead(region): } for k, v in region.items(): - if k in ["rows", "cols", "cells", "zone"]: # zone added in r65224 + if k in {"rows", "cols", "cells", "zone"}: # zone added in r65224 v = int(v) else: v = float(v) diff --git a/gui/wxpython/iscatt/iscatt_core.py b/gui/wxpython/iscatt/iscatt_core.py index 1c5b2887edb..caa2916acce 100644 --- a/gui/wxpython/iscatt/iscatt_core.py +++ b/gui/wxpython/iscatt/iscatt_core.py @@ -809,7 +809,7 @@ def _parseRegion(region_str): for param in region_str: k, v = param.split("=") - if k in ["rows", "cols", "cells"]: + if k in {"rows", "cols", "cells"}: v = int(v) else: v = float(v) @@ -837,7 +837,7 @@ def GetRasterInfo(rast): if v != "CELL": return None pass - elif k in ["rows", "cols", "cells", "min", "max"]: + elif k in {"rows", "cols", "cells", "min", "max"}: v = int(v) else: v = float(v) diff --git a/gui/wxpython/iscatt/toolbars.py b/gui/wxpython/iscatt/toolbars.py index 30d9869f861..7f813723282 100644 --- a/gui/wxpython/iscatt/toolbars.py +++ b/gui/wxpython/iscatt/toolbars.py @@ -150,7 +150,7 @@ def SetPloltsMode(self, event, tool_name): if event.IsChecked(): for i_tool_data in self.controller.data: i_tool_name = get_tool_name(i_tool_data[0]) - if not i_tool_name or i_tool_name in ["cats_mgr", "sel_pol_mode"]: + if not i_tool_name or i_tool_name in {"cats_mgr", "sel_pol_mode"}: continue if i_tool_name == tool_name: continue @@ -175,7 +175,7 @@ def ModeSet(self, mode): def UnsetMode(self): for i_tool_data in self.controller.data: i_tool_name = get_tool_name(i_tool_data[0]) - if not i_tool_name or i_tool_name in ["cats_mgr", "sel_pol_mode"]: + if not i_tool_name or i_tool_name in {"cats_mgr", "sel_pol_mode"}: continue i_tool_id = vars(self)[i_tool_name] self.ToggleTool(i_tool_id, False) @@ -309,7 +309,7 @@ def SetMode(self, event, tool_name): self.scatt_mgr.modeSet.connect(self.ModeSet) def ModeSet(self, mode): - if mode in ["zoom", "pan", "zoom_extend", None]: + if mode in {"zoom", "pan", "zoom_extend", None}: self.UnsetMode() def UnsetMode(self): diff --git a/gui/wxpython/lmgr/frame.py b/gui/wxpython/lmgr/frame.py index 536b3016911..2433f6676c6 100644 --- a/gui/wxpython/lmgr/frame.py +++ b/gui/wxpython/lmgr/frame.py @@ -1128,7 +1128,7 @@ def GetMenuCmd(self, event): # check list of dummy commands for GUI modules that do not have GRASS # bin modules or scripts. - if cmd in ["vcolors", "r.mapcalc", "r3.mapcalc"]: + if cmd in {"vcolors", "r.mapcalc", "r3.mapcalc"}: return cmdlist try: @@ -1420,7 +1420,7 @@ def write_help(): # use chdir or dialog if cmd and len(cmd) == 2: write_beginning(parameter=cmd[1]) - if cmd[1] in ["-h", "--h", "--help", "help"]: + if cmd[1] in {"-h", "--h", "--help", "help"}: write_help() write_end() return @@ -1985,7 +1985,7 @@ def OnMapCreated(self, name, ltype, add=None): def AddOrUpdateMap(self, mapName, ltype): """Add map layer or update""" # start new map display if no display is available - if ltype not in ["raster", "raster_3d", "vector"]: + if ltype not in {"raster", "raster_3d", "vector"}: GError(parent=self, message=_("Unsupported map layer type <%s>.") % ltype) return diff --git a/gui/wxpython/lmgr/layertree.py b/gui/wxpython/lmgr/layertree.py index 54a92ca8529..450dc9340e0 100644 --- a/gui/wxpython/lmgr/layertree.py +++ b/gui/wxpython/lmgr/layertree.py @@ -490,7 +490,7 @@ def OnLayerContextMenu(self, event): same = False break - if ltype not in ("group", "command"): + if ltype not in {"group", "command"}: if numSelected == 1: self.popupMenu.AppendSeparator() if not (ltype == "raster_3d" or self.mapdisplay.IsPaneShown("3d")): @@ -517,17 +517,17 @@ def OnLayerContextMenu(self, event): wx.EVT_MENU, self.OnPopupProperties, id=self.popupID["properties"] ) - if ltype in ( + if ltype in { "raster", "vector", "raster_3d", - ) and self.mapdisplay.IsPaneShown("3d"): + } and self.mapdisplay.IsPaneShown("3d"): self.popupMenu.Append(self.popupID["nviz"], _("3D view properties")) self.Bind( wx.EVT_MENU, self.OnNvizProperties, id=self.popupID["nviz"] ) - if same and ltype in ("raster", "vector", "rgb", "raster_3d"): + if same and ltype in {"raster", "vector", "rgb", "raster_3d"}: self.popupMenu.AppendSeparator() item = wx.MenuItem( self.popupMenu, @@ -1370,9 +1370,9 @@ def OnGrassDBChanged( gisenv = grass.gisenv() if not (gisenv["GISDBASE"] == grassdb and gisenv["LOCATION_NAME"] == location): return - if action not in ("delete", "rename"): + if action not in {"delete", "rename"}: return - if element in ("raster", "vector", "raster_3d"): + if element in {"raster", "vector", "raster_3d"}: name = map + "@" + mapset if "@" not in map else map items = self.FindItemByData(key="name", value=name) if items: @@ -1706,7 +1706,7 @@ def PropertiesDialog(self, layer, show=True): self.SetLayerInfo(layer, key="cmd", value=module.GetCmd()) elif self.GetLayerInfo(layer, key="type") != "command": cmd = [ltype2command[ltype]] - if ltype in ("raster", "rgb"): + if ltype in {"raster", "rgb"}: if UserSettings.Get( group="rasterLayer", key="opaque", subkey="enabled" ): @@ -1956,7 +1956,7 @@ def OnChangeSel(self, event): group="display", key="autoZooming", subkey="enabled" ): mapLayer = self.GetLayerInfo(layer, key="maplayer") - if mapLayer.GetType() in ("raster", "vector"): + if mapLayer.GetType() in {"raster", "vector"}: self.mapdisplay.MapWindow.ZoomToMap( layers=[ mapLayer, @@ -2183,7 +2183,7 @@ def GetOptData(self, dcmd, layer, params, propwin): ) ): mapLayer = self.GetLayerInfo(layer, key="maplayer") - if mapLayer.GetType() in ("raster", "vector"): + if mapLayer.GetType() in {"raster", "vector"}: self.mapdisplay.MapWindow.ZoomToMap( layers=[ mapLayer, @@ -2426,7 +2426,7 @@ def __FindSubItemByName(self, item, value): def _createCommandCtrl(self): """Creates text control for command layer""" height = 25 - if sys.platform in ("win32", "darwin"): + if sys.platform in {"win32", "darwin"}: height = 40 ctrl = TextCtrl( self, diff --git a/gui/wxpython/lmgr/workspace.py b/gui/wxpython/lmgr/workspace.py index 32d56d448c2..68b0a9a366f 100644 --- a/gui/wxpython/lmgr/workspace.py +++ b/gui/wxpython/lmgr/workspace.py @@ -149,7 +149,7 @@ def _tryToSwitchMapsetFromWorkspaceFile(self, gxwXml): style=wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION, ) dlg.CenterOnParent() - if dlg.ShowModal() in [wx.ID_NO, wx.ID_CANCEL]: + if dlg.ShowModal() in {wx.ID_NO, wx.ID_CANCEL}: return False else: # TODO: copy from ChangeLocation function diff --git a/gui/wxpython/location_wizard/wizard.py b/gui/wxpython/location_wizard/wizard.py index ddcf080c52a..3392aa0ddd0 100644 --- a/gui/wxpython/location_wizard/wizard.py +++ b/gui/wxpython/location_wizard/wizard.py @@ -2298,7 +2298,7 @@ def OnEnterPage(self, event): global coordsys # print coordsys,proj4string - if coordsys in ("proj", "epsg", "iau", "wkt", "file"): + if coordsys in {"proj", "epsg", "iau", "wkt", "file"}: extra_opts = {} extra_opts["project"] = "project" extra_opts["getErrorMsg"] = True diff --git a/gui/wxpython/main_window/frame.py b/gui/wxpython/main_window/frame.py index 49a58068718..e425fed5a9e 100644 --- a/gui/wxpython/main_window/frame.py +++ b/gui/wxpython/main_window/frame.py @@ -1279,7 +1279,7 @@ def GetMenuCmd(self, event): # check list of dummy commands for GUI modules that do not have GRASS # bin modules or scripts. - if cmd in ["vcolors", "r.mapcalc", "r3.mapcalc"]: + if cmd in {"vcolors", "r.mapcalc", "r3.mapcalc"}: return cmdlist try: @@ -1571,7 +1571,7 @@ def write_help(): # use chdir or dialog if cmd and len(cmd) == 2: write_beginning(parameter=cmd[1]) - if cmd[1] in ["-h", "--h", "--help", "help"]: + if cmd[1] in {"-h", "--h", "--help", "help"}: write_help() write_end() return @@ -2133,7 +2133,7 @@ def OnMapCreated(self, name, ltype, add=None): def AddOrUpdateMap(self, mapName, ltype): """Add map layer or update""" # start new map display if no display is available - if ltype not in ["raster", "raster_3d", "vector"]: + if ltype not in {"raster", "raster_3d", "vector"}: GError(parent=self, message=_("Unsupported map layer type <%s>.") % ltype) return diff --git a/gui/wxpython/mapdisp/frame.py b/gui/wxpython/mapdisp/frame.py index c85b2756328..2aecda9368c 100644 --- a/gui/wxpython/mapdisp/frame.py +++ b/gui/wxpython/mapdisp/frame.py @@ -1014,10 +1014,10 @@ def Query(self, x, y): ltype = layer.maplayer.GetType() if ltype == "raster": rast.append(name) - elif ltype in ("rgb", "his"): + elif ltype in {"rgb", "his"}: for iname in name.split("\n"): rast.append(iname) - elif ltype in ("vector", "thememap", "themechart"): + elif ltype in {"vector", "thememap", "themechart"}: vect.append(name) if vect: # check for vector maps open to be edited diff --git a/gui/wxpython/mapdisp/main.py b/gui/wxpython/mapdisp/main.py index 9a26378eb1e..25256bb2f0c 100644 --- a/gui/wxpython/mapdisp/main.py +++ b/gui/wxpython/mapdisp/main.py @@ -196,7 +196,7 @@ def GetLayersFromCmdFile(self): args = {} - if ltype in ("barscale", "rastleg", "northarrow", "text", "vectleg"): + if ltype in {"barscale", "rastleg", "northarrow", "text", "vectleg"}: # TODO: this is still not optimal # it is there to prevent adding the same overlay multiple times if cmd in self.oldOverlays: diff --git a/gui/wxpython/mapdisp/statusbar.py b/gui/wxpython/mapdisp/statusbar.py index 43211341147..cec575bc555 100644 --- a/gui/wxpython/mapdisp/statusbar.py +++ b/gui/wxpython/mapdisp/statusbar.py @@ -549,7 +549,7 @@ def ReprojectENToMap(self, e, n, useDefinedProjection): projIn = settings projOut = RunCommand("g.proj", flags="jf", read=True) proj = projIn.split(" ")[0].split("=")[1] - if proj in ("ll", "latlong", "longlat"): + if proj in {"ll", "latlong", "longlat"}: e, n = utils.DMS2Deg(e, n) proj, coord1 = utils.ReprojectCoordinates( coord=(e, n), projIn=projIn, projOut=projOut, flags="d" @@ -629,7 +629,7 @@ def GetCenterString(self, map): flags="d", ) if coord: - if proj in ("ll", "latlong", "longlat") and format == "DMS": + if proj in {"ll", "latlong", "longlat"} and format == "DMS": return "%s" % utils.Deg2DMS( coord[0], coord[1], precision=precision ) @@ -807,7 +807,7 @@ def ReprojectENFromMap(self, e, n, useDefinedProjection, precision, format): ) if coord: e, n = coord - if proj in ("ll", "latlong", "longlat") and format == "DMS": + if proj in {"ll", "latlong", "longlat"} and format == "DMS": return utils.Deg2DMS(e, n, precision=precision) else: return "%.*f; %.*f" % (precision, e, precision, n) @@ -897,7 +897,7 @@ def ReprojectRegionFromMap(self, region, useDefinedProjection, precision, format coord=(region["ewres"], region["nsres"]), projOut=projOut, flags="d" ) if coord1 and coord2: - if proj in ("ll", "latlong", "longlat") and format == "DMS": + if proj in {"ll", "latlong", "longlat"} and format == "DMS": w, s = utils.Deg2DMS( coord1[0], coord1[1], string=False, precision=precision ) diff --git a/gui/wxpython/mapwin/buffered.py b/gui/wxpython/mapwin/buffered.py index 79efe012033..c0c56365ff3 100644 --- a/gui/wxpython/mapwin/buffered.py +++ b/gui/wxpython/mapwin/buffered.py @@ -417,7 +417,7 @@ def Draw( # polyline is a series of connected lines defined as sequence of points # lines are individual, not connected lines which must be drawn as 1 # object (e.g. cross) - elif pdctype in ("polyline", "lines"): + elif pdctype in {"polyline", "lines"}: if pen: pdc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT)) pdc.SetPen(pen) @@ -1526,7 +1526,7 @@ def OnDragging(self, event): self.mouse["end"] = event.GetPosition() if event.LeftIsDown() and not ( digitToolbar - and digitToolbar.GetAction() in ("moveLine",) + and digitToolbar.GetAction() in {"moveLine"} and len(self.digit.GetDisplay().GetSelected()) > 0 ): self.MouseDraw(pdc=self.pdcTmp) @@ -1574,7 +1574,7 @@ def OnLeftUp(self, event): self.mouse["end"] = event.GetPosition() coordinates = self.Pixel2Cell(self.mouse["end"]) - if self.mouse["use"] in ["zoom", "pan"]: + if self.mouse["use"] in {"zoom", "pan"}: # set region in zoom or pan begin = self.mouse["begin"] end = self.mouse["end"] diff --git a/gui/wxpython/modules/colorrules.py b/gui/wxpython/modules/colorrules.py index 21dc93476f6..87c03d0963a 100644 --- a/gui/wxpython/modules/colorrules.py +++ b/gui/wxpython/modules/colorrules.py @@ -785,7 +785,7 @@ def CreateColorTable(self, tmp=False): continue if ( - rule["value"] not in ("nv", "default") + rule["value"] not in {"nv", "default"} and rule["value"][-1] != "%" and not self._IsNumber(rule["value"]) ): diff --git a/gui/wxpython/modules/import_export.py b/gui/wxpython/modules/import_export.py index df1882c6ed6..83d2b447bdd 100644 --- a/gui/wxpython/modules/import_export.py +++ b/gui/wxpython/modules/import_export.py @@ -631,10 +631,10 @@ def OnRun(self, event): if ( self.dsnInput.GetType() == "db" and self.dsnInput.GetFormat() - in ( + in { "PostgreSQL", "PostgreSQL/PostGIS", - ) + } and "GRASS_VECTOR_OGR" not in os.environ ): self.popOGR = True diff --git a/gui/wxpython/nviz/mapwindow.py b/gui/wxpython/nviz/mapwindow.py index 80b5ff73d0a..d00bcd3bc86 100644 --- a/gui/wxpython/nviz/mapwindow.py +++ b/gui/wxpython/nviz/mapwindow.py @@ -85,10 +85,10 @@ def __init__(self, parent, giface, frame, Map, tree, lmgr, id=wx.ID_ANY): # for wxGTK we need to set WX_GL_DEPTH_SIZE to draw vectors correctly # but we don't know the right value # in wxpython 2.9, there is IsDisplaySupported - if CheckWxVersion(version=[2, 8, 11]) and sys.platform not in ( + if CheckWxVersion(version=[2, 8, 11]) and sys.platform not in { "win32", "darwin", - ): + }: depthBuffer = int( UserSettings.Get(group="display", key="nvizDepthBuffer", subkey="value") ) @@ -217,10 +217,10 @@ def __init__(self, parent, giface, frame, Map, tree, lmgr, id=wx.ID_ANY): self.Bind(wx.EVT_CLOSE, self.OnClose) - if CheckWxVersion(version=[2, 8, 11]) and sys.platform not in ( + if CheckWxVersion(version=[2, 8, 11]) and sys.platform not in { "win32", "darwin", - ): + }: wx.CallLater(3000, self._warningDepthBuffer) # cplanes cannot be initialized now @@ -478,7 +478,7 @@ def _onUpdateOverlays(self): Updates self.imagelist""" # update images (legend and text) for oid, overlay in self.overlays.items(): - if not overlay.IsShown() or overlay.name in ("barscale", "northarrow"): + if not overlay.IsShown() or overlay.name in {"barscale", "northarrow"}: continue if oid not in [t.GetId() for t in self.imagelist]: # new self.CreateTexture(overlay=overlay) @@ -557,7 +557,7 @@ def OnKeyDown(self, event): self.render["quick"] = False self.Refresh(False) - elif key in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT, wx.WXK_RIGHT): + elif key in {wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT, wx.WXK_RIGHT}: if not self.fly["mouseControl"]: if not self.timerFly.IsRunning(): sx, sy = self.GetClientSize() @@ -575,9 +575,9 @@ def OnKeyDown(self, event): elif key == wx.WXK_DOWN: self.ChangeFlySpeed(increase=False) - elif key in (wx.WXK_HOME, wx.WXK_PAGEUP) and self.timerFly.IsRunning(): + elif key in {wx.WXK_HOME, wx.WXK_PAGEUP} and self.timerFly.IsRunning(): self.ChangeFlySpeed(increase=True) - elif key in (wx.WXK_END, wx.WXK_PAGEDOWN) and self.timerFly.IsRunning(): + elif key in {wx.WXK_END, wx.WXK_PAGEDOWN} and self.timerFly.IsRunning(): self.ChangeFlySpeed(increase=False) event.Skip() @@ -806,7 +806,7 @@ def OnLeftUp(self, event): # and moreover we are in left up self.mapQueried.emit(x=self.mouse["end"][0], y=self.mouse["end"][1]) - elif self.mouse["use"] in ("arrow", "scalebar"): + elif self.mouse["use"] in {"arrow", "scalebar"}: self.lmgr.nviz.FindWindowById( self.lmgr.nviz.win["decoration"][self.mouse["use"]]["place"] ).SetValue(False) @@ -1343,7 +1343,7 @@ def _GetDataLayers(self, item, litems): item = self.tree.GetNextItem(item) continue - if not item.IsChecked() or type not in ("raster", "vector", "raster_3d"): + if not item.IsChecked() or type not in {"raster", "vector", "raster_3d"}: item = self.tree.GetNextItem(item) continue @@ -1544,7 +1544,7 @@ def SetMapObjProperties(self, item, id, nvizType): continue if isinstance(data[sec][sec1], dict): for sec2 in data[sec][sec1].keys(): - if sec2 not in ("all", "init", "id"): + if sec2 not in {"all", "init", "id"}: data[sec][sec1][sec2]["update"] = None elif isinstance(data[sec][sec1], list): for i in range(len(data[sec][sec1])): @@ -1555,7 +1555,7 @@ def SetMapObjProperties(self, item, id, nvizType): # set id if id > 0: - if mapType in ("raster", "raster_3d"): + if mapType in {"raster", "raster_3d"}: data[nvizType]["object"] = {"id": id, "init": False} elif mapType == "vector": data["vector"][nvizType]["object"] = {"id": id, "init": False} @@ -1585,7 +1585,7 @@ def _loadRaster(self, item): """ layer = self.tree.GetLayerInfo(item, key="maplayer") - if layer.type not in ("raster", "raster_3d"): + if layer.type not in {"raster", "raster_3d"}: return if layer.type == "raster": @@ -1600,7 +1600,7 @@ def _loadRaster(self, item): id = -1 if id < 0: - if layer.type in ("raster", "raster_3d"): + if layer.type in {"raster", "raster_3d"}: self.log.WriteError("%s <%s> %s" % (errorMsg, layer.name, _("failed"))) else: self.log.WriteError(_("Unsupported layer type '%s'") % layer.type) @@ -1714,7 +1714,7 @@ def _unloadRaster(self, item): """ layer = self.tree.GetLayerInfo(item, key="maplayer") - if layer.type not in ("raster", "raster_3d"): + if layer.type not in {"raster", "raster_3d"}: return data = self.tree.GetLayerInfo(item, key="nviz") diff --git a/gui/wxpython/nviz/preferences.py b/gui/wxpython/nviz/preferences.py index 4c8f6da9107..9bd4614a670 100644 --- a/gui/wxpython/nviz/preferences.py +++ b/gui/wxpython/nviz/preferences.py @@ -704,7 +704,7 @@ def OnDefault(self, event): group, key, subkey, subkey1 = gks.split(":") value = self.settings.Get(group, key, [subkey, subkey1]) if subkey == "position": - if subkey1 in ("x", "y"): + if subkey1 in {"x", "y"}: value = float(value) * 100 win = self.FindWindowById(self.winId[gks]) if win.GetName() == "GetSelection": @@ -731,7 +731,7 @@ def OnApply(self, event): value = win.GetValue() if subkey == "position": - if subkey1 in ("x", "y"): + if subkey1 in {"x", "y"}: value = float(value) / 100 if subkey1: self.settings.Set(group, value, key, [subkey, subkey1]) diff --git a/gui/wxpython/nviz/tools.py b/gui/wxpython/nviz/tools.py index 8bbbc3791c7..c727c92d472 100644 --- a/gui/wxpython/nviz/tools.py +++ b/gui/wxpython/nviz/tools.py @@ -263,7 +263,7 @@ def UpdateScrolling(self, foldpanels): foldpanel.GetFoldPanel(panelIdx).GetParent().GetGrandParent() ) width = self.GetSize()[0] - if sys.platform in ("darwin", "win32"): + if sys.platform in {"darwin", "win32"}: width -= 60 # 60px right margin to show scrollbar scrolledPanel.SetVirtualSize(width=width, height=length[2]) scrolledPanel.Layout() @@ -1086,7 +1086,7 @@ def _createSurfacePage(self, parent): parent=panel, id=wx.ID_ANY, size=(100, -1), choices=[_("map")] ) - if code not in ("color", "shine"): + if code not in {"color", "shine"}: use.Insert(item=_("unset"), pos=0) self.win["surface"][code]["required"] = False else: @@ -2925,7 +2925,7 @@ def OnConstantSelection(self, event): for attr, value in data["constant"].items(): if attr == "color": value = self._getColorFromString(value) - if attr in ("color", "value", "resolution", "transp"): + if attr in {"color", "value", "resolution", "transp"}: if attr == "transp": self.FindWindowById(self.win["constant"][attr]).SetValue( self._getPercent(value) @@ -3035,7 +3035,7 @@ def _createIsosurfacePanel(self, parent): else: use = None # check for required properties - if code not in ("topo", "color", "shine"): + if code not in {"topo", "color", "shine"}: use.Insert(item=_("unset"), pos=0) self.win["volume"][code]["required"] = False else: @@ -3090,7 +3090,7 @@ def _createIsosurfacePanel(self, parent): value = SpinCtrl(parent=panel, id=wx.ID_ANY, size=size, initial=0) if code == "topo": value.SetRange(minVal=-1e9, maxVal=1e9) - elif code in ("shine", "transp"): + elif code in {"shine", "transp"}: value.SetRange(minVal=0, maxVal=100) value.Bind(wx.EVT_SPINCTRL, self.OnVolumeIsosurfMap) @@ -3482,7 +3482,7 @@ def OnViewChange(self, event): else: self.PostViewEvent(zExag=False) - if winName in ("persp", "twist"): + if winName in {"persp", "twist"}: convert = int else: convert = float @@ -3686,7 +3686,7 @@ def OnMapObjUse(self, event): def EnablePage(self, name, enabled=True): """Enable/disable all widgets on page""" for key, item in self.win[name].items(): - if key in ("map", "surface", "new", "planes"): + if key in {"map", "surface", "new", "planes"}: continue if isinstance(item, dict): for skey, sitem in self.win[name][key].items(): @@ -3705,7 +3705,7 @@ def EnablePage(self, name, enabled=True): def SetMapObjUseMap(self, nvizType, attrb, map=None): """Update dialog widgets when attribute type changed""" - if attrb in ("topo", "color", "shine"): + if attrb in {"topo", "color", "shine"}: incSel = -1 # decrement selection (no 'unset') else: incSel = 0 @@ -3997,10 +3997,10 @@ def OnSurfacePosition(self, event): self.AdjustSliderRange(slider=slider, value=value) for win in self.win["surface"]["position"].values(): - if win in ( + if win in { self.win["surface"]["position"]["axis"], self.win["surface"]["position"]["reset"], - ): + }: continue else: self.FindWindowById(win).SetValue(value) @@ -4830,10 +4830,10 @@ def OnVolumePosition(self, event): self.AdjustSliderRange(slider=slider, value=value) for win in self.win["volume"]["position"].values(): - if win in ( + if win in { self.win["volume"]["position"]["axis"], self.win["volume"]["position"]["reset"], - ): + }: continue else: self.FindWindowById(win).SetValue(value) @@ -5250,7 +5250,7 @@ def UpdatePage(self, pageId): self.FindWindowById(self.win["view"]["persp"][control]).SetValue(pval) - elif pageId in ("surface", "vector", "volume"): + elif pageId in {"surface", "vector", "volume"}: name = self.FindWindowById(self.win[pageId]["map"]).GetValue() data = self.GetLayerData(pageId) if data: @@ -5829,7 +5829,7 @@ def SetPage(self, name): """Get named page""" if name == "view": self.SetSelection(0) - elif name in ("surface", "vector", "volume"): + elif name in {"surface", "vector", "volume"}: self.SetSelection(1) else: self.SetSelection(2) diff --git a/gui/wxpython/nviz/workspace.py b/gui/wxpython/nviz/workspace.py index 1d2b1a2450d..b6975d415dc 100644 --- a/gui/wxpython/nviz/workspace.py +++ b/gui/wxpython/nviz/workspace.py @@ -79,7 +79,7 @@ def SetSurfaceDefaultProp(self, data=None): if control == "wire-color": value = str(value[0]) + ":" + str(value[1]) + ":" + str(value[2]) - elif control in ("mode", "style", "shading"): + elif control in {"mode", "style", "shading"}: if "mode" not in data["draw"]: data["draw"]["mode"] = {} continue diff --git a/gui/wxpython/psmap/dialogs.py b/gui/wxpython/psmap/dialogs.py index ce10ad3ba96..5fe648d9e18 100644 --- a/gui/wxpython/psmap/dialogs.py +++ b/gui/wxpython/psmap/dialogs.py @@ -1267,7 +1267,7 @@ def OnScaleChoice(self, event): self.scaleType = scaleType self.select.SetValue("") - if scaleType in (0, 1): # automatic - region from raster map, saved region + if scaleType in {0, 1}: # automatic - region from raster map, saved region if scaleType == 0: # set map selection self.rasterTypeRadio.Show() @@ -2208,7 +2208,7 @@ def __init__(self, parent, id, vectors, tmpSettings): self.ColorsPanel = selectPanel[self.type][0](notebook) self.OnOutline(None) - if self.type in ("points", "areas"): + if self.type in {"points", "areas"}: self.OnFill(None) self.OnColor(None) @@ -2252,7 +2252,7 @@ def _DataSelectionPanel(self, notebook): # data type self.checkType1 = self.checkType2 = None - if self.type in ("lines", "points"): + if self.type in {"lines", "points"}: box = StaticBox( parent=panel, id=wx.ID_ANY, label=" %s " % _("Feature type") ) @@ -3108,7 +3108,7 @@ def OnLayer(self, event): self.choiceColumns.SetItems(cols) self.choiceColumns.SetSelection(0) - if self.type in ("points", "lines"): + if self.type in {"points", "lines"}: self.colorColChoice.SetItems(cols) self.colorColChoice.SetSelection(0) @@ -3205,7 +3205,7 @@ def getColsChoice(self, parent): def update(self): # feature type - if self.type in ("lines", "points"): + if self.type in {"lines", "points"}: featureType = None if self.checkType1.GetValue(): featureType = self.checkType1.GetName() @@ -3235,7 +3235,7 @@ def update(self): self.vPropertiesDict["masked"] = "n" # colors - if self.type in ("points", "areas"): + if self.type in {"points", "areas"}: if self.outlineCheck.GetValue(): self.vPropertiesDict["color"] = convertRGB(self.colorPicker.GetColour()) self.vPropertiesDict["width"] = self.widthSpin.GetValue() @@ -4011,7 +4011,7 @@ def OnRaster(self, event): if type == "CELL": self.discrete.SetValue(True) - elif type in ("FCELL", "DCELL"): + elif type in {"FCELL", "DCELL"}: self.continuous.SetValue(True) if event is None: if self.rLegendDict["discrete"] == "y": diff --git a/gui/wxpython/psmap/frame.py b/gui/wxpython/psmap/frame.py index ad5ede86739..029b8fd03d0 100644 --- a/gui/wxpython/psmap/frame.py +++ b/gui/wxpython/psmap/frame.py @@ -1128,7 +1128,7 @@ def DialogDataChanged(self, id): for id in ids: itype = self.instruction[id].type - if itype in ("scalebar", "mapinfo", "image"): + if itype in {"scalebar", "mapinfo", "image"}: drawRectangle = self.canvas.CanvasPaperCoordinates( rect=self.instruction[id]["rect"], canvasToPaper=False ) @@ -1157,7 +1157,7 @@ def DialogDataChanged(self, id): ) self.canvas.RedrawSelectBox(id) - if itype in ("point", "line", "rectangle"): + if itype in {"point", "line", "rectangle"}: drawRectangle = self.canvas.CanvasPaperCoordinates( rect=self.instruction[id]["rect"], canvasToPaper=False ) @@ -1243,7 +1243,7 @@ def DialogDataChanged(self, id): ) self.canvas.RedrawSelectBox(id) - if itype in ("map", "vector", "raster", "labels"): + if itype in {"map", "vector", "raster", "labels"}: if itype == "raster": # set resolution try: info = grass.raster_info(self.instruction[id]["raster"]) @@ -1539,7 +1539,7 @@ def RecalculateEN(self): items = self.instruction.FindInstructionByType(itemType, list=True) for item in items: instr = self.instruction[item.id] - if itemType in ("line", "rectangle"): + if itemType in {"line", "rectangle"}: if itemType == "line": e1, n1 = PaperMapCoordinates( mapInstr=self.instruction[mapId], @@ -1611,14 +1611,14 @@ def OnPaint(self, event): def MouseActions(self, event): """Mouse motion and button click notifier""" - disable = self.preview and self.mouse["use"] in ( + disable = self.preview and self.mouse["use"] in { "pointer", "resize", "addMap", "addPoint", "addLine", "addRectangle", - ) + } # zoom with mouse wheel if event.GetWheelRotation() != 0: self.OnMouseWheel(event) @@ -1681,7 +1681,7 @@ def OnMouseMoving(self, event): if self.preview: return - if self.mouse["use"] in ("pointer", "resize"): + if self.mouse["use"] in {"pointer", "resize"}: pos = event.GetPosition() foundResize = self.pdcTmp.FindObjects(pos[0], pos[1]) if ( @@ -1724,7 +1724,7 @@ def OnLeftDown(self, event): if self.instruction[self.dragId].type == "map": self.constraint = False self.mapBounds = self.pdcObj.GetIdBounds(self.dragId) - if self.instruction[self.dragId]["scaleType"] in (0, 1, 2): + if self.instruction[self.dragId]["scaleType"] in {0, 1, 2}: self.constraint = True self.mapBounds = self.pdcObj.GetIdBounds(self.dragId) @@ -1734,7 +1734,7 @@ def OnLeftDown(self, event): elif found: self.dragId = found[0] self.RedrawSelectBox(self.dragId) - if self.instruction[self.dragId].type not in ("map", "rectangle"): + if self.instruction[self.dragId].type not in {"map", "rectangle"}: self.pdcTmp.RemoveId(self.idResizeBoxTmp) self.Refresh() if self.instruction[self.dragId].type != "line": @@ -1756,7 +1756,7 @@ def OnLeftUp(self, event): Recalculate zooming/resizing/moving and redraw. """ # zoom in, zoom out - if self.mouse["use"] in ("zoomin", "zoomout"): + if self.mouse["use"] in {"zoomin", "zoomout"}: zoomR = self.pdcTmp.GetIdBounds(self.idZoomBoxTmp) self.pdcTmp.RemoveId(self.idZoomBoxTmp) self.Refresh() @@ -1802,7 +1802,7 @@ def OnLeftUp(self, event): ) self.instruction[mapId]["rect"] = newRectPaper - if self.instruction[mapId]["scaleType"] in (0, 1, 2): + if self.instruction[mapId]["scaleType"] in {0, 1, 2}: if self.instruction[mapId]["scaleType"] == 0: scale, foo, rect = AutoAdjust( self, @@ -1871,13 +1871,13 @@ def OnLeftUp(self, event): self.mouse["use"] = "pointer" # recalculate the position of objects after dragging - if self.mouse["use"] in ("pointer", "resize") and self.dragId != -1: + if self.mouse["use"] in {"pointer", "resize"} and self.dragId != -1: if self.mouse["begin"] != event.GetPosition(): # for double click self.RecalculatePosition(ids=[self.dragId]) if self.instruction[self.dragId].type in self.openDialogs: self.openDialogs[self.instruction[self.dragId].type].updateDialog() - elif self.mouse["use"] in ("addPoint", "addLine", "addRectangle"): + elif self.mouse["use"] in {"addPoint", "addLine", "addRectangle"}: endCoordinates = self.CanvasPaperCoordinates( rect=Rect(event.GetX(), event.GetY(), 0, 0), canvasToPaper=True )[:2] @@ -1887,7 +1887,7 @@ def OnLeftUp(self, event): if self.mouse["use"] == "addPoint": self.parent.AddPoint(coordinates=endCoordinates) - elif self.mouse["use"] in ("addLine", "addRectangle"): + elif self.mouse["use"] in {"addLine", "addRectangle"}: # not too small lines/rectangles if sqrt(diffX * diffX + diffY * diffY) < 5: self.pdcTmp.RemoveId(self.idZoomBoxTmp) @@ -1951,13 +1951,13 @@ def OnDragging(self, event): elif event.LeftIsDown(): # draw box when zooming, creating map - if self.mouse["use"] in ( + if self.mouse["use"] in { "zoomin", "zoomout", "addMap", "addLine", "addRectangle", - ): + }: self.mouse["end"] = event.GetPosition() r = Rect( self.mouse["begin"][0], @@ -1967,7 +1967,7 @@ def OnDragging(self, event): ) r = self.modifyRectangle(r) - if self.mouse["use"] in ("addLine", "addRectangle"): + if self.mouse["use"] in {"addLine", "addRectangle"}: if self.mouse["use"] == "addLine": pdcType = "line" lineCoords = (self.mouse["begin"], self.mouse["end"]) @@ -2125,26 +2125,26 @@ def Pan(self, begin, end): def RecalculatePosition(self, ids): for id in ids: itype = self.instruction[id].type - if itype in ("map", "rectangle"): + if itype in {"map", "rectangle"}: self.instruction[id]["rect"] = self.CanvasPaperCoordinates( rect=self.pdcObj.GetIdBounds(id), canvasToPaper=True ) self.RecalculateEN() - elif itype in ( + elif itype in { "mapinfo", "rasterLegend", "vectorLegend", "image", "northArrow", - ): + }: self.instruction[id]["rect"] = self.CanvasPaperCoordinates( rect=self.pdcObj.GetIdBounds(id), canvasToPaper=True ) self.instruction[id]["where"] = self.CanvasPaperCoordinates( rect=self.pdcObj.GetIdBounds(id), canvasToPaper=True )[:2] - if itype in ("image", "northArrow"): + if itype in {"image", "northArrow"}: self.RecalculateEN() elif itype == "point": @@ -2322,14 +2322,14 @@ def Zoom(self, zoomFactor, view): bb=oRect, ) - elif type in ("point", "line", "rectangle"): + elif type in {"point", "line", "rectangle"}: instr = self.instruction[id] color = self.instruction[id]["color"] width = fcolor = coords = None - if type in ("point", "rectangle"): + if type in {"point", "rectangle"}: fcolor = self.instruction[id]["fcolor"] - if type in ("line", "rectangle"): + if type in {"line", "rectangle"}: width = self.instruction[id]["width"] if type in ("line"): point1, point2 = instr["where"][0], instr["where"][1] @@ -2418,7 +2418,7 @@ def Draw( else: # draw only rectangle with label pdctype = "rectText" - if pdctype in ("rect", "rectText"): + if pdctype in {"rect", "rectText"}: pdc.DrawRectangle(*bb) if pdctype == "rectText": @@ -2635,7 +2635,7 @@ def RedrawSelectBox(self, id): ) # draw small marks signalizing resizing - if self.instruction[id].type in ("map", "rectangle"): + if self.instruction[id].type in {"map", "rectangle"}: controlP = self.pdcObj.GetIdBounds(id).GetBottomRight() rect = Rect( controlP[0], diff --git a/gui/wxpython/psmap/instructions.py b/gui/wxpython/psmap/instructions.py index 121c0458d69..e4cd59f09c6 100644 --- a/gui/wxpython/psmap/instructions.py +++ b/gui/wxpython/psmap/instructions.py @@ -118,14 +118,14 @@ def AddInstruction(self, instruction): else: self.instruction.append(instruction) # add to drawable objects - if instruction.type not in ( + if instruction.type not in { "page", "raster", "vector", "vProperties", "initMap", "labels", - ): + }: if instruction.type == "map": self.objectsToDraw.insert(0, instruction.id) else: @@ -201,9 +201,9 @@ def Read(self, filename): kwargs = {} if instruction == "scalebar": kwargs["scale"] = map["scale"] - elif instruction in ("text", "eps", "point", "line", "rectangle"): + elif instruction in {"text", "eps", "point", "line", "rectangle"}: kwargs["mapInstruction"] = map - elif instruction in ("vpoints", "vlines", "vareas"): + elif instruction in {"vpoints", "vlines", "vareas"}: kwargs["id"] = NewId() kwargs["vectorMapNumber"] = vectorMapNumber vectorMapNumber += 1 @@ -222,11 +222,11 @@ def Read(self, filename): buffer.append(line) elif line.startswith("border"): - if line.split()[1].lower() in ("n", "no", "none"): + if line.split()[1].lower() in {"n", "no", "none"}: ok = self.SendToRead("border", [line]) if not ok: return False - elif line.split()[1].lower() in ("y", "yes"): + elif line.split()[1].lower() in {"y", "yes"}: instruction = "border" isBuffer = True buffer.append(line) @@ -284,11 +284,11 @@ def Read(self, filename): buffer.append(line) elif line.startswith("colortable"): - if len(line.split()) == 2 and line.split()[1].lower() in ( + if len(line.split()) == 2 and line.split()[1].lower() in { "n", "no", "none", - ): + }: break instruction = "colortable" isBuffer = True @@ -451,7 +451,7 @@ def SendToRead(self, instruction, text, **kwargs): instr = self.FindInstructionByType(i) if ( i - in ( + in { "text", "vProperties", "image", @@ -459,7 +459,7 @@ def SendToRead(self, instruction, text, **kwargs): "point", "line", "rectangle", - ) + } or not instr ): id = NewId() # !vProperties expect subtype @@ -470,7 +470,7 @@ def SendToRead(self, instruction, text, **kwargs): subType=instruction[1:], env=self.env, ) - elif i in ("image", "northArrow"): + elif i in {"image", "northArrow"}: commentFound = False for line in text: if line.find("# north arrow") >= 0: @@ -642,7 +642,7 @@ def __str__(self): region = self.instruction["region"] comment = "# g.region region=%s\n" % region # current region, fixed scale - elif self.instruction["scaleType"] in (2, 3): + elif self.instruction["scaleType"] in {2, 3}: comment = string.Template( "# g.region n=$n s=$s e=$e w=$w rows=$rows cols=$cols \n" ).substitute(**region) @@ -693,10 +693,10 @@ def Read(self, instruction, text, **kwargs): if line.startswith("end"): break try: - if line.split()[1].lower() in ("n", "no", "none"): + if line.split()[1].lower() in {"n", "no", "none"}: instr["border"] = "n" break - elif line.split()[1].lower() in ("y", "yes"): + elif line.split()[1].lower() in {"y", "yes"}: instr["border"] = "y" elif line.startswith("width"): instr["width"] = line.split()[1] @@ -1050,7 +1050,7 @@ def Read(self, instruction, text, **kwargs): elif sub == "yoffset": instr["yoffset"] = int(line.split(None, 1)[1]) elif sub == "opaque": - if line.split(None, 1)[1].lower() in ("n", "none"): + if line.split(None, 1)[1].lower() in {"n", "none"}: instr["background"] = "none" except (IndexError, ValueError): @@ -1550,14 +1550,14 @@ def Read(self, instruction, text, **kwargs): elif line.startswith("length"): instr["length"] = float(line.split()[1]) elif line.startswith("units"): - if line.split()[1] in [ + if line.split()[1] in { "auto", "meters", "kilometers", "feet", "miles", "nautmiles", - ]: + }: instr["unitsLength"] = line.split()[1] elif line.startswith("height"): instr["height"] = float(line.split()[1]) @@ -1568,9 +1568,9 @@ def Read(self, instruction, text, **kwargs): elif line.startswith("segment"): instr["segment"] = int(line.split()[1]) elif line.startswith("background"): - if line.split()[1].strip().lower() in ("y", "yes"): + if line.split()[1].strip().lower() in {"y", "yes"}: instr["background"] = "y" - elif line.split()[1].strip().lower() in ("n", "no", "none"): + elif line.split()[1].strip().lower() in {"n", "no", "none"}: instr["background"] = "n" except (IndexError, ValueError): GError(_("Failed to read instruction %s") % instruction) @@ -1699,19 +1699,19 @@ def Read(self, instruction, text, **kwargs): instr["min"] = float(line.split()[1]) instr["max"] = float(line.split()[2]) elif line.startswith("nodata"): - if line.split()[1].strip().lower() in ("y", "yes"): + if line.split()[1].strip().lower() in {"y", "yes"}: instr["nodata"] = "y" - elif line.split()[1].strip().lower() in ("n", "no", "none"): + elif line.split()[1].strip().lower() in {"n", "no", "none"}: instr["nodata"] = "n" elif line.startswith("tickbar"): - if line.split()[1].strip().lower() in ("y", "yes"): + if line.split()[1].strip().lower() in {"y", "yes"}: instr["tickbar"] = "y" - elif line.split()[1].strip().lower() in ("n", "no", "none"): + elif line.split()[1].strip().lower() in {"n", "no", "none"}: instr["tickbar"] = "n" elif line.startswith("discrete"): - if line.split()[1].strip().lower() in ("y", "yes"): + if line.split()[1].strip().lower() in {"y", "yes"}: instr["discrete"] = "y" - elif line.split()[1].strip().lower() in ("n", "no", "none"): + elif line.split()[1].strip().lower() in {"n", "no", "none"}: instr["discrete"] = "n" except (IndexError, ValueError): @@ -1752,7 +1752,7 @@ def EstimateHeight(self, raster, discrete, fontsize, cols=None, height=None): cols = 1 rinfo = grass.raster_info(raster) - if rinfo["datatype"] in ("DCELL", "FCELL"): + if rinfo["datatype"] in {"DCELL", "FCELL"}: minim, maxim = rinfo["min"], rinfo["max"] rows = ceil(maxim / cols) else: @@ -2066,7 +2066,7 @@ def __str__(self): dic = self.instruction vInstruction = string.Template("v$subType $name\n").substitute(dic) # data selection - if self.subType in ("points", "lines"): + if self.subType in {"points", "lines"}: vInstruction += string.Template(" type $type\n").substitute(dic) if dic["connection"]: vInstruction += string.Template(" layer $layer\n").substitute(dic) @@ -2077,7 +2077,7 @@ def __str__(self): vInstruction += string.Template(" masked $masked\n").substitute(dic) # colors vInstruction += string.Template(" color $color\n").substitute(dic) - if self.subType in ("points", "areas"): + if self.subType in {"points", "areas"}: if dic["color"] != "none": vInstruction += string.Template(" width $width\n").substitute(dic) if dic["rgbcolumn"]: @@ -2233,7 +2233,7 @@ def Read(self, instruction, text, **kwargs): elif line.startswith("layer"): instr["layer"] = line.split()[1] elif line.startswith("masked"): - if line.split()[1].lower() in ("y", "yes"): + if line.split()[1].lower() in {"y", "yes"}: instr["masked"] = "y" else: instr["masked"] = "n" diff --git a/gui/wxpython/rlisetup/sampling_frame.py b/gui/wxpython/rlisetup/sampling_frame.py index 0fa7c5242df..840f8fa4e30 100644 --- a/gui/wxpython/rlisetup/sampling_frame.py +++ b/gui/wxpython/rlisetup/sampling_frame.py @@ -116,12 +116,12 @@ def __init__(self, parent, samplingType, icon=None, map_=None): self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw( graphicsType="line" ) - elif self.samplingtype in [SamplingType.MUNITSR, SamplingType.MMVWINR]: + elif self.samplingtype in {SamplingType.MUNITSR, SamplingType.MMVWINR}: self.sampleFrameChanged = Signal("RLiSetupMapPanel.sampleFrameChanged") self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw( graphicsType="rectangle" ) - elif self.samplingtype in [SamplingType.MUNITSC, SamplingType.MMVWINC]: + elif self.samplingtype in {SamplingType.MUNITSC, SamplingType.MMVWINC}: self.afterCircleDrawn = Signal("RLiSetupMapPanel.afterCircleDrawn") self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw( graphicsType="line" @@ -404,7 +404,7 @@ def _rectangleDrawn(self): item.SetPropertyVal("hide", False) self.mapWindow.ClearLines() self._registeredGraphics.Draw() - if self.samplingtype in [SamplingType.MUNITSR, SamplingType.MMVWINR]: + if self.samplingtype in {SamplingType.MUNITSR, SamplingType.MMVWINR}: dlg = wx.MessageDialog( self, "Is this area ok?", @@ -475,9 +475,9 @@ def __init__(self, parent, toolSwitcher): if self.parent.samplingtype == SamplingType.REGIONS: self._default = self.digitizeregion - elif self.parent.samplingtype in [SamplingType.MUNITSR, SamplingType.MMVWINR]: + elif self.parent.samplingtype in {SamplingType.MUNITSR, SamplingType.MMVWINR}: self._default = self.digitizeunit - elif self.parent.samplingtype in [SamplingType.MUNITSC, SamplingType.MMVWINC]: + elif self.parent.samplingtype in {SamplingType.MUNITSC, SamplingType.MMVWINC}: self._default = self.digitizeunitc elif self.parent.samplingtype == SamplingType.VECT: self._default = None @@ -502,14 +502,14 @@ def _toolbarData(self): self.parent.OnDigitizeRegion, wx.ITEM_CHECK, ) - elif self.parent.samplingtype in [SamplingType.MUNITSR, SamplingType.MMVWINR]: + elif self.parent.samplingtype in {SamplingType.MUNITSR, SamplingType.MMVWINR}: drawTool = ( ("digitizeunit", icons["digitizeunit"].label), icons["digitizeunit"], self.parent.OnDraw, wx.ITEM_CHECK, ) - elif self.parent.samplingtype in [SamplingType.MUNITSC, SamplingType.MMVWINC]: + elif self.parent.samplingtype in {SamplingType.MUNITSC, SamplingType.MMVWINC}: drawTool = ( ("digitizeunitc", icons["digitizeunitc"].label), icons["digitizeunitc"], diff --git a/gui/wxpython/rlisetup/wizard.py b/gui/wxpython/rlisetup/wizard.py index 67392a0891c..7dff8a2a24a 100644 --- a/gui/wxpython/rlisetup/wizard.py +++ b/gui/wxpython/rlisetup/wizard.py @@ -391,10 +391,10 @@ def _write_area(self, fil): # MUNITSC = samplingtype=units, regionbox=mouse, shape=cirlce # MUNITSR = samplingtype=units, regionbox=mouse, shape=rectangle - elif self.samplingareapage.samplingtype in [ + elif self.samplingareapage.samplingtype in { SamplingType.MUNITSR, SamplingType.MUNITSC, - ]: + }: # get the raster region into rastregion grass.use_temp_region() grass.run_command("g.region", raster=self.startpage.rast) @@ -1646,19 +1646,19 @@ def OnEnterPage(self, event): wx.FindWindowById(wx.ID_FORWARD).Enable(True) else: wx.FindWindowById(wx.ID_FORWARD).Enable(False) - if self.parent.samplingareapage.samplingtype in [ + if self.parent.samplingareapage.samplingtype in { SamplingType.MVWIN, SamplingType.MMVWINR, SamplingType.MMVWINC, - ]: + }: self.title.SetLabel(_("Draw moving windows region")) self.sizer.Hide(self.regionNumPanel) wx.FindWindowById(wx.ID_FORWARD).Enable(True) - elif self.parent.samplingareapage.samplingtype in [ + elif self.parent.samplingareapage.samplingtype in { SamplingType.UNITS, SamplingType.MUNITSR, SamplingType.MUNITSC, - ]: + }: self.title.SetLabel(_("Draw sampling region")) self.sizer.Show(self.regionNumPanel) if self.typeBox.GetSelection() == 2: @@ -1668,22 +1668,22 @@ def OnEnterPage(self, event): def OnType(self, event): chosen = self.typeBox.GetSelection() if chosen == 0: - if self.parent.samplingareapage.samplingtype in [ + if self.parent.samplingareapage.samplingtype in { SamplingType.MVWIN, SamplingType.MMVWINR, SamplingType.MMVWINC, - ]: + }: self.parent.samplingareapage.samplingtype = SamplingType.MMVWINR wx.FindWindowById(wx.ID_FORWARD).Enable(True) else: self.parent.samplingareapage.samplingtype = SamplingType.MUNITSR self.drawtype = "rectangle" else: - if self.parent.samplingareapage.samplingtype in [ + if self.parent.samplingareapage.samplingtype in { SamplingType.MVWIN, SamplingType.MMVWINR, SamplingType.MMVWINC, - ]: + }: self.parent.samplingareapage.samplingtype = SamplingType.MMVWINC wx.FindWindowById(wx.ID_FORWARD).Enable(True) else: @@ -1739,11 +1739,11 @@ def SampleFrameChanged(self, region): def OnEnterPage(self, event): """Function during entering""" - if self.parent.samplingareapage.samplingtype in [ + if self.parent.samplingareapage.samplingtype in { SamplingType.MVWIN, SamplingType.MMVWINC, SamplingType.MMVWINR, - ]: + }: self.numregions = 1 else: self.numregions = int(self.parent.drawunits.numregions) diff --git a/gui/wxpython/startup/locdownload.py b/gui/wxpython/startup/locdownload.py index 588d30960d0..5a4eddbaf9d 100644 --- a/gui/wxpython/startup/locdownload.py +++ b/gui/wxpython/startup/locdownload.py @@ -302,7 +302,7 @@ def _change_download_btn_label( def OnDownload(self, event): """Handle user-initiated action of download""" button_label = self.parent.download_button.GetLabel() - if button_label in (_("Download"), _("Do&wnload")): + if button_label in {_("Download"), _("Do&wnload")}: self._change_download_btn_label( label=self._abort_btn_label, tooltip=self._abort_btn_tooltip, diff --git a/gui/wxpython/tplot/frame.py b/gui/wxpython/tplot/frame.py index 8844918e706..39c90fd3ef1 100755 --- a/gui/wxpython/tplot/frame.py +++ b/gui/wxpython/tplot/frame.py @@ -840,13 +840,13 @@ def drawR(self): xdata = [] ydata = [] for keys, values in self.timeDataR[name].items(): - if keys in [ + if keys in { "temporalType", "granularity", "validTopology", "unit", "temporalDataType", - ]: + }: continue xdata.append(self.convert(values["start_datetime"])) ydata.append(values["value"]) @@ -896,13 +896,13 @@ def drawVCats(self): ydata = [] xcsv = [] for keys, values in self.timeDataV[name_cat[0]][name_cat[1]].items(): - if keys in [ + if keys in { "temporalType", "granularity", "validTopology", "unit", "temporalDataType", - ]: + }: continue xdata.append(self.convert(values["start_datetime"])) if values["value"] == "": @@ -956,13 +956,13 @@ def drawV(self): ydata = [] xcsv = [] for keys, values in self.timeDataV[name].items(): - if keys in [ + if keys in { "temporalType", "granularity", "validTopology", "unit", "temporalDataType", - ]: + }: continue xdata.append(self.convert(values["start_datetime"])) ydata.append(values["value"]) diff --git a/gui/wxpython/vdigit/mapwindow.py b/gui/wxpython/vdigit/mapwindow.py index 650fe65261a..5e554d46393 100644 --- a/gui/wxpython/vdigit/mapwindow.py +++ b/gui/wxpython/vdigit/mapwindow.py @@ -102,7 +102,7 @@ def _mouseMovingToDigitizingInfo(self, x, y): ) if ( self.toolbar.GetAction() != "addLine" - or self.toolbar.GetAction("type") not in ("line", "boundary") + or self.toolbar.GetAction("type") not in {"line", "boundary"} or len(self.polycoords) == 0 ): # we cannot provide info, so find out if it is something new @@ -293,7 +293,7 @@ def OnLeftDownAddLine(self, event): except: return - if self.toolbar.GetAction("type") in ["point", "centroid"]: + if self.toolbar.GetAction("type") in {"point", "centroid"}: # add new point / centroiud east, north = self.Pixel2Cell(self.mouse["begin"]) nfeat, fids = self.digit.AddFeature( @@ -342,7 +342,7 @@ def OnLeftDownAddLine(self, event): addRecordDlg.ShowModal() addRecordDlg.Destroy() - elif self.toolbar.GetAction("type") in ["line", "boundary", "area"]: + elif self.toolbar.GetAction("type") in {"line", "boundary", "area"}: # add new point to the line self.polycoords.append(self.Pixel2Cell(event.GetPosition())) self.DrawLines(pdc=self.pdcTmp) @@ -467,7 +467,7 @@ def OnLeftDownMoveLine(self, event): self.moveInfo["id"] = list() # set pen - if self.toolbar.GetAction() in ["moveVertex", "editLine"]: + if self.toolbar.GetAction() in {"moveVertex", "editLine"}: pcolor = UserSettings.Get( group="vdigit", key="symbol", subkey=["highlight", "color"] ) @@ -612,7 +612,7 @@ def OnLeftDownUndo(self, event): action = self.toolbar.GetAction() if ( action == "addLine" - and self.toolbar.GetAction("type") in ["line", "boundary", "area"] + and self.toolbar.GetAction("type") in {"line", "boundary", "area"} ) or action == "editLine": # add line or boundary -> remove last point from the line try: @@ -635,7 +635,7 @@ def OnLeftDownUndo(self, event): self.UpdateMap(render=False, renderVector=False) - elif action in [ + elif action in { "deleteLine", "deleteArea", "moveLine", @@ -652,11 +652,11 @@ def OnLeftDownUndo(self, event): "queryLine", "breakLine", "typeConv", - ]: + }: # various tools -> unselected selected features self.digit.GetDisplay().SetSelected([]) - if action in ["moveLine", "moveVertex", "editLine"] and hasattr( + if action in {"moveLine", "moveVertex", "editLine"} and hasattr( self, "moveInfo" ): del self.moveInfo @@ -708,7 +708,7 @@ def _onLeftDown(self, event): event.Skip() return - if action not in ("moveVertex", "addVertex", "removeVertex", "editLine"): + if action not in {"moveVertex", "addVertex", "removeVertex", "editLine"}: # set pen self.pen = wx.Pen( colour=UserSettings.Get( @@ -725,7 +725,7 @@ def _onLeftDown(self, event): style=wx.SOLID, ) - if action in ("addVertex", "removeVertex", "splitLines"): + if action in {"addVertex", "removeVertex", "splitLines"}: # unselect self.digit.GetDisplay().SetSelected([]) @@ -735,7 +735,7 @@ def _onLeftDown(self, event): elif action == "editLine" and hasattr(self, "moveInfo"): self.OnLeftDownEditLine(event) - elif action in ("moveLine", "moveVertex", "editLine") and not hasattr( + elif action in {"moveLine", "moveVertex", "editLine"} and not hasattr( self, "moveInfo" ): self.OnLeftDownMoveLine(event) @@ -743,7 +743,7 @@ def _onLeftDown(self, event): elif action in ("displayAttrs" "displayCats"): self.OnLeftDownDisplayCA(event) - elif action in ("copyCats", "copyAttrs"): + elif action in {"copyCats", "copyAttrs"}: self.OnLeftDownCopyCA(event) elif action == "copyLine": @@ -762,7 +762,7 @@ def OnLeftUpVarious(self, event): nselected = 0 action = self.toolbar.GetAction() # -> delete line || move line || move vertex - if action in ("moveVertex", "editLine"): + if action in {"moveVertex", "editLine"}: if len(self.digit.GetDisplay().GetSelected()) == 0: nselected = int( self.digit.GetDisplay().SelectLineByPoint(pos1)["line"] != -1 @@ -799,7 +799,7 @@ def OnLeftUpVarious(self, event): self.UpdateMap(render=False) - elif action in ("copyCats", "copyAttrs"): + elif action in {"copyCats", "copyAttrs"}: if not hasattr(self, "copyCatsIds"): # 'from' -> select by point nselected = int( @@ -851,7 +851,7 @@ def OnLeftUpVarious(self, event): ) if nselected > 0: - if action in ("moveLine", "moveVertex") and hasattr(self, "moveInfo"): + if action in {"moveLine", "moveVertex"} and hasattr(self, "moveInfo"): # get pseudoDC id of objects which should be redrawn if action == "moveLine": # -> move line @@ -893,7 +893,7 @@ def OnLeftUpVarious(self, event): else: # no vector object found if not ( - action in ("moveLine", "moveVertex") + action in {"moveLine", "moveVertex"} and hasattr(self, "moveInfo") and len(self.moveInfo["id"]) > 0 ): @@ -910,7 +910,7 @@ def OnLeftUpModifyLine(self, event): if not pointOnLine: return - if self.toolbar.GetAction() in ["splitLine", "addVertex"]: + if self.toolbar.GetAction() in {"splitLine", "addVertex"}: self.UpdateMap(render=False) # highlight object self.DrawCross( pdc=self.pdcTmp, @@ -1031,7 +1031,7 @@ def _onLeftUp(self, event): self.mouse["begin"] = self.mouse["end"] action = self.toolbar.GetAction() - if action in ( + if action in { "deleteLine", "deleteArea", "moveLine", @@ -1046,10 +1046,10 @@ def _onLeftUp(self, event): "breakLine", "typeConv", "connectLine", - ): + }: self.OnLeftUpVarious(event) - elif action in ("splitLine", "addVertex", "removeVertex"): + elif action in {"splitLine", "addVertex", "removeVertex"}: self.OnLeftUpModifyLine(event) elif action == "copyLine": @@ -1067,7 +1067,7 @@ def _onLeftUp(self, event): def _onRightDown(self, event): # digitization tool (confirm action) action = self.toolbar.GetAction() - if action in ("moveLine", "moveVertex") and hasattr(self, "moveInfo"): + if action in {"moveLine", "moveVertex"} and hasattr(self, "moveInfo"): pFrom = self.moveInfo["begin"] pTo = self.Pixel2Cell(event.GetPosition()) @@ -1094,11 +1094,11 @@ def _onRightDown(self, event): def _onRightUp(self, event): """Right mouse button released (confirm action)""" action = self.toolbar.GetAction() - if action == "addLine" and self.toolbar.GetAction("type") in [ + if action == "addLine" and self.toolbar.GetAction("type") in { "line", "boundary", "area", - ]: + }: # -> add new line / boundary try: mapName = self.toolbar.GetLayer().GetName() @@ -1197,7 +1197,7 @@ def _onRightUp(self, event): fid, ] ) - elif action in ("copyCats", "copyAttrs") and hasattr(self, "copyCatsIds"): + elif action in {"copyCats", "copyAttrs"} and hasattr(self, "copyCatsIds"): if action == "copyCats": if ( self.digit.CopyCats( @@ -1292,17 +1292,17 @@ def _onMouseMoving(self, event): ) action = self.toolbar.GetAction() - if action == "addLine" and self.toolbar.GetAction("type") in [ + if action == "addLine" and self.toolbar.GetAction("type") in { "line", "boundary", "area", - ]: + }: if len(self.polycoords) > 0: self.MouseDraw( pdc=self.pdcTmp, begin=self.Cell2Pixel(self.polycoords[-1]) ) - elif action in ["moveLine", "moveVertex", "editLine"] and hasattr( + elif action in {"moveLine", "moveVertex", "editLine"} and hasattr( self, "moveInfo" ): dx = self.mouse["end"][0] - self.mouse["begin"][0] @@ -1313,7 +1313,7 @@ def _onMouseMoving(self, event): # move line for id in self.moveInfo["id"]: self.pdcTmp.TranslateId(id, dx, dy) - elif action in ["moveVertex", "editLine"]: + elif action in {"moveVertex", "editLine"}: # move vertex -> # (vertex, left vertex, left line, # right vertex, right line) diff --git a/gui/wxpython/vdigit/wxdisplay.py b/gui/wxpython/vdigit/wxdisplay.py index afbc877f73f..49603ba415a 100644 --- a/gui/wxpython/vdigit/wxdisplay.py +++ b/gui/wxpython/vdigit/wxdisplay.py @@ -403,10 +403,10 @@ def _definePen(self, rtype): elif rtype == TYPE_DIRECTION: key = "direction" - if key not in ("direction", "area", "isle"): + if key not in {"direction", "area", "isle"}: self.topology[key] += 1 - if key in ("area", "isle"): + if key in {"area", "isle"}: pen = wx.TRANSPARENT_PEN if key == "area": brush = wx.Brush(self.settings[key]["color"], wx.SOLID) diff --git a/gui/wxpython/vnet/dialogs.py b/gui/wxpython/vnet/dialogs.py index e8bbd4520a7..c82bc25a157 100644 --- a/gui/wxpython/vnet/dialogs.py +++ b/gui/wxpython/vnet/dialogs.py @@ -1541,7 +1541,7 @@ def __init__( for dataSel in dataSelects: selPanels[dataSel[0]] = Panel(parent=self) - if dataSel[0] in ["input", "output"]: + if dataSel[0] in {"input", "output"}: self.inputData[dataSel[0]] = dataSel[2]( parent=selPanels[dataSel[0]], size=(-1, -1), type="vector" ) @@ -1933,18 +1933,18 @@ def Populate(self): def OnGetItemText(self, item, col): val = self.data.GetValue(item, col) - if col in [1, 2]: + if col in {1, 2}: val = RadiansToDegrees(val) return str(val) def SetVirtualData(self, row, column, text): """Set data to table""" - if column in [1, 2, 3]: + if column in {1, 2, 3}: try: text = float(text) except ValueError: return - if column in [1, 2]: + if column in {1, 2}: text = DegreesToRadians(text) # Tested allowed range of values diff --git a/gui/wxpython/vnet/vnet_core.py b/gui/wxpython/vnet/vnet_core.py index ff0e2fd7fe6..97f40458008 100644 --- a/gui/wxpython/vnet/vnet_core.py +++ b/gui/wxpython/vnet/vnet_core.py @@ -769,7 +769,7 @@ def _setInputParams(self, analysis, params, flags): inParams.append(col + "=" + params[colInptF]) for layer in ["arc_layer", "node_layer", "turn_layer", "turn_cat_layer"]: - if not flags["t"] and layer in ["turn_layer", "turn_cat_layer"]: + if not flags["t"] and layer in {"turn_layer", "turn_cat_layer"}: continue # TODO if flags["t"] and layer == "node_layer": diff --git a/gui/wxpython/vnet/vnet_data.py b/gui/wxpython/vnet/vnet_data.py index edf17a7fc8b..51c3d374cef 100644 --- a/gui/wxpython/vnet/vnet_data.py +++ b/gui/wxpython/vnet/vnet_data.py @@ -177,7 +177,7 @@ def InputsErrorMsgs( "turn_cat_layer": _("unique categories layer"), } for layer, layerLabel in vals.items(): - if layer in ["turn_layer", "turn_cat_layer"] and not flags["t"]: + if layer in {"turn_layer", "turn_cat_layer"} and not flags["t"]: continue if layer in inv_params: if params[layer]: @@ -633,7 +633,7 @@ def SetParams(self, params, flags): def GetParam(self, param): invParams = [] - if param in [ + if param in { "input", "arc_layer", "node_layer", @@ -642,7 +642,7 @@ def GetParam(self, param): "node_column", "turn_layer", "turn_cat_layer", - ]: + }: invParams = self._getInvalidParams(self.params) if invParams: @@ -698,10 +698,10 @@ def _getInvalidParams(self, params): invParams.append(col) continue - if columnchoices[params[col]]["type"] not in [ + if columnchoices[params[col]]["type"] not in { "integer", "double precision", - ]: + }: invParams.append(col) continue @@ -1398,7 +1398,7 @@ def PopRow(self, values): def DataValidator(self, row, col, value): """Angle recalculation due to value changing""" - if col not in [1, 2]: + if col not in {1, 2}: return if col == 1: diff --git a/gui/wxpython/web_services/cap_interface.py b/gui/wxpython/web_services/cap_interface.py index d73cdb5bc84..51ef21c4392 100644 --- a/gui/wxpython/web_services/cap_interface.py +++ b/gui/wxpython/web_services/cap_interface.py @@ -229,7 +229,7 @@ def GetLayerData(self, param): title = self.xml_ns.NsOws("Title") name = self.xml_ns.NsOws("Identifier") - if self.layer_node is None and param in ["title", "name"]: + if self.layer_node is None and param in {"title", "name"}: return None elif self.layer_node is None: return [] @@ -373,7 +373,7 @@ def IsRequestable(self): def GetLayerData(self, param): """Get layer data""" - if self.layer_node is None and param in ["title", "name"]: + if self.layer_node is None and param in {"title", "name"}: return None elif self.layer_node is None: return [] diff --git a/gui/wxpython/wxgui.py b/gui/wxpython/wxgui.py index 03166ad851b..201eb0fe738 100644 --- a/gui/wxpython/wxgui.py +++ b/gui/wxpython/wxgui.py @@ -135,10 +135,10 @@ def process_opt(opts, args): """Process command-line arguments""" workspaceFile = None for o, a in opts: - if o in ("-h", "--help"): + if o in {"-h", "--help"}: printHelp() - elif o in ("-w", "--workspace"): + elif o in {"-w", "--workspace"}: if a != "": workspaceFile = str(a) else: diff --git a/gui/wxpython/wxplot/base.py b/gui/wxpython/wxplot/base.py index 37e62b1b2c3..3eb43fd5f43 100755 --- a/gui/wxpython/wxplot/base.py +++ b/gui/wxpython/wxplot/base.py @@ -212,7 +212,7 @@ def InitRasterOpts(self, rasterList, plottype): rdict[r] = {} # initialize sub-dictionaries for each raster in the list rdict[r]["units"] = "" - if ret["units"] not in ("(none)", '"none"', "", None): + if ret["units"] not in {"(none)", '"none"', "", None}: rdict[r]["units"] = ret["units"] rdict[r]["plegend"] = r # use fully-qualified names @@ -284,9 +284,9 @@ def InitRasterPairs(self, rasterList, plottype): rdict[rpair][0]["units"] = "" rdict[rpair][1]["units"] = "" - if ret0["units"] not in ("(none)", '"none"', "", None): + if ret0["units"] not in {"(none)", '"none"', "", None}: rdict[rpair][0]["units"] = ret0["units"] - if ret1["units"] not in ("(none)", '"none"', "", None): + if ret1["units"] not in {"(none)", '"none"', "", None}: rdict[rpair][1]["units"] = ret1["units"] rdict[rpair]["plegend"] = ( From 614e1e78339adb5bdf3576d36c80623c497340fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sun, 30 Jun 2024 20:43:27 -0400 Subject: [PATCH 035/293] style(temporal): Fixes literal-membership (PLR6201) (#3953) Concerns Pylint rule "use-set-for-membership / R6201" Using `ruff check --output-format=concise --select PLR6201 --preview --unsafe-fixes --fix 'temporal/' 'python/grass/temporal'`. --- .../temporal/abstract_space_time_dataset.py | 4 ++-- python/grass/temporal/list_stds.py | 4 ++-- python/grass/temporal/register.py | 8 +++---- python/grass/temporal/temporal_algebra.py | 6 ++--- python/grass/temporal/temporal_granularity.py | 22 +++++++++---------- .../temporal/temporal_raster3d_algebra.py | 4 ++-- .../grass/temporal/temporal_raster_algebra.py | 4 ++-- python/grass/temporal/univar_statistics.py | 2 +- temporal/t.info/t.info.py | 2 +- temporal/t.rast.export/t.rast.export.py | 2 +- temporal/t.rast.gapfill/t.rast.gapfill.py | 2 +- temporal/t.rast.list/t.rast.list.py | 4 ++-- 12 files changed, 32 insertions(+), 32 deletions(-) diff --git a/python/grass/temporal/abstract_space_time_dataset.py b/python/grass/temporal/abstract_space_time_dataset.py index 1a17e6b06f2..be0c0b169c4 100644 --- a/python/grass/temporal/abstract_space_time_dataset.py +++ b/python/grass/temporal/abstract_space_time_dataset.py @@ -779,7 +779,7 @@ def sample_by_dataset(self, stds, method=None, spatial=False, dbif=None): relations = [ relation.upper().strip() for relation in relations - if relation not in ["start", "overlap", "contain"] + if relation not in {"start", "overlap", "contain"} ] # print(relations) @@ -1723,7 +1723,7 @@ def _update_where_statement_by_spatial_extent( if not spatial_relation: spatial_relation = "overlaps" - elif spatial_relation not in ["overlaps", "is_contained", "contains"]: + elif spatial_relation not in {"overlaps", "is_contained", "contains"}: self.msgr.error( _( "Invalid spatial relation <{}> requested." diff --git a/python/grass/temporal/list_stds.py b/python/grass/temporal/list_stds.py index e913409fd60..58fdef5024a 100644 --- a/python/grass/temporal/list_stds.py +++ b/python/grass/temporal/list_stds.py @@ -399,7 +399,7 @@ def check_columns(column_names, output_format, element_type): ) # This method expects a list of objects for gap detection - if method in ["delta", "deltagaps", "gran"]: + if method in {"delta", "deltagaps", "gran"}: if not columns: if output_format == "list": # Only one column is needed. @@ -451,7 +451,7 @@ def check_columns(column_names, output_format, element_type): # End with error for the old, custom formats. Proper formats simply return # empty result whatever empty is for each format (e.g., empty list for JSON). - if not rows and (output_format in ["plain", "line"]): + if not rows and (output_format in {"plain", "line"}): dbif.close() gs.fatal( _( diff --git a/python/grass/temporal/register.py b/python/grass/temporal/register.py index 149a304f628..12431a14408 100644 --- a/python/grass/temporal/register.py +++ b/python/grass/temporal/register.py @@ -449,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 in ["rast", "raster"]: + if type in {"rast", "raster"}: ds = dataset_factory("strds", dataset) - elif type in ["raster_3d", "rast3d", "raster3d"]: + elif type in {"raster_3d", "rast3d", "raster3d"}: ds = dataset_factory("str3ds", dataset) - elif type in ["vect", "vector"]: + elif type in {"vect", "vector"}: ds = dataset_factory("stvds", dataset) ds.select(dbif) ds.update_from_registered_maps(dbif) @@ -620,7 +620,7 @@ def register_map_object_list( 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 type in {"raster", "raster_3d", "rast", "rast3d"}: if ( map_layer.metadata.get_min() is None and map_layer.metadata.get_max() is None diff --git a/python/grass/temporal/temporal_algebra.py b/python/grass/temporal/temporal_algebra.py index fa91a3a71b4..ac5486face8 100644 --- a/python/grass/temporal/temporal_algebra.py +++ b/python/grass/temporal/temporal_algebra.py @@ -1064,7 +1064,7 @@ def overlay_map_extent(self, mapA, mapB, bool_op=None, temp_op="l", copy=False): mapA.set_spatial_extent(overlay_ext) else: returncode = 0 - elif bool_op in ["or", "xor"]: + elif bool_op in {"or", "xor"}: overlay_ext = mapA.spatial_union(mapB) if overlay_ext is not None: mapA.set_spatial_extent(overlay_ext) @@ -2162,14 +2162,14 @@ def eval_global_var(self, gvar, maplist): # Get value for function name from dictionary. tfuncval = tfuncdict[tfunc] # Check if value has to be transferred to datetime object for comparison. - if tfunc in [ + if tfunc in { "START_DATE", "END_DATE", "START_TIME", "END_TIME", "START_DATETIME", "END_DATETIME", - ]: + }: timeobj = string_to_datetime(value.replace('"', "")) value = timeobj.date() boolname = self.eval_datetime_str(tfuncval, comp_op, value) diff --git a/python/grass/temporal/temporal_granularity.py b/python/grass/temporal/temporal_granularity.py index 207404cf3d2..339f65c77c3 100644 --- a/python/grass/temporal/temporal_granularity.py +++ b/python/grass/temporal/temporal_granularity.py @@ -773,7 +773,7 @@ def compute_common_absolute_time_granularity(gran_list, start_date_list=None): num, granule = common_granule.split() - if granule in ["seconds", "second"]: + if granule in {"seconds", "second"}: # If the start seconds are different between the start dates # set the granularity to one second for start_time in start_date_list: @@ -786,7 +786,7 @@ def compute_common_absolute_time_granularity(gran_list, start_date_list=None): else: return "1 second" - if granule in ["minutes", "minute"]: + if granule in {"minutes", "minute"}: # If the start minutes are different between the start dates # set the granularity to one minute for start_time in start_date_list: @@ -799,7 +799,7 @@ def compute_common_absolute_time_granularity(gran_list, start_date_list=None): else: return "1 minute" - if granule in ["hours", "hour"]: + if granule in {"hours", "hour"}: # If the start hours are different between the start dates # set the granularity to one hour for start_time in start_date_list: @@ -812,7 +812,7 @@ def compute_common_absolute_time_granularity(gran_list, start_date_list=None): else: return "1 hour" - if granule in ["days", "day"]: + if granule in {"days", "day"}: # If the start days are different between the start dates # set the granularity to one day for start_time in start_date_list: @@ -825,7 +825,7 @@ def compute_common_absolute_time_granularity(gran_list, start_date_list=None): else: return "1 day" - if granule in ["months", "month"]: + if granule in {"months", "month"}: # If the start months are different between the start dates # set the granularity to one month for start_time in start_date_list: @@ -946,42 +946,42 @@ def compute_common_absolute_time_granularity_simple(gran_list): num, gran = entry.split() - if gran in ["seconds", "second"]: + if gran in {"seconds", "second"}: has_seconds = True min_gran = min(min_gran, 0) max_gran = max(max_gran, 0) seconds.append(int(num)) - if gran in ["minutes", "minute"]: + if gran in {"minutes", "minute"}: has_minutes = True min_gran = min(min_gran, 1) max_gran = max(max_gran, 1) minutes.append(int(num)) - if gran in ["hours", "hour"]: + if gran in {"hours", "hour"}: has_hours = True min_gran = min(min_gran, 2) max_gran = max(max_gran, 2) hours.append(int(num)) - if gran in ["days", "day"]: + if gran in {"days", "day"}: has_days = True min_gran = min(min_gran, 3) max_gran = max(max_gran, 3) days.append(int(num)) - if gran in ["months", "month"]: + if gran in {"months", "month"}: has_months = True min_gran = min(min_gran, 4) max_gran = max(max_gran, 4) months.append(int(num)) - if gran in ["years", "year"]: + if gran in {"years", "year"}: has_years = True min_gran = min(min_gran, 5) max_gran = max(max_gran, 5) diff --git a/python/grass/temporal/temporal_raster3d_algebra.py b/python/grass/temporal/temporal_raster3d_algebra.py index dc136bc7fcd..337407a7b44 100644 --- a/python/grass/temporal/temporal_raster3d_algebra.py +++ b/python/grass/temporal/temporal_raster3d_algebra.py @@ -133,14 +133,14 @@ def p_ts_neighbor_operation(self, t): cmdstring = "%s" % (map_new.cmd_list) elif "cmd_list" not in dir(map_new) and len(t) == 5: cmdstring = "%s" % (map_n.get_id()) - elif "cmd_list" in dir(map_new) and len(t) in (9, 11): + elif "cmd_list" in dir(map_new) and len(t) in {9, 11}: cmdstring = "%s[%s,%s,%s]" % ( map_new.cmd_list, row_neighbor, col_neighbor, depth_neighbor, ) - elif "cmd_list" not in dir(map_new) and len(t) in (9, 11): + elif "cmd_list" not in dir(map_new) and len(t) in {9, 11}: cmdstring = "%s[%s,%s,%s]" % ( map_n.get_id(), row_neighbor, diff --git a/python/grass/temporal/temporal_raster_algebra.py b/python/grass/temporal/temporal_raster_algebra.py index dddbab87ed5..8c40ae9974d 100644 --- a/python/grass/temporal/temporal_raster_algebra.py +++ b/python/grass/temporal/temporal_raster_algebra.py @@ -177,13 +177,13 @@ def p_ts_neighbour_operation(self, t): cmdstring = "%s" % (map_new.cmd_list) elif "cmd_list" not in dir(map_new) and len(t) == 5: cmdstring = "%s" % (map_n.get_id()) - elif "cmd_list" in dir(map_new) and len(t) in (7, 9): + elif "cmd_list" in dir(map_new) and len(t) in {7, 9}: cmdstring = "%s[%s,%s]" % ( map_new.cmd_list, row_neigbour, col_neigbour, ) - elif "cmd_list" not in dir(map_new) and len(t) in (7, 9): + elif "cmd_list" not in dir(map_new) and len(t) in {7, 9}: cmdstring = "%s[%s,%s]" % ( map_n.get_id(), row_neigbour, diff --git a/python/grass/temporal/univar_statistics.py b/python/grass/temporal/univar_statistics.py index 5ae6f255d1b..ddf55345b34 100755 --- a/python/grass/temporal/univar_statistics.py +++ b/python/grass/temporal/univar_statistics.py @@ -351,7 +351,7 @@ def print_vector_dataset_univar_statistics( + fs ) string += "min" + fs + "max" + fs + "range" - if type in ("point", "centroid"): + if type in {"point", "centroid"}: string += ( fs + "mean" diff --git a/temporal/t.info/t.info.py b/temporal/t.info/t.info.py index dd86e8123ef..c8e2427bb4b 100755 --- a/temporal/t.info/t.info.py +++ b/temporal/t.info/t.info.py @@ -119,7 +119,7 @@ def main(): dataset.select(dbif) - if history and type_ in ["strds", "stvds", "str3ds"]: + if history and type_ in {"strds", "stvds", "str3ds"}: dataset.print_history() return diff --git a/temporal/t.rast.export/t.rast.export.py b/temporal/t.rast.export/t.rast.export.py index 830b7499606..3be0fb06255 100755 --- a/temporal/t.rast.export/t.rast.export.py +++ b/temporal/t.rast.export/t.rast.export.py @@ -131,7 +131,7 @@ def main(): if not os.access(directory, os.W_OK): grass.fatal(_("Directory {} is not writable".format(directory))) - if _type and _format in ["pack", "AAIGrid"]: + if _type and _format in {"pack", "AAIGrid"}: grass.warning( _("Type options is not working with pack format, " "it will be skipped") ) diff --git a/temporal/t.rast.gapfill/t.rast.gapfill.py b/temporal/t.rast.gapfill/t.rast.gapfill.py index 3c3bb517b50..fc9a2f4a8c3 100755 --- a/temporal/t.rast.gapfill/t.rast.gapfill.py +++ b/temporal/t.rast.gapfill/t.rast.gapfill.py @@ -121,7 +121,7 @@ def main(): for _map in maps: if _map.get_id() is None: count += 1 - if sp.get_temporal_type() == "absolute" and tsuffix in ["gran", "time"]: + if sp.get_temporal_type() == "absolute" and tsuffix in {"gran", "time"}: _id = "{ba}@{ma}".format(ba=base, ma=mapset) else: map_name = tgis.create_numeric_suffix(base, num + count, tsuffix) diff --git a/temporal/t.rast.list/t.rast.list.py b/temporal/t.rast.list/t.rast.list.py index 5278cc60f82..651bc69b956 100755 --- a/temporal/t.rast.list/t.rast.list.py +++ b/temporal/t.rast.list/t.rast.list.py @@ -167,7 +167,7 @@ def main(): # except for setting it to an empty string which does not have a precedence # in the current code and the behavior is unclear. separator = "," - if output_format in ["json", "yaml"] and header: + if output_format in {"json", "yaml"} and header: gs.fatal( message_option_value_excludes_flag( option_name="format", @@ -185,7 +185,7 @@ def main(): # Pipe is currently not supported at all. separator = "," - if method in ["delta", "deltagaps", "gran"]: + if method in {"delta", "deltagaps", "gran"}: if order: gs.fatal( message_option_value_excludes_option( From ffa270b5aba362b3d6526b33d10e313863f333ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edouard=20Choini=C3=A8re?= <27212526+echoix@users.noreply.github.com> Date: Sun, 30 Jun 2024 20:44:03 -0400 Subject: [PATCH 036/293] style: Fixes literal-membership (PLR6201) for other code (#3954) Concerns Pylint rule "use-set-for-membership / R6201" Using `ruff check --output-format=concise --select PLR6201 --preview --unsafe-fixes --fix`, but reverting some changes where it wasn't clear if it was safe. --- display/d.mon/render_cmd.py | 2 +- lib/init/grass.py | 20 +++++++++---------- man/build_class_graphical.py | 2 +- man/build_class_rest.py | 2 +- man/build_html.py | 4 ++-- man/build_rest.py | 4 ++-- man/parser_standard_options.py | 2 +- python/grass/benchmark/plots.py | 4 ++-- python/grass/jupyter/map3d.py | 2 +- python/grass/pygrass/modules/grid/grid.py | 4 ++-- .../grass/pygrass/modules/interface/flag.py | 2 +- .../grass/pygrass/modules/interface/module.py | 2 +- python/grass/pygrass/raster/__init__.py | 2 +- python/grass/pygrass/raster/abstract.py | 6 +++--- python/grass/pygrass/raster/category.py | 2 +- python/grass/pygrass/vector/abstract.py | 6 +++--- python/grass/script/array.py | 8 ++++---- python/grass/script/core.py | 8 ++++---- python/grass/script/db.py | 6 +++--- python/grass/script/task.py | 6 +++--- .../data/script_using_temporary_region.py | 2 +- python/grass/script/utils.py | 2 +- .../r.neighbors/testsuite/test_r_neighbors.py | 2 +- scripts/d.polar/d.polar.py | 2 +- scripts/d.rast.edit/d.rast.edit.py | 2 +- scripts/db.univar/db.univar.py | 6 +++--- scripts/g.extension/g.extension.py | 8 ++++---- scripts/g.manual/g.manual.py | 4 ++-- scripts/i.image.mosaic/i.image.mosaic.py | 2 +- scripts/i.spectral/i.spectral.py | 2 +- scripts/r.in.aster/r.in.aster.py | 2 +- scripts/r.in.srtm/r.in.srtm.py | 2 +- scripts/r.in.wms/wms_base.py | 4 ++-- scripts/r.in.wms/wms_drv.py | 6 +++--- scripts/r.reclass.area/r.reclass.area.py | 2 +- .../v.db.renamecolumn/v.db.renamecolumn.py | 2 +- scripts/v.db.update/v.db.update.py | 2 +- scripts/v.dissolve/tests/conftest.py | 2 +- scripts/v.dissolve/v.dissolve.py | 8 ++++---- scripts/v.in.e00/v.in.e00.py | 2 +- scripts/v.report/v.report.py | 4 ++-- utils/g.html2man/ggroff.py | 8 ++++---- utils/thumbnails.py | 4 ++-- 43 files changed, 87 insertions(+), 87 deletions(-) diff --git a/display/d.mon/render_cmd.py b/display/d.mon/render_cmd.py index 41a69e502aa..7678a1442e4 100644 --- a/display/d.mon/render_cmd.py +++ b/display/d.mon/render_cmd.py @@ -173,7 +173,7 @@ def read_stdin(cmd): width, height, legfile = read_env_file(os.path.join(path, "env")) if mon.startswith("wx"): mapfile = tempfile.NamedTemporaryFile(dir=path).name - if cmd[0] in ("d.barscale", "d.legend", "d.northarrow", "d.legend.vect"): + if cmd[0] in {"d.barscale", "d.legend", "d.northarrow", "d.legend.vect"}: mapfile += ".png" else: mapfile += ".ppm" diff --git a/lib/init/grass.py b/lib/init/grass.py index 7ac89e0c0c7..4ca298a2ed2 100755 --- a/lib/init/grass.py +++ b/lib/init/grass.py @@ -624,7 +624,7 @@ def read_gui(gisrc, default_gui): grass_gui = default_gui # FIXME oldtcltk, gis.m, d.m no longer exist (remove this around 7.2) - if grass_gui in ["d.m", "gis.m", "oldtcltk", "tcltk"]: + if grass_gui in {"d.m", "gis.m", "oldtcltk", "tcltk"}: warning(_("GUI <%s> not supported in this version") % grass_gui) grass_gui = default_gui @@ -648,7 +648,7 @@ def check_gui(expected_gui): # Check if we are running X windows by checking the DISPLAY variable if os.getenv("DISPLAY") or WINDOWS or MACOS: # Check if python is working properly - if expected_gui in ("wxpython", "gtext"): + if expected_gui in {"wxpython", "gtext"}: nul = open(os.devnull, "w") p = Popen( [os.environ["GRASS_PYTHON"]], @@ -1476,13 +1476,13 @@ def get_shell(): def get_grass_env_file(sh, grass_config_dir): """Get name of the shell-specific GRASS environment (rc) file""" - if sh in ["csh", "tcsh"]: + if sh in {"csh", "tcsh"}: grass_env_file = os.path.join(grass_config_dir, "cshrc") - elif sh in ["bash", "msh", "cygwin", "sh"]: + elif sh in {"bash", "msh", "cygwin", "sh"}: grass_env_file = os.path.join(grass_config_dir, "bashrc") elif sh == "zsh": grass_env_file = os.path.join(grass_config_dir, "zshrc") - elif sh in ["cmd", "powershell"]: + elif sh in {"cmd", "powershell"}: grass_env_file = os.path.join(grass_config_dir, "env.bat") else: grass_env_file = os.path.join(grass_config_dir, "bashrc") @@ -1959,7 +1959,7 @@ def print_params(params): for arg in params: if arg == "path": sys.stdout.write("%s\n" % GISBASE) - elif arg in ["python_path", "python-path"]: + elif arg in {"python_path", "python-path"}: sys.stdout.write("%s\n" % gpath("etc", "python")) elif arg == "arch": val = grep("ARCH", linesplat) @@ -2272,7 +2272,7 @@ def main(): # A shell is activated when using TTY. # Explicit --[g]text in command line, forces a shell to be activated. - force_shell = grass_gui in ["text", "gtext"] + force_shell = grass_gui in {"text", "gtext"} use_shell = io_is_interactive() or force_shell if not use_shell: # If no shell is used, always use actual GUI as GUI, even when "text" is set as @@ -2518,16 +2518,16 @@ def main(): _("Launching <%s> GUI in the background, please wait...") % grass_gui ) - if sh in ["csh", "tcsh"]: + if sh in {"csh", "tcsh"}: shell_process = csh_startup(mapset_settings.full_mapset, grass_env_file) - elif sh in ["zsh"]: + elif sh in {"zsh"}: shell_process = sh_like_startup( mapset_settings.full_mapset, mapset_settings.location, grass_env_file, "zsh", ) - elif sh in ["bash", "msh", "cygwin"]: + elif sh in {"bash", "msh", "cygwin"}: shell_process = sh_like_startup( mapset_settings.full_mapset, mapset_settings.location, diff --git a/man/build_class_graphical.py b/man/build_class_graphical.py index 081884501bd..27b4a9965b9 100644 --- a/man/build_class_graphical.py +++ b/man/build_class_graphical.py @@ -145,7 +145,7 @@ def generate_page_for_category( ) output.write(header_graphical_index_tmpl) - if module_family.lower() not in ["general", "postscript"]: + if module_family.lower() not in {"general", "postscript"}: if module_family == "raster3d": # covert keyword to nice form module_family = "3D raster" diff --git a/man/build_class_rest.py b/man/build_class_rest.py index 8a86a60a42c..c1de3d623d0 100644 --- a/man/build_class_rest.py +++ b/man/build_class_rest.py @@ -24,7 +24,7 @@ f = open(filename + ".tmp", "wb") write_rest_header(f, "GRASS GIS %s Reference Manual: %s" % (grass_version, modclass)) -if modclass.lower() not in ["general", "miscellaneous", "postscript"]: +if modclass.lower() not in {"general", "miscellaneous", "postscript"}: f.write( modclass_intro_tmpl.substitute( modclass=modclass, modclass_lower=modclass.lower() diff --git a/man/build_html.py b/man/build_html.py index 2d1714091fd..e075ebaafc1 100644 --- a/man/build_html.py +++ b/man/build_html.py @@ -436,9 +436,9 @@ def html_files(cls=None, ignore_gui=True): for cmd in sorted(os.listdir(html_dir)): if ( cmd.endswith(".html") - and (cls in [None, "*"] or cmd.startswith(cls + ".")) + and (cls in {None, "*"} or cmd.startswith(cls + ".")) and (cls != "*" or len(cmd.split(".")) >= 3) - and cmd not in ["full_index.html", "index.html"] + and cmd not in {"full_index.html", "index.html"} and cmd not in exclude_mods and (ignore_gui and not cmd.startswith("wxGUI.") or not ignore_gui) ): diff --git a/man/build_rest.py b/man/build_rest.py index ef684fd2de6..ea1dd7692d2 100644 --- a/man/build_rest.py +++ b/man/build_rest.py @@ -314,9 +314,9 @@ def rest_files(cls=None): for cmd in sorted(os.listdir(rest_dir)): if ( cmd.endswith(".txt") - and (cls in [None, "*"] or cmd.startswith(cls + ".")) + and (cls in {None, "*"} or cmd.startswith(cls + ".")) and (cls != "*" or len(cmd.split(".")) >= 3) - and cmd not in ["full_index.txt", "index.txt"] + and cmd not in {"full_index.txt", "index.txt"} and cmd not in exclude_mods and not cmd.startswith("wxGUI.") ): diff --git a/man/parser_standard_options.py b/man/parser_standard_options.py index 6a6d769ab5e..be779fa06c4 100644 --- a/man/parser_standard_options.py +++ b/man/parser_standard_options.py @@ -222,7 +222,7 @@ def _repr_html_(self): options = OptTable(parse_options(cfile.readlines(), startswith=args.startswith)) outform = args.format - if outform in ["csv", "html"]: + if outform in {"csv", "html"}: print(getattr(options, outform)(), file=args.output) args.output.close() else: diff --git a/python/grass/benchmark/plots.py b/python/grass/benchmark/plots.py index 10442b65f3c..3cb66a84d45 100644 --- a/python/grass/benchmark/plots.py +++ b/python/grass/benchmark/plots.py @@ -71,7 +71,7 @@ def nprocs_plot(results, filename=None, title=None, metric="time"): plt.plot(x, result.times, label=result.label) plt.fill_between(x, mins, maxes, color="gray", alpha=0.3) ylabel = "Time [s]" - elif metric in ["speedup", "efficiency"]: + elif metric in {"speedup", "efficiency"}: ylabel = metric.title() plt.plot(x, getattr(result, metric), label=result.label) else: @@ -96,7 +96,7 @@ def nprocs_plot(results, filename=None, title=None, metric="time"): plt.title(title) elif metric == "times": plt.title("Execution time by processing elements") - elif metric in ["speedup", "efficiency"]: + elif metric in {"speedup", "efficiency"}: plt.title(f"{metric.title()} by processing elements") if filename: plt.savefig(filename) diff --git a/python/grass/jupyter/map3d.py b/python/grass/jupyter/map3d.py index 6a8e78a4c4d..acad4040d31 100644 --- a/python/grass/jupyter/map3d.py +++ b/python/grass/jupyter/map3d.py @@ -132,7 +132,7 @@ def cleanup(tmpdir): "because pyvirtualdisplay cannot be imported" ).format(screen_backend) ) - elif screen_backend in ["simple", "pyvirtualdisplay"]: + elif screen_backend in {"simple", "pyvirtualdisplay"}: self._screen_backend = screen_backend else: raise ValueError( diff --git a/python/grass/pygrass/modules/grid/grid.py b/python/grass/pygrass/modules/grid/grid.py index 0fd6038a078..77fff0e2f3a 100644 --- a/python/grass/pygrass/modules/grid/grid.py +++ b/python/grass/pygrass/modules/grid/grid.py @@ -479,7 +479,7 @@ def __init__( # if overlap > 0, r.patch won't work properly if not patch_backend: self.patch_backend = "RasterRow" - elif patch_backend not in ("r.patch", "RasterRow"): + elif patch_backend not in {"r.patch", "RasterRow"}: raise RuntimeError( _("Parameter patch_backend must be 'r.patch' or 'RasterRow'") ) @@ -625,7 +625,7 @@ def define_mapset_inputs(self): """Add the mapset information to the input maps""" for inmap in self.module.inputs: inm = self.module.inputs[inmap] - if inm.type in ("raster", "vector") and inm.value: + if inm.type in {"raster", "vector"} and inm.value: if "@" not in inm.value: mset = get_mapset_raster(inm.value) inm.value = inm.value + "@%s" % mset diff --git a/python/grass/pygrass/modules/interface/flag.py b/python/grass/pygrass/modules/interface/flag.py index 410d17b640a..0fe024cd2a3 100644 --- a/python/grass/pygrass/modules/interface/flag.py +++ b/python/grass/pygrass/modules/interface/flag.py @@ -27,7 +27,7 @@ def __init__(self, xflag=None, diz=None): diz = read.element2dict(xflag) if xflag is not None else diz self.name = diz["name"] self.special = ( - True if self.name in ("verbose", "overwrite", "quiet", "run") else False + True if self.name in {"verbose", "overwrite", "quiet", "run"} else False ) self.description = diz.get("description", None) self.default = diz.get("default", None) diff --git a/python/grass/pygrass/modules/interface/module.py b/python/grass/pygrass/modules/interface/module.py index 9f0c911a4b1..b9c298b4a6f 100644 --- a/python/grass/pygrass/modules/interface/module.py +++ b/python/grass/pygrass/modules/interface/module.py @@ -563,7 +563,7 @@ def __init__(self, cmd, *args, **kargs): tree = fromstring(self.xml) for e in tree: - if e.tag not in ("parameter", "flag"): + if e.tag not in {"parameter", "flag"}: self.__setattr__(e.tag, GETFROMTAG[e.tag](e)) # diff --git a/python/grass/pygrass/raster/__init__.py b/python/grass/pygrass/raster/__init__.py index 448d4e3b1db..a0b7c166307 100644 --- a/python/grass/pygrass/raster/__init__.py +++ b/python/grass/pygrass/raster/__init__.py @@ -313,7 +313,7 @@ def _get_mode(self): return self._mode def _set_mode(self, mode): - if mode and mode.lower() not in ("r", "w", "rw"): + if mode and mode.lower() not in {"r", "w", "rw"}: str_err = _("Mode type: {0} not supported ('r', 'w','rw')") raise ValueError(str_err.format(mode)) self._mode = mode diff --git a/python/grass/pygrass/raster/abstract.py b/python/grass/pygrass/raster/abstract.py index 4967dc6487b..3b05e69f269 100644 --- a/python/grass/pygrass/raster/abstract.py +++ b/python/grass/pygrass/raster/abstract.py @@ -323,7 +323,7 @@ def _get_mtype(self): def _set_mtype(self, mtype): """Private method to change the Raster type""" - if mtype.upper() not in ("CELL", "FCELL", "DCELL"): + if mtype.upper() not in {"CELL", "FCELL", "DCELL"}: str_err = "Raster type: {0} not supported ('CELL','FCELL','DCELL')" raise ValueError(_(str_err).format(mtype)) self._mtype = mtype @@ -335,7 +335,7 @@ def _get_mode(self): return self._mode def _set_mode(self, mode): - if mode.upper() not in ("R", "W"): + if mode.upper() not in {"R", "W"}: str_err = _("Mode type: {0} not supported ('r', 'w')") raise ValueError(str_err.format(mode)) self._mode = mode @@ -346,7 +346,7 @@ def _get_overwrite(self): return self._overwrite def _set_overwrite(self, overwrite): - if overwrite not in (True, False): + if overwrite not in {True, False}: str_err = _("Overwrite type: {0} not supported (True/False)") raise ValueError(str_err.format(overwrite)) self._overwrite = overwrite diff --git a/python/grass/pygrass/raster/category.py b/python/grass/pygrass/raster/category.py index b43a10ae48d..88229752e1f 100644 --- a/python/grass/pygrass/raster/category.py +++ b/python/grass/pygrass/raster/category.py @@ -66,7 +66,7 @@ def _get_mtype(self): return self._mtype def _set_mtype(self, mtype): - if mtype.upper() not in ("CELL", "FCELL", "DCELL"): + if mtype.upper() not in {"CELL", "FCELL", "DCELL"}: raise ValueError(_("Raster type: {0} not supported".format(mtype))) self._mtype = mtype self._gtype = RTYPE[self.mtype]["grass type"] diff --git a/python/grass/pygrass/vector/abstract.py b/python/grass/pygrass/vector/abstract.py index bafbb006d6c..900b1d6a2ea 100644 --- a/python/grass/pygrass/vector/abstract.py +++ b/python/grass/pygrass/vector/abstract.py @@ -368,11 +368,11 @@ def open( # update the overwrite attribute self.overwrite = overwrite if overwrite is not None else self.overwrite # check if the mode is valid - if self.mode not in ("r", "rw", "w"): + if self.mode not in {"r", "rw", "w"}: raise ValueError("Mode not supported. Use one of: 'r', 'rw', 'w'.") # check if the map exist - if self.exist() and self.mode in ("r", "rw"): + if self.exist() and self.mode in {"r", "rw"}: # open in READ mode if self.mode == "r": openvect = libvect.Vect_open_old2( @@ -392,7 +392,7 @@ def open( openvect = libvect.Vect_open_new(self.c_mapinfo, self.name, with_z) self.dblinks = DBlinks(self.c_mapinfo) - if self.mode in ("w", "rw") and tab_cols: + if self.mode in {"w", "rw"} and tab_cols: # create a link link = Link( layer, diff --git a/python/grass/script/array.py b/python/grass/script/array.py index cd5e05325ec..c9abafbc24c 100644 --- a/python/grass/script/array.py +++ b/python/grass/script/array.py @@ -155,7 +155,7 @@ def __new__(cls, mapname=None, null=None, dtype=numpy.double, env=None): else: raise ValueError(_("Invalid kind <%s>") % kind) - if size not in [1, 2, 4, 8]: + if size not in {1, 2, 4, 8}: raise ValueError(_("Invalid size <%d>") % size) gcore.run_command( @@ -202,7 +202,7 @@ def write(self, mapname, title=None, null=None, overwrite=None, quiet=None): raise ValueError(_("Invalid FP size <%d>") % size) size = None elif kind in "biu": - if size not in [1, 2, 4]: + if size not in {1, 2, 4}: raise ValueError(_("Invalid integer size <%d>") % size) flags = None else: @@ -267,7 +267,7 @@ def __new__(cls, mapname=None, null=None, dtype=numpy.double, env=None): else: raise ValueError(_("Invalid kind <%s>") % kind) - if size not in [1, 2, 4, 8]: + if size not in {1, 2, 4, 8}: raise ValueError(_("Invalid size <%d>") % size) gcore.run_command( @@ -310,7 +310,7 @@ def write(self, mapname, null=None, overwrite=None, quiet=None): if size != 4 and size != 8: raise ValueError(_("Invalid FP size <%d>") % size) elif kind in "biu": - if size not in [1, 2, 4, 8]: + if size not in {1, 2, 4, 8}: raise ValueError(_("Invalid integer size <%d>") % size) flags = "i" else: diff --git a/python/grass/script/core.py b/python/grass/script/core.py index d027f941309..b0b8e11e826 100644 --- a/python/grass/script/core.py +++ b/python/grass/script/core.py @@ -884,7 +884,7 @@ def _parse_opts(lines): flags[var[5:]] = bool(int(val)) elif var.startswith("opt_"): options[var[4:]] = val - elif var in ["GRASS_OVERWRITE", "GRASS_VERBOSE"]: + elif var in {"GRASS_OVERWRITE", "GRASS_VERBOSE"}: os.environ[var] = val else: raise SyntaxError( @@ -1288,13 +1288,13 @@ def region_env(region3d=False, flags=None, env=None, **kwargs): grass_region = "" for line in fd.readlines(): key, value = map(lambda x: x.strip(), line.split(":", 1)) - if kwargs and key not in ("proj", "zone"): + if kwargs and key not in {"proj", "zone"}: continue if ( not kwargs and not region3d and key - in ( + in { "top", "bottom", "cols3", @@ -1303,7 +1303,7 @@ def region_env(region3d=False, flags=None, env=None, **kwargs): "e-w resol3", "n-s resol3", "t-b resol", - ) + } ): continue diff --git a/python/grass/script/db.py b/python/grass/script/db.py index abb7ad1764b..421c57267d7 100644 --- a/python/grass/script/db.py +++ b/python/grass/script/db.py @@ -66,7 +66,7 @@ def db_describe(table, env=None, **args): if key.startswith("Column "): n = int(key.split(" ")[1]) cols.insert(n, f[1:]) - elif key in ["ncols", "nrows"]: + elif key in {"ncols", "nrows"}: result[key] = int(f[1]) else: result[key] = f[1:] @@ -238,7 +238,7 @@ def db_begin_transaction(driver): :return: SQL command as string """ - if driver in ("sqlite", "pg"): + if driver in {"sqlite", "pg"}: return "BEGIN" if driver == "mysql": return "START TRANSACTION" @@ -250,6 +250,6 @@ def db_commit_transaction(driver): :return: SQL command as string """ - if driver in ("sqlite", "pg", "mysql"): + if driver in {"sqlite", "pg", "mysql"}: return "COMMIT" return "" diff --git a/python/grass/script/task.py b/python/grass/script/task.py index eedf34738e7..6c181b9e27b 100644 --- a/python/grass/script/task.py +++ b/python/grass/script/task.py @@ -88,7 +88,7 @@ def get_name(self): """Get task name""" if sys.platform == "win32": name, ext = os.path.splitext(self.name) - if ext in (".py", ".sh"): + if ext in {".py", ".sh"}: return name else: return self.name @@ -641,7 +641,7 @@ def cmdtuple_to_list(cmd): cmdList.append("--" + flag) for k, v in cmd[1].items(): - if k in ("flags", "help", "verbose", "quiet", "overwrite"): + if k in {"flags", "help", "verbose", "quiet", "overwrite"}: continue if " " in v: v = '"%s"' % v @@ -667,7 +667,7 @@ def cmdlist_to_tuple(cmd): dcmd[str(key)] = value.replace('"', "") elif item[:2] == "--": # long flags flag = item[2:] - if flag in ("help", "verbose", "quiet", "overwrite"): + if flag in {"help", "verbose", "quiet", "overwrite"}: dcmd[str(flag)] = True elif len(item) == 2 and item[0] == "-": # -> flags if "flags" not in dcmd: diff --git a/python/grass/script/testsuite/data/script_using_temporary_region.py b/python/grass/script/testsuite/data/script_using_temporary_region.py index 2735814582c..5bb5a4cda26 100755 --- a/python/grass/script/testsuite/data/script_using_temporary_region.py +++ b/python/grass/script/testsuite/data/script_using_temporary_region.py @@ -48,7 +48,7 @@ def main(): remaining = None nesting = 0 map_name = None - elif len(sys.argv) not in [3, 4]: + elif len(sys.argv) not in {3, 4}: gs.fatal( "Usage: