diff --git a/imagery/i.pca/testsuite/test_pca.py b/imagery/i.pca/testsuite/test_pca.py new file mode 100644 index 00000000000..9da7991f876 --- /dev/null +++ b/imagery/i.pca/testsuite/test_pca.py @@ -0,0 +1,174 @@ +""" +Name: i.pca test +Purpose: Tests i.pca. + +Author: Hamed Elgizery - hamedashraf2004@gmail.com +Copyright: (C) 2024 by Hamed Elgizery 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 + + +class TestReport(TestCase): + @classmethod + def setUpClass(cls): + """Use temporary region settings""" + cls.runModule("g.region", raster="lsat7_2002_10@PERMANENT") + cls.use_temp_region() + + @classmethod + def tearDownClass(cls): + cls.runModule( + "g.remove", + flags="f", + type="raster", + name="lsat7_2002_pca.1,lsat7_2002_pca.2,lsat7_2002_pca.3,lsat7_2002_pca.4,lsat7_2002_pca.6", + ) + cls.del_temp_region() + + def test_pca_sample(self): + """Testing pca sample""" + self.assertModule( + "i.pca", + input="lsat7_2002_10,lsat7_2002_20,lsat7_2002_30,lsat7_2002_40,lsat7_2002_50,lsat7_2002_70", + output="lsat7_2002_pca", + ) + + lsat7_2002_pca_info_out = """north=228513 + south=214975.5 + east=645012 + west=629992.5 + nsres=28.5 + ewres=28.5 + rows=475 + cols=527 + cells=250325 + datatype=CELL + ncats=0 + comments=\"Eigen values, (vectors), and [percent importance]:PC1 4334.35 ( 0.2824, 0.3342, 0.5092,-0.0087, 0.5264, 0.5217) [83.04%]PC2 588.31 ( 0.2541, 0.1885, 0.2923,-0.7428,-0.5110,-0.0403) [11.27%]PC3 239.22 ( 0.3801, 0.3819, 0.2681, 0.6238,-0.4000,-0.2980) [ 4.58%]PC4 32.85 ( 0.1752,-0.0191,-0.4053, 0.1593,-0.4435, 0.7632) [ 0.63%]PC5 20.73 (-0.6170,-0.2514, 0.6059, 0.1734,-0.3235, 0.2330) [ 0.40%]PC6 4.08 (-0.5475, 0.8021,-0.2282,-0.0607,-0.0208, 0.0252) [ 0.08%]i.pca input=\"lsat7_2002_10,lsat7_2002_20,lsat7_2002_30,lsat7_2002_40\,lsat7_2002_50,lsat7_2002_70\" output=\"lsat7_2002_pca\" rescale=0,255 \percent=99" """ + + lsat7_2002_pca_univar_out = [ + """n=250325 + null_cells=0 + cells=250325 + min=0 + max=255 + range=255 + mean=60.6958074503146 + mean_of_abs=60.6958074503146 + stddev=32.8850846003739 + variance=1081.42878917375 + coeff_var=54.1801583697417 + sum=15193678 + first_quartile=36 + median=51 + third_quartile=77 + percentile_90=101""", + """n=250325 + null_cells=0 + cells=250325 + min=0 + max=255 + range=255 + mean=106.099418755618 + mean_of_abs=106.099418755618 + stddev=26.4487056926998 + variance=699.534032819051 + coeff_var=24.928228639612 + sum=26559337 + first_quartile=88 + median=104 + third_quartile=121 + percentile_90=137""", + """n=250325 + null_cells=0 + cells=250325 + min=0 + max=255 + range=255 + mean=74.1768980325577 + mean_of_abs=74.1768980325577 + stddev=14.1956266450161 + variance=201.515815844691 + coeff_var=19.1375307158104 + sum=18568332 + first_quartile=67 + median=74 + third_quartile=81 + percentile_90=88""", + """n=250325 + null_cells=0 + cells=250325 + min=0 + max=255 + range=255 + mean=113.285145311096 + mean_of_abs=113.285145311096 + stddev=10.689092045444 + variance=114.256688755974 + coeff_var=9.43556369733241 + sum=28358104 + first_quartile=109 + median=114 + third_quartile=118 + percentile_90=122""", + """n=250325 + null_cells=0 + cells=250325 + min=0 + max=255 + range=255 + mean=110.346713272745 + mean_of_abs=110.346713272745 + stddev=8.43087149474902 + variance=71.0795941609716 + coeff_var=7.64034672596938 + sum=27622541 + first_quartile=106 + median=110 + third_quartile=114 + percentile_90=118""", + """n=250325 + null_cells=0 + cells=250325 + min=0 + max=255 + range=255 + mean=115.238465994208 + mean_of_abs=115.238465994208 + stddev=8.97064489504434 + variance=80.4724698329851 + coeff_var=7.78441887233665 + sum=28847069 + first_quartile=110 + median=115 + third_quartile=121 + percentile_90=126""", + ] + + for i in range(1, 7): + # Asserting the results givien from r.info + self.assertRasterFitsInfo( + raster=f"lsat7_2002_pca.{i}", + reference=lsat7_2002_pca_info_out, + precision=3, + ) + + # Asserting the results givien from r.univar + univar_out = lsat7_2002_pca_univar_out[i - 1] + self.assertModuleKeyValue( + "r.univar", + flags="eg", + map=f"lsat7_2002_pca.{i}", + reference=univar_out, + precision=3, + sep="=", + ) + + +if __name__ == "__main__": + from grass.gunittest.main import test + + test() diff --git a/pyproject.toml b/pyproject.toml index b59eed0043c..e06ff451bd0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,4 +38,4 @@ timeout = 300 [tool.bandit] exclude_dirs = ["./testsuite", "*/tests/*", "*/testsuite/*", "utils/test_generate_last_commit_file.py"] -skips = ["B324","B110", "B101", "B112", "B404"] +skips = ["B324","B110", "B101", "B112", "B311", "B404"] diff --git a/python/grass/script/core.py b/python/grass/script/core.py index f35e487548b..d5e529bdaeb 100644 --- a/python/grass/script/core.py +++ b/python/grass/script/core.py @@ -28,9 +28,11 @@ import random import shlex from tempfile import NamedTemporaryFile +from pathlib import Path from .utils import KeyValue, parse_key_val, basename, encode, decode, try_remove from grass.exceptions import ScriptError, CalledModuleError +from grass.grassdb.manage import resolve_mapset_path # subprocess wrapper that uses shell on Windows @@ -1697,9 +1699,16 @@ def mapsets(search_path=False, env=None): # interface to `g.proj -c` -def create_location( - dbase, - location, +def create_location(*args, **kwargs): + if "location" in kwargs: + kwargs["name"] = kwargs["location"] + del kwargs["location"] + return create_project(*args, **kwargs) + + +def create_project( + path, + name=None, epsg=None, proj4=None, filename=None, @@ -1709,44 +1718,34 @@ def create_location( desc=None, overwrite=False, ): - """Create new location + """Create new project Raise ScriptError on error. - :param str dbase: path to GRASS database - :param str location: location name to create - :param epsg: if given create new location based on EPSG code - :param proj4: if given create new location based on Proj4 definition - :param str filename: if given create new location based on georeferenced file - :param str wkt: if given create new location based on WKT definition + :param str path: path to GRASS database or project; if path to database, project + name must be specified with name parameter + :param str name: project name to create + :param epsg: if given create new project based on EPSG code + :param proj4: if given create new project based on Proj4 definition + :param str filename: if given create new project based on georeferenced file + :param str wkt: if given create new project based on WKT definition (can be path to PRJ file or WKT string) :param datum: GRASS format datum code :param datum_trans: datum transformation parameters (used for epsg and proj4) - :param desc: description of the location (creates MYNAME file) - :param bool overwrite: True to overwrite location if exists(WARNING: - ALL DATA from existing location ARE DELETED!) + :param desc: description of the project (creates MYNAME file) + :param bool overwrite: True to overwrite project if exists (WARNING: + ALL DATA from existing project ARE DELETED!) """ - # create dbase if not exists - if not os.path.exists(dbase): - os.mkdir(dbase) + # Add default mapset to project path if needed + if not name: + path = os.path.join(path, "PERMANENT") - # check if location already exists - if os.path.exists(os.path.join(dbase, location)): - if not overwrite: - warning(_("Location <%s> already exists. Operation canceled.") % location) - return - else: - warning( - _("Location <%s> already exists and will be overwritten") % location - ) - shutil.rmtree(os.path.join(dbase, location)) + # resolve dbase, location and mapset + mapset_path = resolve_mapset_path(path=path, location=name) - stdin = None - kwargs = dict() - if datum: - kwargs["datum"] = datum - if datum_trans: - kwargs["datum_trans"] = datum_trans + # create dbase if not exists + if not os.path.exists(mapset_path.directory): + os.mkdir(mapset_path.directory) # Lazy-importing to avoid circular dependencies. # pylint: disable=import-outside-toplevel @@ -1761,8 +1760,30 @@ def create_location( if epsg or proj4 or filename or wkt: # The names don't really matter here. tmp_gisrc, env = create_environment( - dbase, "", "", env=env + mapset_path.directory, mapset_path.location, mapset_path.mapset, env=env + ) + + # check if location already exists + if Path(mapset_path.directory, mapset_path.location).exists(): + if not overwrite: + fatal( + _("Location <%s> already exists. Operation canceled.") + % mapset_path.location, + env=env, + ) + warning( + _("Location <%s> already exists and will be overwritten") + % mapset_path.location, + env=env, ) + shutil.rmtree(os.path.join(mapset_path.directory, mapset_path.location)) + + stdin = None + kwargs = dict() + if datum: + kwargs["datum"] = datum + if datum_trans: + kwargs["datum_trans"] = datum_trans if epsg: ps = pipe_command( @@ -1770,7 +1791,7 @@ def create_location( quiet=True, flags="t", epsg=epsg, - location=location, + location=mapset_path.location, stderr=PIPE, env=env, **kwargs, @@ -1781,7 +1802,7 @@ def create_location( quiet=True, flags="t", proj4=proj4, - location=location, + location=mapset_path.location, stderr=PIPE, env=env, **kwargs, @@ -1791,28 +1812,33 @@ def create_location( "g.proj", quiet=True, georef=filename, - location=location, + location=mapset_path.location, stderr=PIPE, env=env, ) elif wkt: if os.path.isfile(wkt): ps = pipe_command( - "g.proj", quiet=True, wkt=wkt, location=location, stderr=PIPE, env=env + "g.proj", + quiet=True, + wkt=wkt, + location=mapset_path.location, + stderr=PIPE, + env=env, ) else: ps = pipe_command( "g.proj", quiet=True, wkt="-", - location=location, + location=mapset_path.location, stderr=PIPE, stdin=PIPE, env=env, ) stdin = encode(wkt) else: - _create_location_xy(dbase, location) + _create_location_xy(mapset_path.directory, mapset_path.location) if epsg or proj4 or filename or wkt: error = ps.communicate(stdin)[1] @@ -1821,7 +1847,7 @@ def create_location( if ps.returncode != 0 and error: raise ScriptError(repr(error)) - _set_location_description(dbase, location, desc) + _set_location_description(mapset_path.directory, mapset_path.location, desc) def _set_location_description(path, location, text): diff --git a/python/grass/script/tests/grass_script_core_location_test.py b/python/grass/script/tests/grass_script_core_location_test.py index 3cd89a44213..db8696cfeea 100644 --- a/python/grass/script/tests/grass_script_core_location_test.py +++ b/python/grass/script/tests/grass_script_core_location_test.py @@ -99,6 +99,37 @@ def test_with_different_path(tmp_path): assert epsg == "EPSG:3358" +def test_path_only(tmp_path): + desired_location = "desired" + full_path = tmp_path / desired_location + gs.create_location(full_path, epsg="3358") + mapset_path = full_path / "PERMANENT" + wkt_file = mapset_path / "PROJ_WKT" + assert full_path.exists() + assert mapset_path.exists() + assert wkt_file.exists() + with gs.setup.init(full_path): + gs.run_command("g.gisenv", set=f"GISDBASE={tmp_path}") + gs.run_command("g.gisenv", set=f"LOCATION_NAME={desired_location}") + gs.run_command("g.gisenv", set="MAPSET=PERMANENT") + epsg = gs.parse_command("g.proj", flags="g")["srid"] + assert epsg == "EPSG:3358" + + +def test_create_project(tmp_path): + name = "desired" + gs.create_project(tmp_path / name, epsg="3358") + assert (tmp_path / name).exists() + wkt_file = tmp_path / name / "PERMANENT" / "PROJ_WKT" + assert wkt_file.exists() + with gs.setup.init(tmp_path / name): + gs.run_command("g.gisenv", set=f"GISDBASE={tmp_path}") + gs.run_command("g.gisenv", set=f"LOCATION_NAME={name}") + gs.run_command("g.gisenv", set="MAPSET=PERMANENT") + epsg = gs.parse_command("g.proj", flags="g")["srid"] + assert epsg == "EPSG:3358" + + def test_files(tmp_path): """Check expected files are created""" bootstrap_location = "bootstrap"