wolfhece 2.2.28__py3-none-any.whl → 2.2.29__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,6 +14,7 @@ from typing import Union, Literal
14
14
  from ..PyVertexvectors import Zones, zone, vector, wolfvertex, getIfromRGB
15
15
  from ..PyTranslate import _
16
16
  from ..scenario. config_manager import Config_Manager_2D_GPU
17
+ from ..eikonal import inpaint_waterlevel, inpaint_array
17
18
 
18
19
  import subprocess
19
20
  import geopandas as gpd
@@ -28,6 +29,7 @@ from tqdm import tqdm
28
29
  from pyogrio import list_layers, read_dataframe
29
30
  from enum import Enum
30
31
  import numba as nb
32
+ import shutil
31
33
 
32
34
  ENGINE = 'pyogrio' # or 'Fiona -- Pyogrio is faster
33
35
  EXTENT = '.gpkg'
@@ -104,14 +106,14 @@ def empty_folder(folder):
104
106
  fn = os.path.join(folder, files)
105
107
  try:
106
108
  if os.path.isfile(fn) or os.path.islink(fn):
107
- os.unlink(fn)
109
+ os.unlink(fn)
108
110
  elif os.path.isdir(fn):
109
- shutil.rmtree(fn)
111
+ shutil.rmtree(fn)
110
112
  except Exception as e:
111
113
  print(f"Error when deleting file {fn}: {e}")
112
114
  else:
113
115
  print("The folder does not exist.")
114
-
116
+
115
117
  class Accept_Manager():
116
118
  """
117
119
  Structure to store the directories and names of the files.
@@ -207,6 +209,7 @@ class Accept_Manager():
207
209
  self.IN_CSV = self.IN_DIR / "CSVs"
208
210
  self.IN_WATER_DEPTH = self.IN_DIR / "WATER_DEPTH"
209
211
  self.IN_EPU_STATIONS= self.IN_DIR / "EPU_STATIONS_NEW"
212
+ self.IN_SA_INTERP = None
210
213
 
211
214
  self.ORIGINAL_GDB = self.IN_DATABASE / self._original_gdb
212
215
  self.CAPA_WALLOON = self.IN_DATABASE / self._capa_walloon
@@ -410,7 +413,7 @@ class Accept_Manager():
410
413
  if Study_area in self.get_list_studyareas(with_suffix=True) or Study_area+".shp" in self.get_list_studyareas(with_suffix=True):
411
414
  self._study_area = Path(Study_area)
412
415
  else:
413
- logging.error("The study area does not exist in the study area directory")
416
+ logging.error(_("The study area does not exist in the study area directory"))
414
417
 
415
418
  self.create_paths()
416
419
 
@@ -422,7 +425,7 @@ class Accept_Manager():
422
425
  self.check_temporary()
423
426
  self.check_outputs()
424
427
  else:
425
- logging.error("The scenario does not exist in the water depth directory")
428
+ logging.error(_("The scenario does not exist in the water depth directory"))
426
429
 
427
430
  def get_files_in_rm_buildings(self) -> list[Path]:
428
431
  return [Path(a) for a in glob.glob(str(self.IN_RM_BUILD_DIR / ("*"+ EXTENT)))]
@@ -471,7 +474,7 @@ class Accept_Manager():
471
474
  if self.IN_SA_INTERP.exists() :
472
475
  files = [Path(a) for a in glob.glob(str(self.IN_SA_INTERP / "*.tif"))]
473
476
  else :
474
- logging.error("No such scenario")
477
+ logging.error(_("No such scenario"))
475
478
  return files
476
479
 
477
480
  def get_sims_files_for_baseline(self) -> list[Path]:
@@ -481,7 +484,7 @@ class Accept_Manager():
481
484
  track = Path(str(self.IN_SA_BASE_INTERP / "*.tif"))
482
485
  files = [Path(a) for a in glob.glob(str(track))]
483
486
  else :
484
- logging.error("No _baseline interpolated free surfaces files")
487
+ logging.error(_("No _baseline interpolated free surfaces files"))
485
488
 
486
489
  return files
487
490
 
@@ -576,7 +579,7 @@ class Accept_Manager():
576
579
  # List files in directory
577
580
  sims = self.get_sims_files_for_scenario()
578
581
  sims_modif = [
579
- os.path.join(os.path.dirname(path), os.path.basename(path).replace("Q", "T"))
582
+ os.path.join(os.path.dirname(path), os.path.basename(path).replace("Q", "T").split('_')[0])
580
583
  for path in sims
581
584
  ]
582
585
 
@@ -590,7 +593,7 @@ class Accept_Manager():
590
593
  idx_h = [Path(cursim).name.find(".tif") for cursim in sims_modif]
591
594
 
592
595
  # create the list of return periods -- only the numeric part
593
- sims = [int(Path(cursim).name[idx_T[i]+1:idx_h[i]]) for i, cursim in enumerate(sims_modif)]
596
+ sims = [int(Path(cursim).name[idx_T[i]+1:]) for i, cursim in enumerate(sims_modif)]
594
597
  return sorted(sims)
595
598
 
596
599
  def get_modifiedrasters(self):
@@ -653,51 +656,51 @@ class Accept_Manager():
653
656
 
654
657
  err = False
655
658
  if not self.IN_DATABASE.exists():
656
- logging.error("INPUT : The database directory does not exist")
659
+ logging.error(_("INPUT : The database directory does not exist"))
657
660
  err = True
658
661
 
659
662
  if not self.IN_STUDY_AREA.exists():
660
- logging.error("INPUT : The study area directory does not exist")
663
+ logging.error(_("INPUT : The study area directory does not exist"))
661
664
  err = True
662
665
 
663
666
  if not self.IN_CSV.exists():
664
- logging.error("INPUT : The CSV directory does not exist")
667
+ logging.error(_("INPUT : The CSV directory does not exist"))
665
668
  err = True
666
669
 
667
670
  if not self.IN_WATER_DEPTH.exists():
668
- logging.error("INPUT : The water depth directory does not exist")
671
+ logging.error(_("INPUT : The water depth directory does not exist"))
669
672
  err = True
670
673
 
671
674
  if not self.IN_EPU_STATIONS.exists():
672
- logging.error("INPUT : The EPU stations directory does not exist")
675
+ logging.error(_("INPUT : The EPU stations directory does not exist"))
673
676
  err = True
674
677
 
675
678
  if self.Study_area is not None:
676
679
  if not self.SA.exists():
677
- logging.error("INPUT : The study area file does not exist")
680
+ logging.error(_("INPUT : The study area file does not exist"))
678
681
  err = True
679
682
 
680
683
  if not self.ORIGINAL_GDB.exists():
681
- logging.error("INPUT : The original gdb file does not exist - Please pull it from the SPW-ARNE")
684
+ logging.error(_("INPUT : The original gdb file does not exist - Please pull it from the SPW-ARNE"))
682
685
  err = True
683
686
 
684
687
  if not self.CAPA_WALLOON.exists():
685
- logging.error("INPUT : The Cadastre Walloon file does not exist - Please pull it from the SPW")
688
+ logging.error(_("INPUT : The Cadastre Walloon file does not exist - Please pull it from the SPW"))
686
689
  err = True
687
690
 
688
691
  if not self.PICC_WALLOON.exists():
689
- logging.error("INPUT : The PICC Walloon file does not exist - Please pull it from the SPW website")
692
+ logging.error(_("INPUT : The PICC Walloon file does not exist - Please pull it from the SPW website"))
690
693
  err = True
691
694
 
692
695
  if not self.CE_IGN_TOP10V.exists():
693
- logging.error("INPUT : The CE IGN top10v file does not exist - Please pull it from the IGN")
696
+ logging.error(_("INPUT : The CE IGN top10v file does not exist - Please pull it from the IGN"))
694
697
  err = True
695
698
 
696
699
  if self.scenario is None:
697
700
  logging.debug("The scenario has not been defined")
698
701
  else:
699
702
  if not self.IN_SCEN_DIR.exists():
700
- logging.error("The wd scenario directory does not exist")
703
+ logging.error(_("The wd scenario directory does not exist"))
701
704
  err = True
702
705
 
703
706
  return not err
@@ -756,15 +759,15 @@ class Accept_Manager():
756
759
  """ Check if the necessary files are present before the database creation"""
757
760
 
758
761
  if not self.is_valid_inputs:
759
- logging.error("Theere are missing input directories - Please check carefully the input directories and the logs")
762
+ logging.error(_("Theere are missing input directories - Please check carefully the input directories and the logs"))
760
763
  return False
761
764
 
762
765
  if not self.is_valid_study_area:
763
- logging.error("The study area file does not exist - Please create it")
766
+ logging.error(_("The study area file does not exist - Please create it"))
764
767
  return False
765
768
 
766
769
  if not self.is_valid_vulnerability_csv:
767
- logging.error("The vulnerability CSV file does not exist - Please create it")
770
+ logging.error(_("The vulnerability CSV file does not exist - Please create it"))
768
771
  return False
769
772
 
770
773
  return True
@@ -772,11 +775,11 @@ class Accept_Manager():
772
775
  def check_before_rasterize(self) -> bool:
773
776
 
774
777
  if not self.TMP_CODEVULNE.exists():
775
- logging.error("The final database with vulnerability levels does not exist")
778
+ logging.error(_("The final database with vulnerability levels does not exist"))
776
779
  return False
777
780
 
778
781
  if not self.TMP_WMODIF.exists():
779
- logging.error("The vector data with modifications does not exist")
782
+ logging.error(_("The vector data with modifications does not exist"))
780
783
  return False
781
784
 
782
785
  return True
@@ -784,19 +787,19 @@ class Accept_Manager():
784
787
  def check_before_vulnerability(self) -> bool:
785
788
 
786
789
  if self.SA is None:
787
- logging.error("The area of interest does not exist")
790
+ logging.error(_("The area of interest does not exist"))
788
791
  return False
789
792
 
790
793
  if self.IN_WATER_DEPTH is None:
791
- logging.error("The water depth directory does not exist")
794
+ logging.error(_("The water depth directory does not exist"))
792
795
  return False
793
796
 
794
797
  if self.IN_SCEN_DIR is None:
795
- logging.error("The wd scenario directory does not exist in the water depth directory")
798
+ logging.error(_("The wd scenario directory does not exist in the water depth directory"))
796
799
  return False
797
800
 
798
801
  if self.SA_MASKED_RIVER is None:
799
- logging.error("The IGN raster does not exist")
802
+ logging.error(_("The IGN raster does not exist"))
800
803
  return False
801
804
 
802
805
  return True
@@ -804,11 +807,11 @@ class Accept_Manager():
804
807
  def check_vuln_code_sa(self) -> bool:
805
808
 
806
809
  if not self.SA_VULN.exists():#SA_VULN
807
- logging.error("The vulnerability raster file does not exist")
810
+ logging.error(_("The vulnerability raster file does not exist"))
808
811
  return False
809
812
 
810
813
  if not self.SA_CODE.exists():
811
- logging.error("The vulnerability code raster file does not exist")
814
+ logging.error(_("The vulnerability code raster file does not exist"))
812
815
  return False
813
816
 
814
817
  return True
@@ -816,11 +819,11 @@ class Accept_Manager():
816
819
  def check_vuln_code_scenario(self) -> bool:
817
820
 
818
821
  if not self.TMP_VULN.exists():
819
- logging.error("The vulnerability raster file does not exist")
822
+ logging.error(_("The vulnerability raster file does not exist"))
820
823
  return False
821
824
 
822
825
  if not self.TMP_CODE.exists():
823
- logging.error("The vulnerability code raster file does not exist")
826
+ logging.error(_("The vulnerability code raster file does not exist"))
824
827
  return False
825
828
 
826
829
  return True
@@ -913,15 +916,15 @@ class Accept_Manager():
913
916
  vuln = self.get_files_in_rasters_vulne()
914
917
 
915
918
  if len(code) == 0:
916
- logging.error("The code rasters do not exist")
919
+ logging.error(_("The code rasters do not exist"))
917
920
  return False
918
921
 
919
922
  if len(vuln) == 0:
920
- logging.error("The vulnerability rasters do not exist")
923
+ logging.error(_("The vulnerability rasters do not exist"))
921
924
  return False
922
925
 
923
926
  if len(code) != len(vuln):
924
- logging.error("The number of code and vulnerability rasters do not match")
927
+ logging.error(_("The number of code and vulnerability rasters do not match"))
925
928
  return False
926
929
 
927
930
  # we take a reference raster
@@ -941,15 +944,15 @@ class Accept_Manager():
941
944
  col_cur, row_cur = band_cur.XSize, band_cur.YSize
942
945
 
943
946
  if geo_ref != geo_cur:
944
- logging.error("The geotransforms do not match {}".format(cur))
947
+ logging.error(_("The geotransforms do not match {}".format(cur)))
945
948
  diff.append(cur)
946
949
 
947
950
  if proj_ref != proj_cur:
948
- logging.error("The projections do not match {}".format(cur))
951
+ logging.error(_("The projections do not match {}".format(cur)))
949
952
  diff.append(cur)
950
953
 
951
954
  if col_ref != col_cur or row_ref != row_cur:
952
- logging.error("The dimensions do not match {}".format(cur))
955
+ logging.error(_("The dimensions do not match {}".format(cur)))
953
956
  diff.append(cur)
954
957
 
955
958
  return diff
@@ -1052,77 +1055,157 @@ class Accept_Manager():
1052
1055
  trace = None
1053
1056
  if os.path.exists(self.OUT_MASKED_RIVER):
1054
1057
  trace = self.OUT_MASKED_RIVER
1055
- if os.path.exists(self.OUT_MASKED_RIVER_S):
1058
+ elif os.path.exists(self.OUT_MASKED_RIVER_S):
1056
1059
  trace = self.OUT_MASKED_RIVER_S
1057
1060
  else :
1058
- logging.error("No Masked_River_extent files. Please provide them.")
1061
+ logging.error(_("No Masked_River_extent files. Please provide them."))
1059
1062
  return trace
1060
-
1063
+
1061
1064
  # Interpolation
1062
- # -----------------------------
1063
- def batch_creation_and_interpolation(self, checked_paths, iftest):
1065
+ # -------------
1066
+
1067
+ def batch_creation_and_interpolation_fotran_holes(self, checked_paths:list[Path], iftest:bool) -> tuple[bool, list[str]]:
1064
1068
  """Creates a batch file to launch holes.exe from the selected simulations and launches it.
1065
- -Every files in EXTRACTED_LAST_STEP are interpolated for tests (iftest==True).
1066
- -Only the check simulations of the windows are interpolated for the GUI (iftest!=True)."""
1069
+
1070
+ - Every files in EXTRACTED_LAST_STEP are interpolated for tests (iftest==True).
1071
+ - Only the check simulations of the windows are interpolated for the GUI (iftest!=True).
1072
+
1073
+ :param checked_paths: list of paths to the checked simulations
1074
+ :param iftest: boolean to indicate if the function is called from the tests or not
1075
+ """
1076
+
1067
1077
  path_LastSteps = Path(self.IN_SA_EXTRACTED)
1068
- C = None
1069
- D = None
1078
+
1079
+ # Identifying the DEM and its mask
1080
+ C = None # DEM mask
1081
+ D = None # DEM
1070
1082
  for file in os.listdir(Path(self.IN_SA_DEM)):
1071
1083
  file_path = Path(self.IN_SA_DEM) / file
1072
1084
  if file_path.is_file() and file.startswith("MNT_") and file_path.suffix == ".bin":
1073
1085
  if "mask" not in file:
1074
1086
  D = file_path
1075
1087
  else:
1076
- C = file_path
1077
- if D == None:
1078
- return logging.error("DEM (.bin) not found in DEM_FILES. The file must begins by 'MNT_' and CANNOT include the word 'mask'")
1079
-
1080
- if C == None:
1081
- return logging.error("DEM mask (.bin) not found in DEM_FILES. The file must begins by 'MNT_' and MUST include the word 'mask'")
1082
-
1088
+ C = file_path
1089
+
1090
+ if D is None:
1091
+ return logging.error(_("DEM (.bin) not found in DEM_FILES. The file must begins by 'MNT_' and CANNOT include the word 'mask'"))
1092
+
1093
+ if C is None:
1094
+ return logging.error(_("DEM mask (.bin) not found in DEM_FILES. The file must begins by 'MNT_' and MUST include the word 'mask'"))
1095
+
1083
1096
  path_Interp = Path(self.IN_SA_INTERP)
1084
1097
  path_bat_file = os.path.join(self.IN_SCEN_DIR, "process_files.bat")
1085
-
1098
+
1086
1099
  if os.path.exists(path_bat_file):
1087
1100
  logging.info(f"The file {path_bat_file} already exists and will be replaced.")
1088
1101
  os.remove(path_bat_file)
1089
- path_code = os.path.join(self.IN_WATER_DEPTH, "holes.exe")
1102
+
1103
+ path_code = os.path.join(self.IN_WATER_DEPTH, "holes.exe")
1104
+
1090
1105
  A, B = [], []
1091
- if iftest == True:
1092
- #no checked box in the tests
1093
- path_Interp = Path(self.IN_SA_INTERP)
1094
- path_LastSteps = Path(self.IN_SA_EXTRACTED)
1095
- A = [os.path.join(path_LastSteps,f) for f in os.listdir(path_LastSteps) if f.endswith('.bin') and not f.endswith('.bin.txt')]
1096
- B = [os.path.join(path_Interp, os.path.splitext(os.path.basename(f))[0]) for f in A]
1097
-
1098
- else :
1106
+ if iftest:
1107
+ # no checked box in the tests
1108
+ A = [path_LastSteps / f for f in os.listdir(path_LastSteps) if f.endswith('.bin') and not f.endswith('.bin.txt')]
1109
+
1110
+ else :
1099
1111
  for path in checked_paths:
1100
- parts = path.name.split("sim_")
1101
- A.extend([os.path.join(path_LastSteps, g) for g in os.listdir(path_LastSteps) if g.endswith(f"{parts[1]}.bin")])
1102
- B = [os.path.join(path_Interp, os.path.splitext(os.path.basename(f))[0]) for f in A]
1112
+ parts = path.name.split("sim_")
1113
+ A.extend([path_LastSteps / g for g in os.listdir(path_LastSteps) if g.endswith(f"{parts[1]}.bin")])
1114
+
1115
+ B = [path_Interp / f.stem for f in A]
1116
+
1103
1117
  if not A or not B or not C or not D:
1104
- return logging.error("Missing files.")
1118
+ return logging.error(_("Missing files."))
1119
+
1105
1120
  with open(path_bat_file, "w") as bat_file:
1106
1121
  for a, b in zip(A, B):
1107
1122
  line = f'"{path_code}" filling in="{a}" out="{b}" mask="{C}" dem="{D} avoid_last=1"\n'
1108
1123
  bat_file.write(line)
1109
-
1124
+
1110
1125
  empty_folder(self.IN_SA_INTERP)
1111
- path_bat_file = os.path.join(self.IN_SCEN_DIR, "process_files.bat")
1126
+ path_bat_file = self.IN_SCEN_DIR / "process_files.bat"
1112
1127
  subprocess.run([path_bat_file], check=True)
1113
-
1128
+
1114
1129
  renamed_files = []
1115
- path_fichier=self.IN_SA_INTERP
1130
+ path_fichier = self.IN_SA_INTERP
1116
1131
  for file in path_fichier.glob("*.tif"):
1117
- if "_h" in file.name:
1132
+ if "_h" in file.name:
1118
1133
  new_name = file.stem.split("_h")[0].replace(".bin", "") + ".tif"
1119
1134
  file.rename(file.with_name(new_name))
1120
- renamed_files.append(new_name)
1135
+ renamed_files.append(new_name)
1136
+
1121
1137
  #deleting the other
1122
1138
  for file in path_fichier.glob("*.tif"):
1123
1139
  if "_combl" in file.name or file.name not in renamed_files:
1124
1140
  file.unlink()
1125
-
1141
+
1142
+ return True, renamed_files
1143
+
1144
+ def batch_creation_and_interpolation_python_eikonal(self, checked_paths:list[Path], iftest:bool) -> tuple[bool, list[str]]:
1145
+ """Creates a batch file to launch holes.exe from the selected simulations and launches it.
1146
+
1147
+ - Every files in EXTRACTED_LAST_STEP are interpolated for tests (iftest==True).
1148
+ - Only the check simulations of the windows are interpolated for the GUI (iftest!=True).
1149
+
1150
+ :param checked_paths: list of paths to the checked simulations
1151
+ :param iftest: boolean to indicate if the function is called from the tests or not
1152
+ """
1153
+
1154
+ path_LastSteps = Path(self.IN_SA_EXTRACTED)
1155
+
1156
+ # Identifying the DEM and its mask
1157
+ C = None # DEM mask
1158
+ D = None # DEM
1159
+ for file in os.listdir(Path(self.IN_SA_DEM)):
1160
+ file_path = Path(self.IN_SA_DEM) / file
1161
+ if file_path.is_file() and file.startswith("MNT_") and file_path.suffix == ".bin":
1162
+ if "mask" not in file:
1163
+ D = file_path
1164
+ else:
1165
+ C = file_path
1166
+
1167
+ if D is None:
1168
+ return logging.error(_("DTM (.bin) not found in DTM_FILES. The file must begins by 'MNT_' and CANNOT include the word 'mask'"))
1169
+
1170
+ if C is None:
1171
+ return logging.error(_("DEM mask (.bin) not found in DEM_FILES. The file must begins by 'MNT_' and MUST include the word 'mask'"))
1172
+
1173
+ path_Interp = Path(self.IN_SA_INTERP)
1174
+ path_bat_file = os.path.join(self.IN_SCEN_DIR, "process_files.bat")
1175
+
1176
+ if os.path.exists(path_bat_file):
1177
+ logging.info(f"The file {path_bat_file} already exists and will be replaced.")
1178
+ os.remove(path_bat_file)
1179
+
1180
+ A, B = [], []
1181
+ if iftest:
1182
+ # no checked box in the tests
1183
+ A = [path_LastSteps / f for f in os.listdir(path_LastSteps) if f.endswith('.bin') and not f.endswith('.bin.txt')]
1184
+
1185
+ else :
1186
+ for path in checked_paths:
1187
+ parts = path.name.split("sim_")
1188
+ A.extend([path_LastSteps / g for g in os.listdir(path_LastSteps) if g.endswith(f"{parts[1]}.bin")])
1189
+
1190
+ B = [path_Interp / f.stem for f in A]
1191
+
1192
+ if not A or not B or not C or not D:
1193
+ return logging.error(_("Missing files."))
1194
+
1195
+ renamed_files = []
1196
+ for a, b in zip(A, B):
1197
+ wa_a = WolfArray(a)
1198
+ wa_c = WolfArray(C)
1199
+ wa_d = WolfArray(D)
1200
+ _t, _d, wh = inpaint_array(data = wa_a.array,
1201
+ where_compute = wa_c.array.data != wa_c.array.data[0,0],
1202
+ test = wa_d.array.data,
1203
+ ignore_last_patches= 1)
1204
+
1205
+ new_name = b.with_suffix(".tif")
1206
+ wa_a.write_all(new_name)
1207
+ renamed_files.append(new_name.name)
1208
+
1126
1209
  return True, renamed_files
1127
1210
 
1128
1211
  def clip_layer(layer:str,
@@ -1171,7 +1254,7 @@ def clip_layer(layer:str,
1171
1254
 
1172
1255
  df.to_file(str(output_dir / (layer+EXTENT)), mode='w', engine=ENGINE)
1173
1256
  except Exception as e:
1174
- logging.error("Error while saving the clipped " + str(layer) + " to file")
1257
+ logging.error(_("Error while saving the clipped " + str(layer) + " to file"))
1175
1258
  logging.error(e)
1176
1259
  pass
1177
1260
 
@@ -1208,7 +1291,7 @@ def data_modification(layer:str,
1208
1291
 
1209
1292
  # Read the data
1210
1293
  df:gpd.GeoDataFrame = gpd.read_file(input_file, engine=ENGINE)
1211
- nblines, _ = df.shape
1294
+ nblines, notused = df.shape
1212
1295
 
1213
1296
  if nblines>0:
1214
1297
  op = manager.get_operand(input_file)
@@ -1333,7 +1416,7 @@ def data_modification(layer:str,
1333
1416
  if manager.is_polygons(set(df.geom_type)):
1334
1417
  df.to_file(output_file, engine=ENGINE)
1335
1418
  else:
1336
- logging.error("The layer does not contains polygons - " + str(layer))
1419
+ logging.error(_("The layer does not contains polygons - " + str(layer)))
1337
1420
  else:
1338
1421
  raise ValueError(f"The operand {op} is not recognized")
1339
1422
 
@@ -1341,7 +1424,7 @@ def data_modification(layer:str,
1341
1424
  else:
1342
1425
  # Normally, phase 1 does not create empty files
1343
1426
  # But it is better to check... ;-)
1344
- logging.error("skipped" + str(layer) + "due to no polygon in the study area")
1427
+ logging.error(_("skipped" + str(layer) + "due to no polygon in the study area"))
1345
1428
  return "skipped" + str(layer) + "due to no polygon in the study area"
1346
1429
 
1347
1430
  def compute_vulnerability(manager:Accept_Manager):
@@ -1636,6 +1719,10 @@ def compute_acceptability(manager:Accept_Manager,
1636
1719
 
1637
1720
  points_accept = pd.read_csv(manager.POINTS_CSV)
1638
1721
 
1722
+ if interval not in points_accept["Interval"]:
1723
+ logging.error(_("The return period {} is not in the intermediate.csv file -- Please update it").format(interval))
1724
+ return None
1725
+
1639
1726
  points_accept = points_accept[points_accept["Interval"]==interval] #les wd vs Ti matrices
1640
1727
  points_accept = points_accept.reset_index()
1641
1728
 
@@ -1697,7 +1784,7 @@ def shp_to_raster(vector_fn:str | Path, raster_fn:str | Path, pixel_size:float =
1697
1784
  raster_path = Path(raster_fn)
1698
1785
 
1699
1786
  if not vector_path.exists():
1700
- logging.error(f"The vector file {vector_path} does not exist")
1787
+ logging.error(_(f"The vector file {vector_path} does not exist"))
1701
1788
  return
1702
1789
 
1703
1790
  if raster_path.exists():
@@ -1807,7 +1894,7 @@ def vector_to_raster(layer:str,
1807
1894
  # Open the data sources and read the extents
1808
1895
  source_ds:ogr.DataSource = ogr.Open(vector_input)
1809
1896
  if source_ds is None:
1810
- logging.error(f"Could not open the data source {layer}")
1897
+ logging.error(_(f"Could not open the data source {layer}"))
1811
1898
  return
1812
1899
  source_layer = source_ds.GetLayer()
1813
1900
 
wolfhece/apps/version.py CHANGED
@@ -5,7 +5,7 @@ class WolfVersion():
5
5
 
6
6
  self.major = 2
7
7
  self.minor = 2
8
- self.patch = 28
8
+ self.patch = 29
9
9
 
10
10
  def __str__(self):
11
11
 
wolfhece/irm_qdf.py CHANGED
@@ -9,17 +9,22 @@ copying or distribution of this file, via any medium, is strictly prohibited.
9
9
  """
10
10
 
11
11
  #Code INS des communes belges
12
- import numpy as np
12
+ import re
13
13
  from os import path, mkdir
14
14
  from pathlib import Path
15
15
  from time import sleep
16
+ from typing import Literal, Union
17
+ import logging
18
+
19
+ from tqdm import tqdm
16
20
  import pandas as pd
17
21
  import matplotlib.pyplot as plt
18
22
  from scipy.optimize import minimize,curve_fit
19
23
  from scipy.stats import gumbel_r,genextreme
20
- from typing import Literal
21
- from tqdm import tqdm
22
- import logging
24
+ import numpy as np
25
+
26
+ # We have tried pymupdf but its license is AGPL so it's more or less a no-go.
27
+ import pdfplumber
23
28
 
24
29
  from .ins import Localities
25
30
  from .PyTranslate import _
@@ -749,6 +754,12 @@ class QDF_Belgium():
749
754
  return None
750
755
 
751
756
 
757
+ TRANSLATION_HEADER = {'année': 'year', 'janv.': 'January', 'févr.': 'February', 'mars': 'March',
758
+ 'avr.': 'April', 'mai': 'May', 'juin': 'June',
759
+ 'juil.': 'July', 'août': 'August', 'sept.': 'September',
760
+ 'oct.': 'October', 'nov.': 'November', 'déc.': 'December'}
761
+ RE_REFERENCE = re.compile(r"\([0-9]+\)")
762
+
752
763
  class Climate_IRM():
753
764
 
754
765
  def __init__(self, store_path= 'irm', ins:Literal['2018', '2019', '2025', 2018, 2019, 2025] = 2018) -> None:
@@ -761,7 +772,7 @@ class Climate_IRM():
761
772
  return self._climate_data[key]
762
773
 
763
774
  @classmethod
764
- def importfromwebsite(cls, store_path= 'irm', verbose:bool= False, waitingtime:float= .01, ins:Literal['2018', '2019', '2025', 2018, 2019, 2025] = 2018, ins_code: int = None):
775
+ def importfromwebsite(cls, store_path= 'irm', verbose:bool= False, waitingtime:float= .01, ins:Literal['2018', '2019', '2025', 2018, 2019, 2025] = 2018, ins_code: int = None, convert=False):
765
776
  """ Import Excel files for one or all municipalities from the IRM website
766
777
 
767
778
  :param store_path: Where to store the downloaded data. Directory will be created if it doesn't exists.
@@ -775,6 +786,7 @@ class Climate_IRM():
775
786
 
776
787
  :param ins: The year of the INS codes to use.
777
788
  :param code: Restricts the data download to a specific NIS code. `None` means full download.
789
+ :param convert: Converts the downloaded PDF to Excel files.
778
790
  """
779
791
  import requests
780
792
 
@@ -800,9 +812,14 @@ class Climate_IRM():
800
812
  if ins_code is not None and not path.exists(store_path):
801
813
  mkdir(store_path)
802
814
 
803
- file=open(path.join(store_path,str(myins)+".pdf"), 'wb')
815
+ pdf_file = path.join(store_path,str(myins)+".pdf")
816
+ file=open(pdf_file, 'wb')
804
817
  file.write(response.content)
805
818
  file.close()
819
+
820
+ if convert:
821
+ cls._convert_irm_file(pdf_file)
822
+
806
823
  if verbose:
807
824
  if callable(verbose):
808
825
  verbose(key/len(codes_to_load))
@@ -812,4 +829,51 @@ class Climate_IRM():
812
829
  #logging.error(response.content)
813
830
  logging.error(f"Failed to load IRM data: {url} --> {response}")
814
831
 
815
- sleep(waitingtime)
832
+ if len(codes_to_load) >= 2:
833
+ sleep(waitingtime)
834
+
835
+ @classmethod
836
+ def _scrap_table(cls, t):
837
+ """
838
+ Helper method to transform a table represented as a list of list to a
839
+ pandas DataFrame.
840
+ """
841
+
842
+ def fix_cid(strings: list[str]):
843
+ # The CID thing is a known issue:
844
+ # https://github.com/euske/pdfminer/issues/122
845
+ return [s.replace('(cid:176)C ', '°C').replace('¢', "'") for s in strings]
846
+
847
+ nt = []
848
+ row_headers = []
849
+ for rndx in range(1, len(t)):
850
+ # In the row header, we remove the "references" like "(1)".
851
+ row_headers.append( RE_REFERENCE.sub("", t[rndx][0]) )
852
+
853
+ # The PDFs use different "minus" glyph instead of an ASCII one,
854
+ # let's fix it.
855
+ nt.append( list(map(lambda s:float(s.replace("−","-")), t[rndx][1:])))
856
+
857
+ columns_headers = map(TRANSLATION_HEADER.get, t[0][1:])
858
+ df = pd.DataFrame(nt, columns=fix_cid(columns_headers), index=fix_cid(row_headers))
859
+ return df
860
+
861
+ @classmethod
862
+ def _convert_irm_file(cls, pdf_file: Union[str, Path]):
863
+ """
864
+ Scrap a PDF from IRM into two tables in a single Excel file with two
865
+ sheets.
866
+ """
867
+ pdf_file = Path(pdf_file)
868
+ with pdfplumber.open(pdf_file) as pdf:
869
+
870
+ # Rain data
871
+ df = cls._scrap_table(pdf.pages[1].extract_table())
872
+
873
+ # Sun data
874
+ df_sun = cls._scrap_table(pdf.pages[4].extract_table())
875
+
876
+ dest_file = pdf_file.with_suffix('.xlsx')
877
+ with pd.ExcelWriter(dest_file) as writer: # doctest: +SKIP
878
+ df.to_excel(writer, sheet_name='Rain')
879
+ df_sun.to_excel(writer, sheet_name='Sun')
File without changes