From a20e74f1208b36b9cf3b8aabac02804725fb4770 Mon Sep 17 00:00:00 2001 From: changliao1025 Date: Tue, 7 May 2024 08:20:13 -0700 Subject: [PATCH 1/3] reorg for configuration --- docs/conf.py | 6 +- examples/create_model_condfiguration.py | 6 +- .../susquehanna/run_simulation_hexagon.py | 16 +++--- examples/susquehanna/run_simulation_latlon.py | 16 +++--- examples/susquehanna/run_simulation_mpas.py | 8 +-- examples/susquehanna/run_simulation_square.py | 16 +++--- pyhexwatershed/change_json_key_value.py | 16 ------ pyhexwatershed/configuration/__init__.py | 0 .../configuration/change_json_key_value.py | 39 +++++++++++++ .../create_template_configuration_file.py} | 0 pyhexwatershed/configuration/path_manager.py | 57 +++++++++++++++++++ .../read_configuration_file.py} | 16 +++--- 12 files changed, 138 insertions(+), 58 deletions(-) delete mode 100644 pyhexwatershed/change_json_key_value.py create mode 100755 pyhexwatershed/configuration/__init__.py create mode 100644 pyhexwatershed/configuration/change_json_key_value.py rename pyhexwatershed/{pyhexwatershed_create_template_configuration_file.py => configuration/create_template_configuration_file.py} (100%) create mode 100644 pyhexwatershed/configuration/path_manager.py rename pyhexwatershed/{pyhexwatershed_read_model_configuration_file.py => configuration/read_configuration_file.py} (94%) diff --git a/docs/conf.py b/docs/conf.py index a6bc0f3..6cbdb25 100755 --- a/docs/conf.py +++ b/docs/conf.py @@ -21,7 +21,7 @@ import sys sys.path.insert(0, os.path.abspath('..')) -import pyhexwatershed +import pyhexwatershed.configuration as configuration # -- General configuration --------------------------------------------- @@ -55,9 +55,9 @@ # the built documents. # # The short X.Y version. -version = pyhexwatershed.__version__ +version = configuration.__version__ # The full version, including alpha/beta/rc tags. -release = pyhexwatershed.__version__ +release = configuration.__version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/examples/create_model_condfiguration.py b/examples/create_model_condfiguration.py index 092ce1d..e759f3a 100644 --- a/examples/create_model_condfiguration.py +++ b/examples/create_model_condfiguration.py @@ -11,8 +11,8 @@ logging.warning('is the time pyhexwatershed simulation started.') from pyhexwatershed.classes.pycase import hexwatershedcase -from pyhexwatershed.pyhexwatershed_read_model_configuration_file import pyhexwatershed_read_model_configuration_file -from pyhexwatershed.pyhexwatershed_create_template_configuration_file import pyhexwatershed_create_template_configuration_file +from pyhexwatershed.configuration.read_configuration_file import pyhexwatershed_read_model_configuration_file +from pyhexwatershed.configuration.create_template_configuration_file import pyhexwatershed_create_template_configuration_file sMesh_type = 'latlon' @@ -30,7 +30,7 @@ sWorkspace_output = '/compyfs/liao313/04model/pyhexwatershed/susquehanna' -sFilename_configuration_in = sPath + '/tests/configurations/pyhexwatershed_susquehanna_latlon.json' +sFilename_configuration_in = sPath + '/tests/configurations/pyhexwatershed_susquehanna_latlon.json' sWorkspace_data = realpath( sPath + '/data/susquehanna' ) oPyhexwatershed = pyhexwatershed_create_template_configuration_file(sFilename_configuration_in, sWorkspace_input, sWorkspace_output, iFlag_use_mesh_dem_in = 0, sMesh_type_in=sMesh_type, iCase_index_in = iCase_index, sDate_in = sDate) print(oPyhexwatershed.tojson()) diff --git a/examples/susquehanna/run_simulation_hexagon.py b/examples/susquehanna/run_simulation_hexagon.py index 087a75d..e65104b 100644 --- a/examples/susquehanna/run_simulation_hexagon.py +++ b/examples/susquehanna/run_simulation_hexagon.py @@ -3,7 +3,7 @@ from pathlib import Path from os.path import realpath -from pyhexwatershed.pyhexwatershed_read_model_configuration_file import pyhexwatershed_read_model_configuration_file +from pyhexwatershed.configuration.read_configuration_file import pyhexwatershed_read_model_configuration_file #=========================== #setup workspace path @@ -37,7 +37,7 @@ oPyhexwatershed = pyhexwatershed_read_model_configuration_file(sFilename_configuration_in,\ iCase_index_in=iCase_index, \ dResolution_meter_in = dResolution_meter, \ - sDate_in= sDate, sMesh_type_in= sMesh_type) + sDate_in= sDate, sMesh_type_in= sMesh_type) if oPyhexwatershed.iFlag_global==1: #global simulation @@ -48,7 +48,7 @@ if oPyhexwatershed.iFlag_multiple_outlet ==1: pass else: - #single basin example + #single basin example #use dem oPyhexwatershed.pPyFlowline.aBasin[0].dLatitude_outlet_degree=39.4620 oPyhexwatershed.pPyFlowline.aBasin[0].dLongitude_outlet_degree=-76.0093 @@ -61,16 +61,16 @@ aCell_out = oPyhexwatershed.assign_elevation_to_cells() aCell_new = oPyhexwatershed.update_outlet(aCell_origin) oPyhexwatershed.pPyFlowline.export() - oPyhexwatershed.export_config_to_json() + oPyhexwatershed.export_config_to_json() oPyhexwatershed.run_hexwatershed() oPyhexwatershed.analyze() oPyhexwatershed.export() pass - + iCase_index = iCase_index + 1 - - - + + + print('Finished') diff --git a/examples/susquehanna/run_simulation_latlon.py b/examples/susquehanna/run_simulation_latlon.py index 5756175..f1a1088 100644 --- a/examples/susquehanna/run_simulation_latlon.py +++ b/examples/susquehanna/run_simulation_latlon.py @@ -3,7 +3,7 @@ from pathlib import Path from os.path import realpath -from pyhexwatershed.pyhexwatershed_read_model_configuration_file import pyhexwatershed_read_model_configuration_file +from pyhexwatershed.configuration.read_configuration_file import pyhexwatershed_read_model_configuration_file #=========================== #setup workspace path @@ -37,7 +37,7 @@ oPyhexwatershed = pyhexwatershed_read_model_configuration_file(sFilename_configuration_in,\ iCase_index_in=iCase_index, \ dResolution_meter_in = dResolution_meter, \ - sDate_in= sDate, sMesh_type_in= sMesh_type) + sDate_in= sDate, sMesh_type_in= sMesh_type) if oPyhexwatershed.iFlag_global==1: #global simulation @@ -48,7 +48,7 @@ if oPyhexwatershed.iFlag_multiple_outlet ==1: pass else: - #single basin example + #single basin example #use dem oPyhexwatershed.pPyFlowline.aBasin[0].dLatitude_outlet_degree=39.4620 oPyhexwatershed.pPyFlowline.aBasin[0].dLongitude_outlet_degree=-76.0093 @@ -61,16 +61,16 @@ aCell_out = oPyhexwatershed.assign_elevation_to_cells() aCell_new = oPyhexwatershed.update_outlet(aCell_origin) oPyhexwatershed.pPyFlowline.export() - oPyhexwatershed.export_config_to_json() + oPyhexwatershed.export_config_to_json() oPyhexwatershed.run_hexwatershed() oPyhexwatershed.analyze() oPyhexwatershed.export() pass - + iCase_index = iCase_index + 1 - - - + + + print('Finished') diff --git a/examples/susquehanna/run_simulation_mpas.py b/examples/susquehanna/run_simulation_mpas.py index 195771a..2e7a726 100644 --- a/examples/susquehanna/run_simulation_mpas.py +++ b/examples/susquehanna/run_simulation_mpas.py @@ -3,7 +3,7 @@ from pathlib import Path from os.path import realpath -from pyhexwatershed.pyhexwatershed_read_model_configuration_file import pyhexwatershed_read_model_configuration_file +from pyhexwatershed.configuration.read_configuration_file import pyhexwatershed_read_model_configuration_file #=========================== #setup workspace path @@ -33,7 +33,7 @@ sDate='20220801' oPyhexwatershed = pyhexwatershed_read_model_configuration_file(sFilename_configuration_in,\ - iCase_index_in=iCase_index, sDate_in= sDate, sMesh_type_in= sMesh_type) + iCase_index_in=iCase_index, sDate_in= sDate, sMesh_type_in= sMesh_type) if oPyhexwatershed.iFlag_global==1: #global simulation @@ -44,7 +44,7 @@ if oPyhexwatershed.iFlag_multiple_outlet ==1: pass else: - #single basin example + #single basin example #mpas mesh has elevation built-in oPyhexwatershed.pPyFlowline.aBasin[0].dLatitude_outlet_degree=39.4620 oPyhexwatershed.pPyFlowline.aBasin[0].dLongitude_outlet_degree=-76.0093 @@ -60,7 +60,7 @@ oPyhexwatershed.analyze() oPyhexwatershed.export() pass - pass + pass print('Finished') diff --git a/examples/susquehanna/run_simulation_square.py b/examples/susquehanna/run_simulation_square.py index d3b2e29..f172471 100644 --- a/examples/susquehanna/run_simulation_square.py +++ b/examples/susquehanna/run_simulation_square.py @@ -3,7 +3,7 @@ from pathlib import Path from os.path import realpath -from pyhexwatershed.pyhexwatershed_read_model_configuration_file import pyhexwatershed_read_model_configuration_file +from pyhexwatershed.configuration.read_configuration_file import pyhexwatershed_read_model_configuration_file #=========================== #setup workspace path @@ -37,7 +37,7 @@ oPyhexwatershed = pyhexwatershed_read_model_configuration_file(sFilename_configuration_in,\ iCase_index_in=iCase_index, \ dResolution_meter_in = dResolution_meter, \ - sDate_in= sDate, sMesh_type_in= sMesh_type) + sDate_in= sDate, sMesh_type_in= sMesh_type) if oPyhexwatershed.iFlag_global==1: #global simulation @@ -48,7 +48,7 @@ if oPyhexwatershed.iFlag_multiple_outlet ==1: pass else: - #single basin example + #single basin example #use dem oPyhexwatershed.pPyFlowline.aBasin[0].dLatitude_outlet_degree=39.4620 oPyhexwatershed.pPyFlowline.aBasin[0].dLongitude_outlet_degree=-76.0093 @@ -61,16 +61,16 @@ aCell_out = oPyhexwatershed.assign_elevation_to_cells() aCell_new = oPyhexwatershed.update_outlet(aCell_origin) oPyhexwatershed.pPyFlowline.export() - oPyhexwatershed.export_config_to_json() + oPyhexwatershed.export_config_to_json() oPyhexwatershed.run_hexwatershed() oPyhexwatershed.analyze() oPyhexwatershed.export() pass - + iCase_index = iCase_index + 1 - - - + + + print('Finished') diff --git a/pyhexwatershed/change_json_key_value.py b/pyhexwatershed/change_json_key_value.py deleted file mode 100644 index 46da235..0000000 --- a/pyhexwatershed/change_json_key_value.py +++ /dev/null @@ -1,16 +0,0 @@ -import json - -def change_json_key_value(sFilename_json_in, sKey, new_value, iFlag_basin_in= None): - # Read the JSON file - with open(sFilename_json_in, 'r') as file: - data = json.load(file) - - # Update the value associated with the specified sKey - if iFlag_basin_in is None: - data[sKey] = new_value - else: - data[0][sKey] = new_value - - # Write the updated data back to the JSON file - with open(sFilename_json_in, 'w') as file: - json.dump(data, file, indent=4) \ No newline at end of file diff --git a/pyhexwatershed/configuration/__init__.py b/pyhexwatershed/configuration/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/pyhexwatershed/configuration/change_json_key_value.py b/pyhexwatershed/configuration/change_json_key_value.py new file mode 100644 index 0000000..8757876 --- /dev/null +++ b/pyhexwatershed/configuration/change_json_key_value.py @@ -0,0 +1,39 @@ +import json +from pathlib import Path +import shutil +import tempfile + +def change_json_key_value(sFilename_json_in, sKey, new_value, iFlag_basin_in=None): + """ + Change the value associated with the specified key in a JSON file safely. + + Args: + sFilename_json_in (str or Path): Path to the JSON file. + sKey (str): Key whose value needs to be changed. + new_value: New value to set for the specified key. + iFlag_basin_in (bool, optional): Flag indicating if the key is for a basin configuration. Defaults to None. + """ + # Ensure input filename is a string + sFilename_json_in = str(sFilename_json_in) + + # Convert new_value to string if it's a Path object + if isinstance(new_value, Path): + new_value = str(new_value) + + # Read the original JSON data + with open(sFilename_json_in, 'r') as file: + data = json.load(file) + + # Update the value associated with the specified key + if iFlag_basin_in is None: + data[sKey] = new_value + else: + data[0][sKey] = new_value + + # Write the updated data to a temporary file + with tempfile.NamedTemporaryFile(delete=False, mode='w', suffix='.json') as temp_file: + json.dump(data, temp_file, indent=4) + temp_path = temp_file.name + + # Replace the original file with the updated temporary file + shutil.move(temp_path, sFilename_json_in) diff --git a/pyhexwatershed/pyhexwatershed_create_template_configuration_file.py b/pyhexwatershed/configuration/create_template_configuration_file.py similarity index 100% rename from pyhexwatershed/pyhexwatershed_create_template_configuration_file.py rename to pyhexwatershed/configuration/create_template_configuration_file.py diff --git a/pyhexwatershed/configuration/path_manager.py b/pyhexwatershed/configuration/path_manager.py new file mode 100644 index 0000000..65fe180 --- /dev/null +++ b/pyhexwatershed/configuration/path_manager.py @@ -0,0 +1,57 @@ +from pathlib import Path +from pkg_resources import resource_filename +""" +path_manager +All paths are created with Path and call resolve() +""" + +def join_project_path(*args: str) -> Path: + """ + Join components of a path relative to the project root. + + Args: + *args: Variable number of string arguments representing path components. + + Returns: + Path: Absolute path joined with the project root. + """ + project_root = pyhexwatershed_project_root() + return project_root.joinpath(*args) + +def pyhexwatershed_project_root() -> Path: + """ + Attempt to find the root path of the project, first by looking for a 'setup.py' file, and then using pkg_resources to find the installation path. + """ + try: + return root_path_from_setup_file() + except FileNotFoundError: + return root_path_from_pyhexwatershed_package_root() + except Exception as e: + raise RuntimeError(f"An unexpected error occurred while locating the project root: {str(e)}") + +def pyhexwatershed_package_root(package_name='pyhexwatershed') -> Path: + """Get the installation root directory of the package.""" + return Path(resource_filename(package_name, '')).resolve() + +def root_path_from_setup_file() -> Path: + """Return the top-level (root) path of the pyhexwatershed project. + This function navigates upwards from this file's path until it finds a directory + with a specific marker (e.g., a setup.py file) indicating the root of the project. + """ + this_path = Path(__file__).resolve() + for parent in this_path.parents: + if (parent / 'setup.py').exists(): + return parent + raise FileNotFoundError("setup.py not found. Is this the right project structure?") + +def root_path_from_pyhexwatershed_package_root() -> Path: + """Get the installation root directory of the package.""" + return pyhexwatershed_package_root().parent + +# Example additional function for getting root from this module's location, adjusted for best practice +def root_path_from_path_manager_location() -> Path: + """Return the top-level (root) path of the pyhexwatershed project + This function assumes the module is located at a fixed level: + root_path/pyhexwatershed/configuration/path_manager + """ + return Path(__file__).resolve().parents[2] diff --git a/pyhexwatershed/pyhexwatershed_read_model_configuration_file.py b/pyhexwatershed/configuration/read_configuration_file.py similarity index 94% rename from pyhexwatershed/pyhexwatershed_read_model_configuration_file.py rename to pyhexwatershed/configuration/read_configuration_file.py index b6631ef..9652cda 100644 --- a/pyhexwatershed/pyhexwatershed_read_model_configuration_file.py +++ b/pyhexwatershed/configuration/read_configuration_file.py @@ -9,7 +9,7 @@ pDate = datetime.datetime.today() sDate_default = "{:04d}".format(pDate.year) + "{:02d}".format(pDate.month) + "{:02d}".format(pDate.day) -def pyhexwatershed_read_model_configuration_file(sFilename_configuration_in, +def pyhexwatershed_read_configuration_file(sFilename_configuration_in, iCase_index_in=None, iFlag_stream_burning_topology_in = None, iFlag_elevation_profile_in = None, @@ -41,25 +41,25 @@ def pyhexwatershed_read_model_configuration_file(sFilename_configuration_in, if sDggrid_type_in is not None: sDggrid_type = sDggrid_type_in else: - if "sDggrid_type" in aConfig: + if "sDggrid_type" in aConfig: sDggrid_type = aConfig["sDggrid_type"] else: sDggrid_type = 'ISEA3H' - + if iCase_index_in is not None: iCase_index = iCase_index_in else: iCase_index = int( aConfig['iCase_index']) pass - if iResolution_index_in is not None: + if iResolution_index_in is not None: iResolution_index = iResolution_index_in else: - if "iResolution_index" in aConfig: + if "iResolution_index" in aConfig: iResolution_index = int( aConfig['iResolution_index']) else: iResolution_index = 10 - + pass if iFlag_stream_burning_topology_in is not None: @@ -118,13 +118,13 @@ def pyhexwatershed_read_model_configuration_file(sFilename_configuration_in, oPyflowline = flowlinecase(aConfig , iFlag_standalone_in = 0, sModel_in = 'pyflowline', sWorkspace_output_in = oPyhexwatershed.sWorkspace_output_pyflowline) - + oPyhexwatershed.dResolution_meter = oPyflowline.dResolution_meter oPyhexwatershed.pPyFlowline = oPyflowline oPyhexwatershed.aBasin.clear() #set up the basin object - #check flowline flag + #check flowline flag if oPyhexwatershed.iFlag_flowline == 1 and os.path.exists(oPyhexwatershed.sFilename_basins): with open(oPyhexwatershed.sFilename_basins) as json_file: dummy_data = json.load(json_file) From 2fdab85c927e3ba3b70248e0e7fd3da1dd6f3a88 Mon Sep 17 00:00:00 2001 From: changliao1025 Date: Thu, 9 May 2024 15:32:37 -0700 Subject: [PATCH 2/3] update for workshop --- pyhexwatershed/classes/pycase.py | 454 +++++++++++++++---------------- 1 file changed, 227 insertions(+), 227 deletions(-) diff --git a/pyhexwatershed/classes/pycase.py b/pyhexwatershed/classes/pycase.py index 0231988..86115eb 100644 --- a/pyhexwatershed/classes/pycase.py +++ b/pyhexwatershed/classes/pycase.py @@ -38,15 +38,15 @@ def default(self, obj): if isinstance(obj, np.ndarray): return obj.tolist() if isinstance(obj, list): - pass + pass if isinstance(obj, flowlinecase): - return obj.sWorkspace_output + return obj.sWorkspace_output return JSONEncoder.default(self, obj) class hexwatershedcase(object): - - iFlag_profile = 0 - iFlag_resample_method=2 + + iFlag_profile = 0 + iFlag_resample_method=2 iFlag_flowline=1 iFlag_global = 0 iFlag_antarctic=0 @@ -54,16 +54,16 @@ class hexwatershedcase(object): iFlag_elevation_profile = 0 iFlag_stream_burning_topology=1 iFlag_create_mesh= 1 - iFlag_mesh_boundary =0 + iFlag_mesh_boundary =0 iFlag_simplification= 0 iFlag_intersect= 0 iFlag_merge_reach=1 - iMesh_type = 4 - iFlag_save_mesh = 0 + iMesh_type = 4 + iFlag_save_mesh = 0 iFlag_use_mesh_dem=0 iFlag_user_provided_binary= 0 iFlag_slurm = 0 - nOutlet=1 + nOutlet=1 dResolution_degree=0.0 dResolution_meter=0.0 dThreshold_small_river=0.0 @@ -71,18 +71,18 @@ class hexwatershedcase(object): dLongitude_right = 180 dLatitude_bot = -90 dLatitude_top = 90 - sFilename_dem='' + sFilename_dem='' sFilename_model_configuration='' sFilename_mesh='' sFilename_mesh_info='' sFilename_mesh_boundary='' sFilename_flowline_info='' - sFilename_basins='' + sFilename_basins='' sFilename_hexwatershed_bin='' sFilename_elevation='' sFilename_slope='' sFilename_drainage_area='' - sFilename_flow_direction='' + sFilename_flow_direction='' sFilename_distance_to_outlet= '' sFilename_variable_polyline='' sFilename_variable_polygon='' @@ -92,26 +92,26 @@ class hexwatershedcase(object): sModel='' iMesh_type ='mpas' sCase='' - sDate='' + sDate='' sFilename_spatial_reference='' - + sFilename_hexwatershed_json='' pPyFlowline = None #the pyflowline object - + sWorkspace_input='' sWorkspace_output_pyflowline='' sWorkspace_output_hexwatershed='' sWorkspace_model_region='' #is this variable used? - aBasin = list() - + aBasin = list() + iFlag_visual = importlib.util.find_spec("cartopy") if iFlag_visual is not None: from ._visual import plot - from ._visual import _animate - + from ._visual import _animate + else: pass - + #hpc feature from ._hpc import _pyhexwatershed_create_hpc_job from ._hpc import _pyhexwatershed_submit_hpc_job @@ -122,10 +122,10 @@ def __init__(self, aConfig_in): if 'sWorkspace_data' in aConfig_in: self.sWorkspace_data = aConfig_in[ 'sWorkspace_data'] - + if 'sWorkspace_input' in aConfig_in: self.sWorkspace_input = aConfig_in[ 'sWorkspace_input'] - + if 'sWorkspace_output' in aConfig_in: self.sWorkspace_output = aConfig_in[ 'sWorkspace_output'] @@ -137,49 +137,49 @@ def __init__(self, aConfig_in): if 'sModel' in aConfig_in: self.sModel = aConfig_in[ 'sModel'] - + if 'iFlag_resample_method' in aConfig_in: self.iFlag_resample_method = int(aConfig_in[ 'iFlag_resample_method']) if 'iFlag_flowline' in aConfig_in: self.iFlag_flowline = int(aConfig_in[ 'iFlag_flowline']) - else: - self.iFlag_flowline =1 + else: + self.iFlag_flowline =1 if 'iFlag_create_mesh' in aConfig_in: self.iFlag_create_mesh = int(aConfig_in[ 'iFlag_create_mesh']) - else: - self.iFlag_create_mesh =1 + else: + self.iFlag_create_mesh =1 if 'iFlag_simplification' in aConfig_in: self.iFlag_simplification = int(aConfig_in[ 'iFlag_simplification']) - else: - self.iFlag_simplification = 1 + else: + self.iFlag_simplification = 1 if self.iFlag_simplification ==1: self.iFlag_flowline = 1 if 'iFlag_intersect' in aConfig_in: self.iFlag_intersect = int(aConfig_in[ 'iFlag_intersect']) - else: - self.iFlag_intersect = 1 + else: + self.iFlag_intersect = 1 if 'iFlag_global' in aConfig_in: self.iFlag_global = int(aConfig_in[ 'iFlag_global']) - + if 'iFlag_antarctic' in aConfig_in: - self.iFlag_antarctic = int(aConfig_in[ 'iFlag_antarctic']) + self.iFlag_antarctic = int(aConfig_in[ 'iFlag_antarctic']) if 'iFlag_multiple_outlet' in aConfig_in: - self.iFlag_multiple_outlet = int(aConfig_in[ 'iFlag_multiple_outlet']) + self.iFlag_multiple_outlet = int(aConfig_in[ 'iFlag_multiple_outlet']) if 'iFlag_use_mesh_dem' in aConfig_in: self.iFlag_use_mesh_dem = int(aConfig_in[ 'iFlag_use_mesh_dem']) - + if 'iFlag_mesh_boundary' in aConfig_in: self.iFlag_mesh_boundary = int(aConfig_in[ 'iFlag_mesh_boundary']) else: - self.iFlag_mesh_boundary=0 + self.iFlag_mesh_boundary=0 if 'iFlag_stream_burning_topology' in aConfig_in: self.iFlag_stream_burning_topology = int(aConfig_in[ 'iFlag_stream_burning_topology']) @@ -189,13 +189,13 @@ def __init__(self, aConfig_in): if 'iFlag_save_mesh' in aConfig_in: self.iFlag_save_mesh = int(aConfig_in[ 'iFlag_save_mesh']) - + if 'iFlag_save_elevation' in aConfig_in: self.iFlag_save_elevation = int(aConfig_in[ 'iFlag_save_elevation']) if 'iFlag_elevation_profile' in aConfig_in: self.iFlag_elevation_profile = int(aConfig_in[ 'iFlag_elevation_profile']) - + if 'iFlag_user_provided_binary' in aConfig_in: self.iFlag_user_provided_binary = int(aConfig_in[ 'iFlag_user_provided_binary']) else: @@ -214,7 +214,7 @@ def __init__(self, aConfig_in): if 'dAccumulation_threshold' in aConfig_in: self.dAccumulation_threshold = float(aConfig_in[ 'dAccumulation_threshold']) - + if 'sFilename_spatial_reference' in aConfig_in: self.sFilename_spatial_reference = aConfig_in['sFilename_spatial_reference'] @@ -252,7 +252,7 @@ def __init__(self, aConfig_in): else: print('The model will use the default binary file') pass - + if 'iCase_index' in aConfig_in: iCase_index = int(aConfig_in['iCase_index']) @@ -263,12 +263,12 @@ def __init__(self, aConfig_in): self.iResolution_index = int(aConfig_in['iResolution_index']) else: self.iResolution_index=10 - + if 'sDggrid_type' in aConfig_in: self.sDggrid_type = aConfig_in['sDggrid_type'] else: self.sDggrid_type='ISEA3H' - + sDate = aConfig_in[ 'sDate'] if sDate is not None: self.sDate= sDate @@ -281,7 +281,7 @@ def __init__(self, aConfig_in): self.sCase = sCase sPath = str(Path(self.sWorkspace_output) / sCase) - self.sWorkspace_output = sPath + self.sWorkspace_output = sPath try: Path(sPath).mkdir(parents=True, exist_ok=True) except Exception as e: @@ -290,12 +290,12 @@ def __init__(self, aConfig_in): return else: print(f"Directory {sPath} created successfully") - + if 'sMesh_type' in aConfig_in: self.sMesh_type = aConfig_in['sMesh_type'] else: self.sMesh_type = 'hexagon' - + sMesh_type = self.sMesh_type if sMesh_type =='hexagon': #hexagon self.iMesh_type = 1 @@ -316,34 +316,34 @@ def __init__(self, aConfig_in): self.iMesh_type = 6 pass print('Unsupported mesh type?') - + if 'dResolution_degree' in aConfig_in: - self.dResolution_degree = float(aConfig_in['dResolution_degree']) + self.dResolution_degree = float(aConfig_in['dResolution_degree']) if 'dResolution_meter' in aConfig_in: - self.dResolution_meter = float(aConfig_in['dResolution_meter']) + self.dResolution_meter = float(aConfig_in['dResolution_meter']) else: print('Please specify resolution.') if 'dLongitude_left' in aConfig_in: - self.dLongitude_left = float(aConfig_in['dLongitude_left']) + self.dLongitude_left = float(aConfig_in['dLongitude_left']) if 'dLongitude_right' in aConfig_in: - self.dLongitude_right = float(aConfig_in['dLongitude_right']) + self.dLongitude_right = float(aConfig_in['dLongitude_right']) if 'dLatitude_bot' in aConfig_in: - self.dLatitude_bot = float(aConfig_in['dLatitude_bot']) + self.dLatitude_bot = float(aConfig_in['dLatitude_bot']) if 'dLatitude_top' in aConfig_in: - self.dLatitude_top = float(aConfig_in['dLatitude_top']) + self.dLatitude_top = float(aConfig_in['dLatitude_top']) if 'sJob' in aConfig_in: - self.sJob = aConfig_in['sJob'] - + self.sJob = aConfig_in['sJob'] + if 'sFilename_basins' in aConfig_in: self.sFilename_basins = aConfig_in['sFilename_basins'] else: - self.sFilename_basins = '' + self.sFilename_basins = '' sPath = str(Path(self.sWorkspace_output) / 'hexwatershed') self.sWorkspace_output_hexwatershed = sPath @@ -352,9 +352,9 @@ def __init__(self, aConfig_in): sPath = str(Path(self.sWorkspace_output) / 'pyflowline') self.sWorkspace_output_pyflowline = sPath Path(sPath).mkdir(parents=True, exist_ok=True) - + self.sFilename_mesh = os.path.join(str(Path(self.sWorkspace_output_pyflowline) ) , sMesh_type + ".geojson" ) - self.sFilename_mesh_info = os.path.join(str(Path(self.sWorkspace_output_pyflowline) ) , sMesh_type + "_mesh_info.json" ) + self.sFilename_mesh_info = os.path.join(str(Path(self.sWorkspace_output_pyflowline) ) , sMesh_type + "_mesh_info.json" ) self.sFilename_hexwatershed_json = os.path.join(str(Path(self.sWorkspace_output_hexwatershed) ) , "hexwatershed.json" ) #individual output variables @@ -363,15 +363,15 @@ def __init__(self, aConfig_in): self.sFilename_drainage_area = os.path.join(str(Path(self.sWorkspace_output_hexwatershed) ) , sMesh_type + "_drainage_area.geojson" ) self.sFilename_flow_direction = os.path.join(str(Path(self.sWorkspace_output_hexwatershed) ) , sMesh_type + "_flow_direction.geojson" ) self.sFilename_distance_to_outlet = os.path.join(str(Path(self.sWorkspace_output_hexwatershed) ) , sMesh_type + "_distance_to_outlet.geojson" ) - + self.sFilename_variable_polyline = os.path.join(str(Path(self.sWorkspace_output_hexwatershed) ) , sMesh_type + "_variable_polyline.geojson" ) self.sFilename_variable_polygon = os.path.join(str(Path(self.sWorkspace_output_hexwatershed) ) , sMesh_type + "_variable_polygon.geojson" ) self.sFilename_animation_json = os.path.join(str(Path(self.sWorkspace_output_hexwatershed) ) , "animation.json" ) - return + return def tojson(self): - aSkip = ['aBasin'] + aSkip = ['aBasin'] obj = self.__dict__.copy() for sKey in aSkip: obj.pop(sKey, None) @@ -385,11 +385,11 @@ def tojson(self): return sJson - def pyhexwatershed_export_config_to_json(self, sFilename_out=None): + def pyhexwatershed_export_config_to_json(self, sFilename_out=None): self.pPyFlowline.pyflowline_export_basin_config_to_json() self.sFilename_model_configuration = os.path.join(self.sWorkspace_output, 'configuration.json') self.sFilename_basins = self.pPyFlowline.sFilename_basins - #save the configuration to a new file, which has the full path + #save the configuration to a new file, which has the full path if sFilename_out is not None: sFilename_configuration = sFilename_out else: @@ -403,42 +403,42 @@ def pyhexwatershed_export_config_to_json(self, sFilename_out=None): for sKey in aSkip: obj.pop(sKey, None) pass - + with open(sFilename_configuration, 'w', encoding='utf-8') as f: json.dump(obj, f,sort_keys=True, \ ensure_ascii=False, \ - indent=4, cls=CaseClassEncoder) + indent=4, cls=CaseClassEncoder) - #make a copy + #make a copy if sFilename_out is not None: - copy2(sFilename_configuration, self.sFilename_model_configuration) - + copy2(sFilename_configuration, self.sFilename_model_configuration) + return - + def pyhexwatershed_setup(self): print('Started setting up model') ptimer = pytimer() - ptimer.start() + ptimer.start() #setup the pyflowline self.pPyFlowline.pyflowline_setup() #setup the hexwatershed system = platform.system() - iFlag_found_binary = 0 + iFlag_found_binary = 0 #if user provided the binary, then use the user provided binary if self.iFlag_user_provided_binary == 1: - sFilename_executable = 'hexwatershed' - #copy the binary + sFilename_executable = 'hexwatershed' + #copy the binary iFlag_found_binary = 1 sFilename_new = os.path.join(str(Path(self.sWorkspace_output_hexwatershed) ) , sFilename_executable ) copy2(self.sFilename_hexwatershed_bin, sFilename_new) os.chmod(sFilename_new, stat.S_IRWXU ) pass - else: + else: if system == 'Windows': sFilename_executable = 'hexwatershed.exe' else: - sFilename_executable = 'hexwatershed' - + sFilename_executable = 'hexwatershed' + #search for system wide binary in the system path for folder in os.environ['PATH'].split(os.pathsep): sFilename_hexwatershed_bin = os.path.join(folder, sFilename_executable) @@ -453,7 +453,7 @@ def pyhexwatershed_setup(self): copy2(sFilename_hexwatershed_bin, sFilename_new) os.chmod(sFilename_new, stat.S_IRWXU ) pass - else: + else: # Get the distribution object for the package distribution = pkg_resources.get_distribution('hexwatershed') # Get the installation path for the package @@ -466,12 +466,12 @@ def pyhexwatershed_setup(self): copy2(sFilename_hexwatershed_bin, sFilename_new) os.chmod(sFilename_new, stat.S_IRWXU ) else: - iFlag_found_binary = 0 - + iFlag_found_binary = 0 + ptimer.stop() sys.stdout.flush() return - + def pyhexwatershed_run_pyflowline(self): """ Run the pyflowline submodel @@ -486,7 +486,7 @@ def pyhexwatershed_run_pyflowline(self): ptimer.stop() sys.stdout.flush() return aCell_out - + def pyhexwatershed_run_hexwatershed(self): """ Run the hexwatershed model @@ -499,45 +499,45 @@ def pyhexwatershed_run_hexwatershed(self): print('Running on a Windows system') #run the model using bash self.generate_bash_script() - os.chdir(self.sWorkspace_output_hexwatershed) + os.chdir(self.sWorkspace_output_hexwatershed) sCommand = "./run_hexwatershed.bat" #print(sCommand) sys.stdout.flush() p = subprocess.Popen(sCommand, shell= True) - p.wait() + p.wait() elif system == 'Linux': print('Running on a Unix-based system') #run the model using bash self.pyhexwatershed_generate_bash_script() - os.chdir(self.sWorkspace_output_hexwatershed) + os.chdir(self.sWorkspace_output_hexwatershed) sCommand = "./run_hexwatershed.sh" #print(sCommand) sys.stdout.flush() p = subprocess.Popen(sCommand, shell= True) p.wait() - elif system == 'Darwin': + elif system == 'Darwin': print('Running on a Unix-based system') #run the model using bash self.pyhexwatershed_generate_bash_script() - os.chdir(self.sWorkspace_output_hexwatershed) + os.chdir(self.sWorkspace_output_hexwatershed) sCommand = "./run_hexwatershed.sh" #print(sCommand) sys.stdout.flush() p = subprocess.Popen(sCommand, shell= True) - p.wait() + p.wait() else: print('Unknown operating system') pass - sys.stdout.flush() + sys.stdout.flush() ptimer.stop() return - - def pyhexwatershed_assign_elevation_to_cells(self): + + def pyhexwatershed_assign_elevation_to_cells(self, dMissing_value_in =None): """When there is no elevation in the meshm, a DEM file is used to assign elevation for each mesh cell. However, it is posselbe that a cell has no elevation because it is - not cover by the DEM raster file. + not cover by the DEM raster file. In this case, the cell is removed from the mesh. P.S. because of the removal, the outlet ID may be changed. @@ -546,34 +546,37 @@ def pyhexwatershed_assign_elevation_to_cells(self): """ print('Started assigning elevation') ptimer = pytimer() - ptimer.start() + ptimer.start() iFlag_resample_method= self.iFlag_resample_method sFilename_dem_in = self.sFilename_dem aCell_in=self.pPyFlowline.aCell - - ncell = len(aCell_in) + + ncell = len(aCell_in) pDriver_shapefile = ogr.GetDriverByName('ESRI Shapefile') pDriver_json = ogr.GetDriverByName('GeoJSON') pDriver_memory = gdal.GetDriverByName('MEM') sFilename_shapefile_cut = "/vsimem/tmp_polygon.shp" - pSrs = osr.SpatialReference() + pSrs = osr.SpatialReference() pSrs.ImportFromEPSG(4326) # WGS84 lat/lon pDataset_elevation = gdal.Open(sFilename_dem_in, gdal.GA_ReadOnly) dummy = gdal_read_geotiff_file(sFilename_dem_in) aDem_in= dummy['dataOut'] - dPixelWidth = dummy['pixelWidth'] + dPixelWidth = dummy['pixelWidth'] pPixelHeight = dummy['pixelHeight'] dOriginX = dummy['originX'] dOriginY = dummy['originY'] nrow = dummy['nrow'] ncolumn = dummy['ncolumn'] - dMissing_value= dummy['missingValue'] + if dMissing_value_in is not None: + dMissing_value = dMissing_value_in + else: + dMissing_value= dummy['missingValue'] #pGeotransform = dummy['geotransform'] pProjection = dummy['projection'] pSpatialRef_target = dummy['spatialReference'] - #transform = osr.CoordinateTransformation(pSrs, pSpatialRef_target) - #get raster extent + #transform = osr.CoordinateTransformation(pSrs, pSpatialRef_target) + #get raster extent dX_left=dOriginX dX_right = dOriginX + ncolumn * dPixelWidth dY_top = dOriginY @@ -582,106 +585,103 @@ def pyhexwatershed_assign_elevation_to_cells(self): lCellIndex = 0 if iFlag_resample_method == 1: #the nearest resample method - - for pCell in aCell_in: - dX_out,dY_out = reproject_coordinates(pCell.dLongitude_center_degree,pCell.dLatitude_center_degree,pSrs,pSpatialRef_target) - lColumn_index = int((dX_out - dX_left) / dPixelWidth) - lRow_index = int((dY_top - dY_out) / dPixelWidth) + + for pCell in aCell_in: + dX_out,dY_out = reproject_coordinates(pCell.dLongitude_center_degree,pCell.dLatitude_center_degree,pSrs,pSpatialRef_target) + lColumn_index = int((dX_out - dX_left) / dPixelWidth) + lRow_index = int((dY_top - dY_out) / dPixelWidth) if lColumn_index >= ncolumn or lColumn_index < 0 \ - or lRow_index >= nrow or lRow_index < 0: - #this pixel is out of bound + or lRow_index >= nrow or lRow_index < 0: + #this pixel is out of bound continue - else: - dElevation = aDem_in[lRow_index, lColumn_index] - if( dElevation!=dMissing_value): - pCell.dElevation_mean = dElevation - pCell.dz = dElevation + else: + dElevation = aDem_in[lRow_index, lColumn_index] + if( dElevation!=dMissing_value): + pCell.dElevation_mean = dElevation + pCell.dz = dElevation aCell_dict[pCell.lCellID] = lCellIndex lCellIndex = lCellIndex + 1 - - else: + + else: pCell.dElevation_mean=-9999.0 pass pass else: if iFlag_resample_method == 2: - for pCell in aCell_in: + for pCell in aCell_in: nVertex = pCell.nVertex ring = ogr.Geometry(ogr.wkbLinearRing) aX= list() aY=list() for j in range(nVertex): aX.append( pCell.aVertex[j].dLongitude_degree ) - aY.append( pCell.aVertex[j].dLatitude_degree ) + aY.append( pCell.aVertex[j].dLatitude_degree ) pass aX.append( pCell.aVertex[0].dLongitude_degree ) aY.append( pCell.aVertex[0].dLatitude_degree ) - aX_out,aY_out = reproject_coordinates_batch(aX,aY,pSrs,pSpatialRef_target) + aX_out,aY_out = reproject_coordinates_batch(aX,aY,pSrs,pSpatialRef_target) for j in range(nVertex + 1): x1 = aX_out[j] - y1 = aY_out[j] - ring.AddPoint(x1, y1) - pass - + y1 = aY_out[j] + ring.AddPoint(x1, y1) + pass + pPolygon = ogr.Geometry(ogr.wkbPolygon) pPolygon.AddGeometry(ring) #pPolygon.AssignSpatialReference(pSpatialRef_target) - if os.path.exists(sFilename_shapefile_cut): + if os.path.exists(sFilename_shapefile_cut): os.remove(sFilename_shapefile_cut) pDataset3 = pDriver_shapefile.CreateDataSource(sFilename_shapefile_cut) - pLayerOut3 = pDataset3.CreateLayer('cell', pSpatialRef_target, ogr.wkbPolygon) + pLayerOut3 = pDataset3.CreateLayer('cell', pSpatialRef_target, ogr.wkbPolygon) pLayerDefn3 = pLayerOut3.GetLayerDefn() pFeatureOut3 = ogr.Feature(pLayerDefn3) - pFeatureOut3.SetGeometry(pPolygon) - pLayerOut3.CreateFeature(pFeatureOut3) + pFeatureOut3.SetGeometry(pPolygon) + pLayerOut3.CreateFeature(pFeatureOut3) pDataset3.FlushCache() minX, maxX, minY, maxY = pPolygon.GetEnvelope() iNewWidth = int( (maxX - minX) / abs(dPixelWidth) ) iNewHeigh = int( (maxY - minY) / abs(dPixelWidth) ) - newGeoTransform = (minX, dPixelWidth, 0, maxY, 0, -dPixelWidth) + newGeoTransform = (minX, dPixelWidth, 0, maxY, 0, -dPixelWidth) if minX > dX_right or maxX < dX_left \ - or minY > dY_top or maxY < dY_bot: - #this polygon is out of bound + or minY > dY_top or maxY < dY_bot: + #this polygon is out of bound continue - else: + else: pDataset_clip = pDriver_memory.Create('', iNewWidth, iNewHeigh, 1, gdalconst.GDT_Float32) pDataset_clip.SetGeoTransform( newGeoTransform ) - pDataset_clip.SetProjection( pProjection) + pDataset_clip.SetProjection( pProjection) pWrapOption = gdal.WarpOptions( cropToCutline=True,cutlineDSName = sFilename_shapefile_cut , \ width=iNewWidth, \ height=iNewHeigh, \ dstSRS=pProjection , format = 'MEM' ) pDataset_clip = gdal.Warp('',pDataset_elevation, options=pWrapOption) pBand = pDataset_clip.GetRasterBand( 1 ) - dMissing_value = pBand.GetNoDataValue() aData_out = pBand.ReadAsArray(0,0,iNewWidth, iNewHeigh) - - aElevation = aData_out[np.where(aData_out !=dMissing_value)] - - if(len(aElevation) >0 and np.mean(aElevation)!=-9999): - dElevation = float(np.mean(aElevation) ) - pCell.dElevation_mean = dElevation - pCell.dz = dElevation + aElevation = aData_out[np.where(aData_out !=dMissing_value)] + if(len(aElevation) >0 and np.mean(aElevation)!=-9999): + dElevation = float(np.mean(aElevation) ) + pCell.dElevation_mean = dElevation + pCell.dz = dElevation aCell_dict[pCell.lCellID] = lCellIndex lCellIndex = lCellIndex + 1 - else: + else: pCell.dElevation_mean=-9999.0 pass else: #other resampling method - pass + pass #update neighbor because not all cells have elevation now - + aCell_out=list() - #if a cell has no elevation and was removed earlier, + #if a cell has no elevation and was removed earlier, #it should be removed from the land neighbor list #but the distance list remained the same. for pCell in aCell_in: @@ -689,7 +689,7 @@ def pyhexwatershed_assign_elevation_to_cells(self): aNeighbor_land = pCell.aNeighbor_land aNeighbor_new = [neighbor for neighbor in aNeighbor_land if neighbor in aCell_dict] pCell.aNeighbor_land = aNeighbor_new - pCell.nNeighbor_land = len(aNeighbor_new) + pCell.nNeighbor_land = len(aNeighbor_new) aCell_out.append(pCell) #update the cell information @@ -697,13 +697,13 @@ def pyhexwatershed_assign_elevation_to_cells(self): ptimer.stop() sys.stdout.flush() return aCell_out - + def pyhexwatershed_update_outlet(self, aCell_origin): """update the outlet location if the outlet is not in the mesh anymore Because the orginal mesh does not have elevation information, it must remain unchanged. - Args: + Args: aCell_origin (_type_): The original mesh cells produced by pyflowline Returns: @@ -720,7 +720,7 @@ def search_upstream(lCellID_in): if pCell_temp.lCellID_downstream_burned == lCellID_in: aCell_remove.append(lCellID_in) return pCell_temp.lCellID - + for iBasin in range(len(self.pPyFlowline.aBasin)): pBasin = self.pPyFlowline.aBasin[iBasin] lCellID_outlet = pBasin.lCellID_outlet @@ -730,8 +730,8 @@ def search_upstream(lCellID_in): if pCell_temp.dElevation_mean == -9999: iFlag_error = 1 break - - if iFlag_error ==1: + + if iFlag_error ==1: iFlag_found = 0 lCellID_current = lCellID_outlet while(iFlag_found ==0 ): @@ -742,7 +742,7 @@ def search_upstream(lCellID_in): iFlag_found = 1 pCell_temp.lCellID_downstream_burned = -1 break - + lCellID_current = lOutletID_next self.pPyFlowline.aBasin[iBasin].lCellID_outlet = lOutletID_next @@ -768,10 +768,10 @@ def search_upstream(lCellID_in): sys.stdout.flush() return - def pyhexwatershed_generate_bash_script(self): + def pyhexwatershed_generate_bash_script(self): sName = 'configuration.json' sFilename_configuration = os.path.join( self.sWorkspace_output, sName ) - os.chdir(self.sWorkspace_output_hexwatershed) + os.chdir(self.sWorkspace_output_hexwatershed) #detemine the system platform # Determine the appropriate executable name for the platform system = platform.system() @@ -780,10 +780,10 @@ def pyhexwatershed_generate_bash_script(self): iFlag_unix = 0 else: sFilename_executable = './hexwatershed' - + if system == 'Windows': - # execute binary on Windows + # execute binary on Windows iFlag_unix = 0 elif system == 'Linux': # execute binary on Linux @@ -796,38 +796,38 @@ def pyhexwatershed_generate_bash_script(self): print('Unsupported operating system: ' + system) print('Please reach out to the developers for assistance.') - #generate the bash/batch script + #generate the bash/batch script if iFlag_unix == 1 : sFilename_bash = os.path.join(str(Path(self.sWorkspace_output_hexwatershed) ) , "run_hexwatershed.sh" ) ofs = open(sFilename_bash, 'w') sLine = '#!/bin/bash\n' - ofs.write(sLine) + ofs.write(sLine) sLine = 'cd ' + self.sWorkspace_output_hexwatershed+ '\n' ofs.write(sLine) sLine = sFilename_executable + ' ' + sFilename_configuration + '\n' ofs.write(sLine) ofs.close() - os.chmod(sFilename_bash, stat.S_IRWXU ) + os.chmod(sFilename_bash, stat.S_IRWXU ) else: sFilename_bash = os.path.join(str(Path(self.sWorkspace_output_hexwatershed) ) , "run_hexwatershed.bat" ) - ofs = open(sFilename_bash, 'w') + ofs = open(sFilename_bash, 'w') sLine = 'cd ' + self.sWorkspace_output_hexwatershed+ '\n' ofs.write(sLine) sLine = sFilename_executable + ' ' + sFilename_configuration + '\n' ofs.write(sLine) ofs.close() - os.chmod(sFilename_bash, stat.S_IRWXU ) + os.chmod(sFilename_bash, stat.S_IRWXU ) return - + def pyhexwatershed_analyze(self): #a list of analysis was done within the C++ backend #additional analysis can be implemented here return - def pyhexwatershed_export(self): + def pyhexwatershed_export(self): """ #https://hexwatershed.readthedocs.io/en/latest/application/application.html#simulation-results - """ + """ print('Started export results') ptimer = pytimer() ptimer.start() @@ -841,20 +841,20 @@ def pyhexwatershed_export(self): else: #if the pyflowline does not turn on flowline, #then we need to include at least one watershed - self.pyhexwatershed_export_flow_direction() + self.pyhexwatershed_export_flow_direction() self.pyhexwatershed_export_stream_segment() #polygon #self.pyhexwatershed_export_elevation() #self.pyhexwatershed_export_slope() - #self.pyhexwatershed_export_drainage_area() + #self.pyhexwatershed_export_drainage_area() #self.pyhexwatershed_export_travel_distance() #we can also save a geojson that has all the information self.pyhexwatershed_export_all_polygon_variables() #self.pyhexwatershed_export_all_polyline_variables() pass - + ptimer.stop() sys.stdout.flush() return @@ -862,21 +862,21 @@ def pyhexwatershed_export(self): def pyhexwatershed_change_model_parameter(self, sVariable_in, dValue, iFlag_basin_in = None): if iFlag_basin_in is None: if hasattr(self, sVariable_in): - #get default data type + #get default data type sType_default = type(getattr(self, sVariable_in)) #get the data type of the input value sType_input = type(dValue) - if sType_default == sType_input: - setattr(self, sVariable_in, dValue) - pass + if sType_default == sType_input: + setattr(self, sVariable_in, dValue) + pass else: print('Incorrect data type for the input value: ' + sVariable_in) return True else: print("This model parameter is unknown, please check the full parameter list in the documentation: " + sVariable_in) return False - - + + else: #this mean the variable is in the basin object @@ -885,10 +885,10 @@ def pyhexwatershed_change_model_parameter(self, sVariable_in, dValue, iFlag_basi #get default data type sType_default = type(getattr(pBasin, sVariable_in)) sType_input = type(dValue) - if sType_default == sType_input: + if sType_default == sType_input: setattr(pBasin, sVariable_in, dValue) else: - print('Incorrect data type for the input value: ' + sVariable_in) + print('Incorrect data type for the input value: ' + sVariable_in) return False else: print("This model parameter is unknown, please check the full parameter list in the documentation: " + sVariable_in) @@ -897,8 +897,8 @@ def pyhexwatershed_change_model_parameter(self, sVariable_in, dValue, iFlag_basi def pyhexwatershed_export_stream_segment(self): """ https://hexwatershed.readthedocs.io/en/latest/application/application.html#simulation-results - """ - + """ + iFlag_flowline = self.iFlag_flowline nWatershed = self.nOutlet @@ -907,9 +907,9 @@ def pyhexwatershed_export_stream_segment(self): return else: - if self.iFlag_multiple_outlet==1: - for iWatershed in range(1, nWatershed+1): - pBasin = self.aBasin[iWatershed-1] + if self.iFlag_multiple_outlet==1: + for iWatershed in range(1, nWatershed+1): + pBasin = self.aBasin[iWatershed-1] sFilename_stream_edge = pBasin.sFilename_stream_edge_json sFilename_stream_edge_geojson = pBasin.sFilename_stream_edge sFilename_stream_segment_geojson = pBasin.sFilename_stream_segment @@ -918,17 +918,17 @@ def pyhexwatershed_export_stream_segment(self): point['dLatitude_degree'] = pBasin.dLatitude_outlet_degree pVertex_outlet=pyvertex(point) export_json_to_geojson_polyline(sFilename_stream_edge, sFilename_stream_edge_geojson) - merge_stream_edge_to_stream_segment(sFilename_stream_edge_geojson, - sFilename_stream_segment_geojson, + merge_stream_edge_to_stream_segment(sFilename_stream_edge_geojson, + sFilename_stream_segment_geojson, pVertex_outlet) return else: - #use the only one basin - if iFlag_flowline == 1: #we usually has one outlet + #use the only one basin + if iFlag_flowline == 1: #we usually has one outlet #for iWatershed in range(1, nWatershed+1): iWatershed = 1 - pBasin = self.aBasin[iWatershed-1] - pBasin_pyflowline = self.pPyFlowline.aBasin[iWatershed-1] + pBasin = self.aBasin[iWatershed-1] + pBasin_pyflowline = self.pPyFlowline.aBasin[iWatershed-1] sFilename_stream_edge = pBasin.sFilename_stream_edge_json sFilename_stream_edge_geojson = pBasin.sFilename_stream_edge sFilename_stream_segment_geojson = pBasin.sFilename_stream_segment @@ -940,25 +940,25 @@ def pyhexwatershed_export_stream_segment(self): aVariable_json_in = ['lStream_segment'] aVariable_geojson_out = ['stream_segment'] aVariable_type_out= [1] - export_json_to_geojson_polyline(sFilename_stream_edge, - sFilename_stream_edge_geojson, + export_json_to_geojson_polyline(sFilename_stream_edge, + sFilename_stream_edge_geojson, aVariable_json_in, aVariable_geojson_out, aVariable_type_out) - merge_stream_edge_to_stream_segment(sFilename_stream_edge_geojson, - sFilename_stream_segment_geojson, + merge_stream_edge_to_stream_segment(sFilename_stream_edge_geojson, + sFilename_stream_segment_geojson, pVertex_outlet) else: #even there is no flowline, it could be elevation-based simulation iWatershed = 1 - pBasin = self.aBasin[iWatershed-1] + pBasin = self.aBasin[iWatershed-1] sFilename_stream_edge = pBasin.sFilename_stream_edge_json sFilename_stream_edge_geojson = pBasin.sFilename_stream_edge - sFilename_stream_segment_geojson = pBasin.sFilename_stream_segment + sFilename_stream_segment_geojson = pBasin.sFilename_stream_segment aVariable_json_in = ['lStream_segment'] aVariable_geojson_out = ['stream_segment'] aVariable_type_out= [1] - export_json_to_geojson_polyline(sFilename_stream_edge, - sFilename_stream_edge_geojson, + export_json_to_geojson_polyline(sFilename_stream_edge, + sFilename_stream_edge_geojson, aVariable_json_in, aVariable_geojson_out, aVariable_type_out) @@ -971,18 +971,18 @@ def pyhexwatershed_export_stream_segment(self): lIndex_longtitude = np.where(dummy_data[:,0] == 'Outlet longitude degree')[0] lIndex_latitude = np.where(dummy_data[:,0] == 'Outlet latitude degree')[0] dLongitude_outlet_longitude = float(dummy_data[lIndex_longtitude, 1]) - dLatitude_outlet_latitude = float(dummy_data[lIndex_latitude, 1]) + dLatitude_outlet_latitude = float(dummy_data[lIndex_latitude, 1]) point = dict() point['dLongitude_degree'] = dLongitude_outlet_longitude point['dLatitude_degree'] = dLatitude_outlet_latitude pVertex_outlet=pyvertex(point) - merge_stream_edge_to_stream_segment(sFilename_stream_edge_geojson, - sFilename_stream_segment_geojson, + merge_stream_edge_to_stream_segment(sFilename_stream_edge_geojson, + sFilename_stream_segment_geojson, pVertex_outlet) pass - - + + return @@ -994,30 +994,30 @@ def pyhexwatershed_export_flow_direction(self): if self.iFlag_global==1: sFilename_json = self.sFilename_hexwatershed_json sFilename_geojson = self.sFilename_flow_direction - export_json_to_geojson_polyline(sFilename_json, sFilename_geojson) + export_json_to_geojson_polyline(sFilename_json, sFilename_geojson) else: if self.iFlag_multiple_outlet==1: sFilename_json = self.sFilename_hexwatershed_json - sFilename_geojson = self.sFilename_flow_direction + sFilename_geojson = self.sFilename_flow_direction export_json_to_geojson_polyline(sFilename_json, sFilename_geojson) else: iWatershed = 1 - pBasin = self.aBasin[iWatershed-1] + pBasin = self.aBasin[iWatershed-1] #use the only one basin sFilename_json = pBasin.sFilename_watershed_json - sFilename_geojson = pBasin.sFilename_flow_direction - + sFilename_geojson = pBasin.sFilename_flow_direction + aVariable_json = ['lStream_segment','dDrainage_area'] #new names - + aVariable_geojson = ['stream_segment','drainage_area'] aVariable_type_out = [1, 2] export_json_to_geojson_polyline(sFilename_json, sFilename_geojson, aVariable_json, aVariable_geojson, aVariable_type_out) - - #convert to geoparquet for visualization + + #convert to geoparquet for visualization convert_geojson_to_geoparquet(sFilename_geojson, sFilename_geojson.replace('.geojson','.parquet')) - + def pyhexwatershed_export_elevation(self): """ @@ -1029,10 +1029,10 @@ def pyhexwatershed_export_elevation(self): aVariable_json = ['dElevation'] aVariable_geojson= ['elevation'] export_json_to_geojson_polygon(sFilename_json, - sFilename_geojson, + sFilename_geojson, aVariable_json, aVariable_geojson) - + def pyhexwatershed_export_slope(self): sFilename_json = self.sFilename_hexwatershed_json @@ -1040,39 +1040,39 @@ def pyhexwatershed_export_slope(self): aVariable_json = ['dSlope_between'] aVariable_geojson= ['slope'] export_json_to_geojson_polygon(sFilename_json, - sFilename_geojson, + sFilename_geojson, aVariable_json, aVariable_geojson) - + def pyhexwatershed_export_drainage_area(self): sFilename_json = self.sFilename_hexwatershed_json sFilename_geojson = self.sFilename_drainage_area aVariable_json = ['dDrainage_area'] aVariable_geojson= ['draiange_area'] export_json_to_geojson_polygon(sFilename_json, - sFilename_geojson, + sFilename_geojson, aVariable_json, aVariable_geojson) - - #starting from here, we will save watershed-level files - + + #starting from here, we will save watershed-level files + def pyhexwatershed_export_travel_distance(self): - + nWatershed = self.nOutlet - if self.iFlag_flowline==1: + if self.iFlag_flowline==1: for iWatershed in range(1, nWatershed+1): - pBasin = self.aBasin[iWatershed-1] + pBasin = self.aBasin[iWatershed-1] sFilename_json = pBasin.sFilename_watershed_json sFilename_geojson = pBasin.sFilename_distance_to_outlet aVariable_json = ['dDistance_to_watershed_outlet'] aVariable_geojson= ['travel_distance'] aVariable_type= [2] export_json_to_geojson_polygon(sFilename_json, - sFilename_geojson, + sFilename_geojson, aVariable_json, aVariable_geojson, aVariable_type ) - pass + pass def pyhexwatershed_export_all_polyline_variables(self): sFilename_json = self.sFilename_hexwatershed_json @@ -1081,7 +1081,7 @@ def pyhexwatershed_export_all_polyline_variables(self): aVariable_json = ['iStream_segment'] aVariable_geojson = ['stream_segment'] export_json_to_geojson_polyline(sFilename_json, - sFilename_geojson, + sFilename_geojson, aVariable_json, aVariable_geojson) return @@ -1092,42 +1092,42 @@ def pyhexwatershed_export_all_polygon_variables(self): """ if self.iFlag_global==1: sFilename_json = self.sFilename_hexwatershed_json - sFilename_geojson = self.sFilename_variable_polygon + sFilename_geojson = self.sFilename_variable_polygon pass else: if self.iFlag_multiple_outlet==1: sFilename_json = self.sFilename_hexwatershed_json - sFilename_geojson = self.sFilename_variable_polygon + sFilename_geojson = self.sFilename_variable_polygon else: iWatershed = 1 - pBasin = self.aBasin[iWatershed-1] + pBasin = self.aBasin[iWatershed-1] #use the only one basin sFilename_json = pBasin.sFilename_watershed_json - sFilename_geojson = pBasin.sFilename_variable_polygon + sFilename_geojson = pBasin.sFilename_variable_polygon if self.iMesh_type == 4: #mpas mesh aVariable_json = ['lSubbasin','lHillslope','dArea','dElevation','dSlope_between', 'dDrainage_area','dDistance_to_watershed_outlet'] #profile not enabled aVariable_geojson = ['subbasin','hillslope','area','elevation', 'slope', 'drainage_area','travel_distance'] else: - aVariable_json = ['lSubbasin','lHillslope','dArea','dElevation','dSlope_between', 'dDrainage_area','dDistance_to_watershed_outlet'] #profile not enabled + aVariable_json = ['lSubbasin','lHillslope','dArea','dElevation','dSlope_between', 'dDrainage_area','dDistance_to_watershed_outlet'] #profile not enabled aVariable_geojson = ['subbasin','hillslope','area','elevation', 'slope', 'drainage_area','travel_distance'] aVariable_type= [1,1,2,2,2,2,2] export_json_to_geojson_polygon(sFilename_json, - sFilename_geojson, + sFilename_geojson, aVariable_json, aVariable_geojson, - aVariable_type) + aVariable_type) #convert to geoparquet for visualization sFilename_parquet = sFilename_geojson.replace('.geojson','.parquet') convert_geojson_to_geoparquet(sFilename_geojson, sFilename_parquet) #because each geojson file has many small polygons, we can merge them into large polygons #get the folder of the geojson sFolder = os.path.dirname(sFilename_geojson) - sFilename_subbasin = os.path.join( sFolder, 'subbasin.parquet' ) #sFilename_geojson.replace('.geojson','_subbasin.parquet') + sFilename_subbasin = os.path.join( sFolder, 'subbasin.parquet' ) #sFilename_geojson.replace('.geojson','_subbasin.parquet') merge_cell_to_polygon(sFilename_parquet, sFilename_subbasin,'subbasin') - sFilename_hillslope = os.path.join( sFolder, 'hillslope.parquet' ) #sFilename_geojson.replace('.geojson','_hillslope.parquet') + sFilename_hillslope = os.path.join( sFolder, 'hillslope.parquet' ) #sFilename_geojson.replace('.geojson','_hillslope.parquet') merge_cell_to_polygon(sFilename_parquet, sFilename_hillslope,'hillslope') - + return From 596bfa66a52216cff9fea5768e4bb67670fee705 Mon Sep 17 00:00:00 2001 From: changliao1025 Date: Thu, 9 May 2024 15:34:37 -0700 Subject: [PATCH 3/3] update version --- CITATION.cff | 4 ++-- conda-recipe/conda_build_config.yaml | 6 +++--- conda-recipe/meta.yaml | 24 ++++++++++++------------ external/setup_with_hexwatershed.py | 24 ++++++++++++------------ pyhexwatershed/classes/pycase.py | 6 +++--- setup.cfg | 2 +- setup.py | 8 ++++---- 7 files changed, 37 insertions(+), 37 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index c8f211c..b1e2894 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -3,8 +3,8 @@ message: "If you use this software, please cite it as below." authors: - family-names: Liao given-names: Chang - orcid: https://orcid.org/0000-0002-7348-8858 + orcid: https://orcid.org/0000-0002-7348-8858 title: "HexWatershed: a mesh-independent flow direction model for hydrologic models" -version: 0.2.29 +version: 0.2.30 doi: 10.5281/zenodo.6425881 date-released: 2022-03-31 \ No newline at end of file diff --git a/conda-recipe/conda_build_config.yaml b/conda-recipe/conda_build_config.yaml index 87ed2bb..ee3fe02 100644 --- a/conda-recipe/conda_build_config.yaml +++ b/conda-recipe/conda_build_config.yaml @@ -16,14 +16,14 @@ build_string: py38_0 test: imports: - pyhexwatershed - + # Define the package metadata metadata: # Package name name: hexwatershed # Package version - version: "0.2.29" + version: "0.2.30" # Package summary summary: A mesh-independent flow direction model for hydrologic models # Package homepage @@ -46,4 +46,4 @@ metadata: - python - setuptools - pip - + diff --git a/conda-recipe/meta.yaml b/conda-recipe/meta.yaml index bd31a5d..0909824 100644 --- a/conda-recipe/meta.yaml +++ b/conda-recipe/meta.yaml @@ -1,6 +1,6 @@ {% set name = "hexwatershed" %} {% set git_rev = "main" %} -{% set version = "0.2.29" %} +{% set version = "0.2.30" %} package: name: {{ name|lower }} @@ -13,29 +13,29 @@ source: #git_rev: {{ git_rev }} #git_url: https://github.com/changliao1025/pyhexwatershed -build: +build: noarch: python #script: - # - pip install . + # - pip install . # - python setup.py build_external -vv - + number: 0 - -requirements: - + +requirements: + build: - cmake - make - {{ compiler('cxx') }} - - python >=3.8 + - python >=3.8 - setuptools - pip - + host: - - python >=3.8 + - python >=3.8 - pip - {{ compiler('cxx') }} - + run: - python >=3.8 - numpy @@ -46,7 +46,7 @@ requirements: test: imports: - pyhexwatershed - + about: home: https://github.com/changliao1025/pyhexwatershed diff --git a/external/setup_with_hexwatershed.py b/external/setup_with_hexwatershed.py index 95e7418..936932a 100644 --- a/external/setup_with_hexwatershed.py +++ b/external/setup_with_hexwatershed.py @@ -11,7 +11,7 @@ AUTHOR = "Chang Liao" AUTHOR_EMAIL = "chang.liao@pnnl.gov" URL = "https://github.com/changliao1025/pyhexwatershed" -VERSION = "0.2.29" +VERSION = "0.2.30" REQUIRES_PYTHON = ">=3.8.0" KEYWORDS = ["hexwatershed", "hydrology", @@ -22,7 +22,7 @@ REQUIRED = [ "numpy", - "gdal", + "gdal", "shapely", "pyflowline" ] @@ -45,12 +45,12 @@ def get_data_files(sFolder_in): for root, dirs, files in os.walk(sFolder_in): for file in files: data_files_tmp.append(os.path.join(root, file)) - + #print(data_files_tmp) return data_files_tmp -data_files=[ ( 'external/hexwatershed/', ["external/hexwatershed/CMakeLists.txt"] ) , +data_files=[ ( 'external/hexwatershed/', ["external/hexwatershed/CMakeLists.txt"] ) , ( "external/rapidjson/" , get_data_files('external/rapidjson') ), ( "external/rapidjson/error/" , get_data_files('external/rapidjson/error/') ), ( "external/rapidjson/internal/" , get_data_files('external/rapidjson/internal') ), @@ -60,7 +60,7 @@ def get_data_files(sFolder_in): ( "external/hexwatershed/src/domain/" , get_data_files('external/hexwatershed/src/domain') ), ( "external/hexwatershed/src/json/" , get_data_files('external/hexwatershed/src/json') ) ] - + HERE = os.path.abspath(os.path.dirname(__file__)) HERE = os.path.expandvars(HERE) # Check if the expanded path exists @@ -112,18 +112,18 @@ def run(self): source_path = os.path.join( HERE, "external", "hexwatershed") - + source_path = os.path.expandvars(source_path) # Check if the expanded path exists if os.path.exists(source_path): print('Path exists:', source_path) else: - print('Path does not exist:', source_path) + print('Path does not exist:', source_path) builds_path = os.path.join(source_path, "build") - if os.path.exists(builds_path): + if os.path.exists(builds_path): sFilename_cache = os.path.join(builds_path, "CMakeCache.txt") if os.path.exists(sFilename_cache): os.remove(sFilename_cache) @@ -152,7 +152,7 @@ def run(self): libdst_path, ignore_errors=True) os.chdir(builds_path) - #copy cmakelistx.txt to the build folder + #copy cmakelistx.txt to the build folder dst = os.getcwd() shutil.copy("../CMakeLists.txt", dst) config_call = ["cmake", "CMakeLists.txt"] @@ -162,13 +162,13 @@ def run(self): self.announce("cmake complie", level=3) ver = get_cmake_version() - + compilecall = [ "cmake", "--build", ".", "--config", "Release", "--target", "install" ] - + subprocess.run(compilecall, check=True) @@ -197,7 +197,7 @@ def run(self): setup_requires=['setuptools'], packages=find_packages(), package_data={ - "pyhexwatershed": ["_bin/*", "_lib/*"] + "pyhexwatershed": ["_bin/*", "_lib/*"] }, install_requires=REQUIRED, cmdclass={"build_external": build_external}, diff --git a/pyhexwatershed/classes/pycase.py b/pyhexwatershed/classes/pycase.py index 86115eb..6a5a898 100644 --- a/pyhexwatershed/classes/pycase.py +++ b/pyhexwatershed/classes/pycase.py @@ -655,9 +655,9 @@ def pyhexwatershed_assign_elevation_to_cells(self, dMissing_value_in =None): pDataset_clip = pDriver_memory.Create('', iNewWidth, iNewHeigh, 1, gdalconst.GDT_Float32) pDataset_clip.SetGeoTransform( newGeoTransform ) pDataset_clip.SetProjection( pProjection) - pWrapOption = gdal.WarpOptions( cropToCutline=True,cutlineDSName = sFilename_shapefile_cut , \ - width=iNewWidth, \ - height=iNewHeigh, \ + pWrapOption = gdal.WarpOptions( cropToCutline=True,cutlineDSName = sFilename_shapefile_cut , + width=iNewWidth, + height=iNewHeigh, dstSRS=pProjection , format = 'MEM' ) pDataset_clip = gdal.Warp('',pDataset_elevation, options=pWrapOption) pBand = pDataset_clip.GetRasterBand( 1 ) diff --git a/setup.cfg b/setup.cfg index 0b9c041..654c44d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.2.29 +current_version = 0.2.30 commit = True tag = True diff --git a/setup.py b/setup.py index 40b404a..ee5e30e 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ AUTHOR = "Chang Liao" AUTHOR_EMAIL = "chang.liao@pnnl.gov" URL = "https://github.com/changliao1025/pyhexwatershed" -VERSION = "0.2.29" +VERSION = "0.2.30" REQUIRES_PYTHON = ">=3.8.0" KEYWORDS = ["hexwatershed", "hydrology", @@ -22,7 +22,7 @@ REQUIRED = [ "numpy", - "gdal", + "gdal", "pyearth", "pyflowline" ] @@ -38,8 +38,8 @@ "Topic :: Scientific/Engineering :: Hydrology", "Topic :: Scientific/Engineering :: GIS", "Topic :: Scientific/Engineering :: Physics" -] - +] + HERE = os.path.abspath(os.path.dirname(__file__)) HERE = os.path.expandvars(HERE) # Check if the expanded path exists