wolfhece 2.1.77__py3-none-any.whl → 2.1.78__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.
@@ -1,1637 +1,1597 @@
1
- """
2
- Author: University of Liege, HECE, LEMA
3
- Date: 2024
4
-
5
- Copyright (c) 2024 University of Liege. All rights reserved.
6
-
7
- This script and its content are protected by copyright law. Unauthorized
8
- copying or distribution of this file, via any medium, is strictly prohibited.
9
- """
10
-
11
- import geopandas as gpd
12
- import pandas as pd
13
- import numpy as np
14
- from osgeo import gdal, ogr, osr, gdalconst
15
- import os
16
- import glob
17
- from pathlib import Path
18
- import logging
19
- from tqdm import tqdm
20
- from pyogrio import list_layers, read_dataframe
21
- from enum import Enum
22
- import numba as nb
23
-
24
- ENGINE = 'pyogrio' # or 'Fiona -- Pyogrio is faster
25
- EXTENT = '.gpkg'
26
- class Modif_Type(Enum):
27
- """
28
- Enum class for the type of modification
29
- """
30
-
31
- WALOUS = 'Walous layers changed to PICC buidings'
32
- POINT2POLY_EPURATION = 'Change BDREF based on AJOUT_PDET sent by Perrine (SPI)'
33
- POINT2POLY_PICC = 'Convert the points to polygons based on PICC'
34
- POINT2POLY_CAPAPICC = 'Convert the points to polygons based on PICC and CaPa'
35
- INHABITED = 'Select only inhabited buildings'
36
- ROAD = 'Create a buffer around the roads'
37
- COPY = 'Copy the data'
38
-
39
- class Vulnerability_csv():
40
-
41
- def __init__(self, file:Path) -> None:
42
- self.file = file
43
- self.data = pd.read_csv(file, sep=",", encoding='latin-1')
44
-
45
- def get_layers(self) -> list:
46
- return [a[1] for a in self.data["Path"].str.split('/')]
47
-
48
- def get_vulnerability_level(self, layer:str) -> str:
49
- idx = self.get_layers().index(layer)
50
- return self.data.iloc[idx]["Vulne"]
51
-
52
- def get_vulnerability_code(self, layer:str) -> str:
53
- idx = self.get_layers().index(layer)
54
- return self.data.iloc[idx]["Code"]
55
-
56
-
57
- def get_data_type(fname:Path):
58
-
59
- fname = Path(fname)
60
- """ Get the data type of the input file from extension """
61
- if fname.name.endswith('.gpkg'):
62
- return 'GPKG'
63
- elif fname.name.endswith('.shp'):
64
- return 'ESRI Shapefile'
65
- elif fname.name.endswith('.gdb'):
66
- return 'OpenfileGDB'
67
- else:
68
- return None
69
-
70
- def cleaning_directory(dir:Path):
71
- """ Cleaning the directory """
72
-
73
- logging.info("Cleaning the directory {}".format(dir))
74
-
75
- files_in_output = list(dir.iterdir())
76
- for item in files_in_output:
77
- if item.is_file():
78
- os.remove(item)
79
-
80
- class Accept_Manager():
81
- """
82
- Structure to store the directories and names of the files.
83
-
84
- In the main directory, the following directories are mandatory/created:
85
- - INPUT : filled by the user - contains the input data
86
- - TEMP : created by the script - contains the temporary data for the study area
87
- - OUTPUT: created by the script - contains the output data for each scenario of the study area
88
-
89
- The INPUT directory contains the following subdirectories:
90
- - DATABASE: contains the data for the **entire Walloon region**
91
- - Cadastre_Walloon.gpkg: the Cadastre Walloon file
92
- - GT_Resilence_dataRisques202010.gdb: the original gdb file from SPW - GT Resilience
93
- - PICC-vDIFF.gdb: the PICC Walloon file
94
- - CE_IGN_TOP10V: the IGN top10v shapefile
95
- - EPU_STATIONS_NEW:
96
- - AJOUT_PDET_EPU_DG03_STATIONS.shp: the EPU stations shapefile
97
- - STUDY_AREA: contains the study area shapefiles - one for each study area - e.g. Bassin_Vesdre.shp
98
- - CSVs: contains the CSV files
99
- - Intermediate.csv: contains the matrices data for the acceptability computation
100
- # - Ponderation.csv: contains the ponderation data for the acceptability computation
101
- - Vulnerability.csv: contains the mapping between layers and vulnerability levels - a code value is also provided
102
- - WATER_DEPTH: contains the water depth data for each scenario
103
- - Study_area1:
104
- - Scenario1
105
- - Scenario2
106
- -...
107
- - ScenarioN
108
- - Study_area2:
109
- - Scenario1
110
- - Scenario2
111
- -...
112
- - ScenarioN
113
- -...
114
- - Study_areaN:
115
- - Scenario1
116
- - Scenario2
117
- -...
118
- - ScenarioN
119
-
120
- The TEMP directory contains the following subdirectories:
121
- - DATABASES: contains the temporary data each study area
122
- - Study_area1:
123
- - database.gpkg: the clipped database
124
- - CaPa.gpkg: the clipped Cadastre Walloon file
125
- - PICC.gpkg: the clipped PICC Walloon file
126
- - database_final.gpkg: the final database
127
- - database_final_V.gpkg: the final database with vulnerability levels
128
- - CE_IGN_TOP10V.tiff: the IGN top10v raster file
129
- - Maske_River_extent.tiff: the river extent raster file from IGN
130
- - VULNERABILITY: the vulnerability data
131
- - RASTERS:
132
- - Code : one file for each layer
133
- - Vulne : one file for each layer
134
- - Scenario1:
135
-
136
- """
137
-
138
- def __init__(self,
139
- main_dir:str = 'Data',
140
- Study_area:str = 'Bassin_Vesdre.shp',
141
- scenario = None,
142
- Original_gdb:str = 'GT_Resilence_dataRisques202010.gdb',
143
- CaPa_Walloon:str = 'Cadastre_Walloon.gpkg',
144
- PICC_Walloon:str = 'PICC_vDIFF.gdb',
145
- CE_IGN_top10v:str = 'CE_IGN_TOP10V/CE_IGN_TOP10V.shp',
146
- EPU_Stations:str = 'AJOUT_PDET_EPU_DG03_STATIONS.shp',
147
- Ponderation_csv:str = 'Ponderation.csv',
148
- Vuln_csv:str = 'Vulnerability.csv',
149
- Intermediate_csv:str = 'Intermediate.csv'
150
- ) -> None:
151
-
152
- self.old_dir:Path = Path(os.getcwd())
153
-
154
- self.main_dir:Path = Path(main_dir)
155
-
156
- # If it is a string, concatenate it with the current directory
157
- if not self.main_dir.is_absolute():
158
- self.main_dir = Path(os.getcwd()) / self.main_dir
159
-
160
- self._study_area = Study_area
161
- if Study_area is not None:
162
- if not self._study_area.endswith('.shp'):
163
- self._study_area += '.shp'
164
-
165
- self._scenario = scenario
166
- self._original_gdb = Original_gdb
167
- self._capa_walloon = CaPa_Walloon
168
- self._picc_walloon = PICC_Walloon
169
- self._ce_ign_top10v = CE_IGN_top10v
170
-
171
- self.IN_DIR = self.main_dir / "INPUT"
172
- self.IN_DATABASE = self.IN_DIR / "DATABASE"
173
- self.IN_STUDY_AREA = self.IN_DIR / "STUDY_AREA"
174
- self.IN_CSV = self.IN_DIR / "CSVs"
175
- self.IN_WATER_DEPTH = self.IN_DIR / "WATER_DEPTH"
176
- self.IN_EPU_STATIONS= self.IN_DIR / "EPU_STATIONS_NEW"
177
-
178
- self.ORIGINAL_GDB = self.IN_DATABASE / self._original_gdb
179
- self.CAPA_WALLOON = self.IN_DATABASE / self._capa_walloon
180
- self.PICC_WALLOON = self.IN_DATABASE / self._picc_walloon
181
- self.CE_IGN_TOP10V = self.IN_DATABASE / self._ce_ign_top10v
182
- self.EPU_STATIONS = self.IN_EPU_STATIONS / EPU_Stations
183
-
184
- self.VULNERABILITY_CSV = self.IN_CSV / Vuln_csv
185
- self.POINTS_CSV = self.IN_CSV / Intermediate_csv
186
- self.PONDERATION_CSV = self.IN_CSV / Ponderation_csv
187
-
188
- self._CSVs = [self.VULNERABILITY_CSV, self.POINTS_CSV]
189
- self._GPKGs= [self.CAPA_WALLOON, self.PICC_WALLOON]
190
- self._GDBs = [self.ORIGINAL_GDB]
191
- self._SHPs = [self.CE_IGN_TOP10V, self.EPU_STATIONS]
192
- self._ALLS = self._CSVs + self._GPKGs + self._GDBs + self._SHPs
193
-
194
- self.TMP_DIR = self.main_dir / "TEMP"
195
-
196
- self.OUT_DIR = self.main_dir / "OUTPUT"
197
-
198
- self.points2polys = []
199
- self.lines2polys = []
200
-
201
- self.create_paths()
202
- self.create_paths_scenario()
203
-
204
- def create_paths(self):
205
- """ Create the paths for the directories and files """
206
-
207
- self.points2polys = []
208
- self.lines2polys = []
209
-
210
- if self._study_area is not None:
211
-
212
- self.Study_area:Path = Path(self._study_area)
213
-
214
- self.TMP_STUDYAREA = self.TMP_DIR / self.Study_area.stem
215
- self.TMP_DATABASE = self.TMP_STUDYAREA / "DATABASES"
216
-
217
- self.TMP_CLIPGDB = self.TMP_DATABASE / "CLIP_GDB"
218
- self.TMP_CADASTER = self.TMP_DATABASE / "CLIP_CADASTER"
219
- self.TMP_PICC = self.TMP_DATABASE / "CLIP_PICC"
220
- self.TMP_IGNCE = self.TMP_DATABASE / "CLIP_IGN_CE"
221
- self.TMP_WMODIF = self.TMP_DATABASE / "WITH_MODIF"
222
- self.TMP_CODEVULNE = self.TMP_DATABASE / "CODE_VULNE"
223
-
224
- self.TMP_VULN_DIR = self.TMP_STUDYAREA / "VULNERABILITY"
225
- self.TMP_RASTERS = self.TMP_VULN_DIR / "RASTERS"
226
- self.TMP_RASTERS_CODE = self.TMP_RASTERS / "Code"
227
- self.TMP_RASTERS_VULNE = self.TMP_RASTERS / "Vulne"
228
-
229
- self.OUT_STUDY_AREA = self.OUT_DIR / self.Study_area.stem
230
-
231
- self.SA = self.IN_STUDY_AREA / self.Study_area
232
-
233
- # self.SA_DATABASE = self.TMP_STUDYAREA / "database.gpkg"
234
- # self.SA_CAPA = self.TMP_STUDYAREA / "CaPa.gpkg"
235
- # self.SA_PICC = self.TMP_STUDYAREA / "PICC.gpkg"
236
- self.SA_FINAL = self.TMP_STUDYAREA / "database_final.gpkg"
237
- self.SA_FINAL_V = self.TMP_STUDYAREA / "database_final_V.gpkg"
238
- self.SA_MASKED_RIVER = self.TMP_IGNCE / "CE_IGN_TOP10V.tiff"
239
-
240
- self.SA_VULN = self.TMP_VULN_DIR / "Vulnerability.tiff"
241
- self.SA_CODE = self.TMP_VULN_DIR / "Vulnerability_Code.tiff"
242
-
243
- else:
244
- self.Study_area = None
245
- self._scenario = None
246
-
247
- self.TMP_STUDYAREA = None
248
- self.TMP_DATABASE = None
249
- self.TMP_CADASTER = None
250
- self.TMP_PICC = None
251
- self.TMP_IGNCE = None
252
- self.TMP_WMODIF = None
253
- self.TMP_CODEVULNE = None
254
- self.TMP_VULN_DIR = None
255
- self.TMP_RASTERS = None
256
- self.TMP_RASTERS_CODE = None
257
- self.TMP_RASTERS_VULNE = None
258
-
259
- self.OUT_STUDY_AREA = None
260
-
261
- self.SA = None
262
- self.SA_DATABASE = None
263
- self.SA_CAPA = None
264
- self.SA_PICC = None
265
- self.SA_FINAL = None
266
- self.SA_FINAL_V = None
267
- self.SA_MASKED_RIVER = None
268
-
269
- self.SA_VULN = None
270
- self.SA_CODE = None
271
-
272
- self.create_paths_scenario()
273
-
274
- self.check_inputs()
275
- self.check_temporary()
276
- self.check_outputs()
277
-
278
- def create_paths_scenario(self):
279
-
280
- if self._scenario is not None:
281
-
282
- self.scenario:str = str(self._scenario)
283
-
284
- self.IN_SCEN_DIR = self.IN_WATER_DEPTH / self.SA.stem / self.scenario
285
- self.IN_RM_BUILD_DIR = self.IN_SCEN_DIR / "REMOVED_BUILDINGS"
286
-
287
- self.TMP_SCEN_DIR = self.TMP_VULN_DIR / self.scenario
288
- self.TMP_RM_BUILD_DIR = self.TMP_SCEN_DIR / "REMOVED_BUILDINGS"
289
- self.TMP_QFILES = self.TMP_SCEN_DIR / "Q_FILES"
290
-
291
- self.TMP_VULN = self.TMP_SCEN_DIR / "Vulnerability.tiff"
292
- self.TMP_CODE = self.TMP_SCEN_DIR / "Vulnerability_Code.tiff"
293
-
294
- self.OUT_SCEN_DIR = self.OUT_STUDY_AREA / self.scenario
295
- self.OUT_VULN = self.OUT_SCEN_DIR / "Vulnerability.tiff"
296
- self.OUT_CODE = self.OUT_SCEN_DIR / "Vulnerability_Code.tiff"
297
- self.OUT_MASKED_RIVER = self.OUT_SCEN_DIR / "Masked_River_extent.tiff"
298
- self.OUT_ACCEPT = self.OUT_SCEN_DIR / "Acceptability.tiff"
299
- self.OUT_ACCEPT_100M = self.OUT_SCEN_DIR / "Acceptability_100m.tiff"
300
-
301
- else:
302
- self.scenario = None
303
-
304
- self.IN_SCEN_DIR = None
305
- self.IN_RM_BUILD_DIR = None
306
-
307
- self.TMP_SCEN_DIR = None
308
- self.TMP_RM_BUILD_DIR = None
309
- self.TMP_QFILES = None
310
-
311
- self.TMP_VULN = None
312
- self.TMP_CODE = None
313
-
314
- self.OUT_SCEN_DIR = None
315
- self.OUT_VULN = None
316
- self.OUT_CODE = None
317
- self.OUT_MASKED_RIVER = None
318
- self.OUT_ACCEPT = None
319
- self.OUT_ACCEPT_100M = None
320
-
321
- @property
322
- def is_valid_inputs(self) -> bool:
323
- return self.check_inputs()
324
-
325
- @property
326
- def is_valid_study_area(self) -> bool:
327
- return self.SA.exists()
328
-
329
- @property
330
- def is_valid_vulnerability_csv(self) -> bool:
331
- return self.VULNERABILITY_CSV.exists()
332
-
333
- @property
334
- def is_valid_points_csv(self) -> bool:
335
- return self.POINTS_CSV.exists()
336
-
337
- @property
338
- def is_valid_ponderation_csv(self) -> bool:
339
- return self.PONDERATION_CSV.exists()
340
-
341
- def check_files(self) -> str:
342
- """ Check the files in the directories """
343
-
344
- files = ""
345
- for a in self._ALLS:
346
- if not a.exists():
347
- files += str(a) + "\n"
348
-
349
- return files
350
-
351
- def change_studyarea(self, Study_area:str = None) -> None:
352
-
353
- if Study_area is None:
354
- self._study_area = None
355
- self._scenario = None
356
- else:
357
- if Study_area in self.get_list_studyareas(with_suffix=True):
358
- self._study_area = Path(Study_area)
359
- else:
360
- logging.error("The study area does not exist in the study area directory")
361
-
362
- self.create_paths()
363
-
364
- def change_scenario(self, scenario:str) -> None:
365
-
366
- if scenario in self.get_list_scenarios():
367
- self._scenario = scenario
368
- self.create_paths_scenario()
369
- self.check_temporary()
370
- self.check_outputs()
371
- else:
372
- logging.error("The scenario does not exist in the water depth directory")
373
-
374
- def get_files_in_rm_buildings(self) -> list[Path]:
375
- return [Path(a) for a in glob.glob(str(self.IN_RM_BUILD_DIR / ("*"+ EXTENT)))]
376
-
377
- def get_files_in_rasters_vulne(self) -> list[Path]:
378
- return [Path(a) for a in glob.glob(str(self.TMP_RASTERS_VULNE / "*.tiff"))]
379
-
380
- def get_layers_in_gdb(self) -> list[str]:
381
- return [a[0] for a in list_layers(str(self.ORIGINAL_GDB))]
382
-
383
- def get_layer_types_in_gdb(self) -> list[str]:
384
- return [a[1] for a in list_layers(str(self.ORIGINAL_GDB))]
385
-
386
- def get_layers_in_clipgdb(self) -> list[str]:
387
- return [Path(a).stem for a in glob.glob(str(self.TMP_CLIPGDB / ("*"+ EXTENT)))]
388
-
389
- def get_layers_in_wmodif(self) -> list[str]:
390
- return [Path(a).stem for a in glob.glob(str(self.TMP_WMODIF / ("*"+ EXTENT)))]
391
-
392
- def get_layers_in_codevulne(self) -> list[str]:
393
- return [Path(a).stem for a in glob.glob(str(self.TMP_CODEVULNE / ("*"+ EXTENT)))]
394
-
395
- def get_files_in_rasters_code(self) -> list[Path]:
396
- return [Path(a) for a in glob.glob(str(self.TMP_RASTERS_CODE / "*.tiff"))]
397
-
398
- def get_q_files(self) -> list[Path]:
399
- return [Path(a) for a in glob.glob(str(self.TMP_QFILES / "*.tif"))]
400
-
401
- def get_list_scenarios(self) -> list[str]:
402
- return [Path(a).stem for a in glob.glob(str(self.IN_WATER_DEPTH / self.SA.stem / "Scenario*"))]
403
-
404
- def get_list_studyareas(self, with_suffix:bool = False) -> list[str]:
405
-
406
- if with_suffix:
407
- return [Path(a).name for a in glob.glob(str(self.IN_STUDY_AREA / "*.shp"))]
408
- else:
409
- return [Path(a).stem for a in glob.glob(str(self.IN_STUDY_AREA / "*.shp"))]
410
-
411
- def get_sims_files_for_scenario(self) -> list[Path]:
412
-
413
- return [Path(a) for a in glob.glob(str(self.IN_SCEN_DIR / "*.tif"))]
414
-
415
- def get_sim_file_for_return_period(self, return_period:int) -> Path:
416
-
417
- sims = self.get_sims_files_for_scenario()
418
-
419
- if len(sims)==0:
420
- logging.error("No simulations found")
421
- return None
422
-
423
- if "_h.tif" in sims[0].name:
424
- for cursim in sims:
425
- if cursim.stem.find("_T{}_".format(return_period)) != -1:
426
- return cursim
427
- else:
428
- for cursim in sims:
429
- if cursim.stem.find("T{}".format(return_period)) != -1:
430
- return cursim
431
-
432
- return None
433
-
434
- def get_types_in_file(self, file:str) -> list[str]:
435
- """ Get the types of the geometries in the Shape file """
436
-
437
- return [a[1] for a in list_layers(str(file))]
438
-
439
- def is_type_unique(self, file:str) -> bool:
440
- """ Check if the file contains only one type of geometry """
441
-
442
- types = self.get_types_in_file(file)
443
- return len(types) == 1
444
-
445
- def is_polygons(self, set2test:set) -> bool:
446
- """ Check if the set contains only polygons """
447
-
448
- set2test = list(set2test)
449
- firstone = set2test[0]
450
- if 'Polygon' in firstone:
451
- for curtype in set2test:
452
- if 'Polygon' not in curtype:
453
- return False
454
- return True
455
- else:
456
- return False
457
-
458
- def is_same_types(self, file:str) -> tuple[bool, str]:
459
- """ Check if the file contains only the same type of geometry """
460
-
461
- types = self.get_types_in_file(file)
462
-
463
- if len(types) == 1:
464
- if 'Point' in types[0]:
465
- return True, 'Point'
466
- elif 'Polygon' in types[0]:
467
- return True, 'Polygon'
468
- elif 'LineString' in types[0]:
469
- return True, 'LineString'
470
- else:
471
- raise ValueError(f"The type of geometry {types[0]} is not recognized")
472
- else:
473
- firstone = types[0]
474
- if 'Point' in firstone:
475
- for curtype in types:
476
- if 'Point' not in curtype:
477
- return False, None
478
- return True, 'Point'
479
-
480
- elif 'Polygon' in firstone:
481
- for curtype in types:
482
- if 'Polygon' not in curtype:
483
- return False, None
484
-
485
- return True, 'Polygon'
486
-
487
- elif 'LineString' in firstone:
488
- for curtype in types:
489
- if 'LineString' not in curtype:
490
- return False, None
491
-
492
- return True, 'LineString'
493
- else:
494
- raise ValueError(f"The type of geometry {firstone} is not recognized")
495
-
496
-
497
- def get_return_periods(self) -> list[int]:
498
- """
499
- Get the return periods from the simulations
500
-
501
- :return list[int]: the **sorted list** of return periods
502
- """
503
-
504
- # List files in directory
505
- sims = self.get_sims_files_for_scenario()
506
-
507
- if len(sims)==0:
508
- logging.error("No simulations found")
509
- return None
510
-
511
- # Two cases:
512
- # - Return periods are named as T2.tif, T5.tif, T10.tif, ...
513
- # - Return periods are named as *_T2_h.tif, *_T5_h.tif, *_T10_h.tif, ...
514
- if "_h.tif" in sims[0].name:
515
-
516
- # Searching for the position of the return period in the name
517
- idx_T = [cursim.name.find("_T") for cursim in sims]
518
- idx_h = [cursim.name.find("_h.tif") for cursim in sims]
519
-
520
- assert len(idx_T) == len(idx_h), "The number of T and h are not the same"
521
- for curT, curh in zip(idx_T, idx_h):
522
- assert curT != -1, "The T is not found"
523
- assert curh != -1, "The h is not found"
524
- assert curh > curT, "The h is before the T"
525
-
526
- # Create the list of return periods -- only the numeric part
527
- sims = [int(cursim.name[idx_T[i]+2:idx_h[i]]) for i, cursim in enumerate(sims)]
528
- else:
529
- # searching for the position of the return period in the name
530
- idx_T = [cursim.name.find("T") for cursim in sims]
531
- idx_h = [cursim.name.find(".tif") for cursim in sims]
532
-
533
- assert len(idx_T) == len(idx_h), "The number of T and h are not the same"
534
- for curT, curh in zip(idx_T, idx_h):
535
- assert curT != -1, "The T is not found"
536
- assert curh != -1, "The h is not found"
537
- assert curh > curT, "The h is before the T"
538
-
539
- # create the list of return periods -- only the numeric part
540
- sims = [int(cursim.name[idx_T[i]+1:idx_h[i]]) for i, cursim in enumerate(sims)]
541
-
542
- return sorted(sims)
543
-
544
- def get_ponderations(self) -> pd.DataFrame:
545
- """ Get the ponderation data from available simulations """
546
-
547
- rt = self.get_return_periods()
548
-
549
- if len(rt)==0:
550
- logging.error("No simulations found")
551
- return None
552
-
553
- pond = []
554
-
555
- pond.append(1./float(rt[0]) + (1./float(rt[0]) - 1./float(rt[1]))/2.)
556
- for i in range(1, len(rt)-1):
557
- # Full formula
558
- # pond.append((1./float(rt[i-1]) - 1./float(rt[i]))/2. + (1./float(rt[i]) - 1./float(rt[i+1]))/2.)
559
-
560
- # More compact formula
561
- pond.append((1./float(rt[i-1]) - 1./float(rt[i+1]))/2.)
562
-
563
- pond.append(1./float(rt[-1]) + (1./float(rt[-2]) - 1./float(rt[-1]))/2.)
564
-
565
- return pd.DataFrame(pond, columns=["Ponderation"], index=rt)
566
-
567
- def get_filepath_for_return_period(self, return_period:int) -> Path:
568
-
569
- return self.get_sim_file_for_return_period(return_period)
570
-
571
- def change_dir(self) -> None:
572
- os.chdir(self.main_dir)
573
- logging.info("Current directory: %s", os.getcwd())
574
-
575
- def restore_dir(self) -> None:
576
- os.chdir(self.old_dir)
577
- logging.info("Current directory: %s", os.getcwd())
578
-
579
- def check_inputs(self) -> bool:
580
- """
581
- Check if the input directories exist.
582
-
583
- Inputs can not be created automatically. The user must provide them.
584
-
585
- """
586
-
587
- err = False
588
- if not self.IN_DATABASE.exists():
589
- logging.error("INPUT : The database directory does not exist")
590
- err = True
591
-
592
- if not self.IN_STUDY_AREA.exists():
593
- logging.error("INPUT : The study area directory does not exist")
594
- err = True
595
-
596
- if not self.IN_CSV.exists():
597
- logging.error("INPUT : The CSV directory does not exist")
598
- err = True
599
-
600
- if not self.IN_WATER_DEPTH.exists():
601
- logging.error("INPUT : The water depth directory does not exist")
602
- err = True
603
-
604
- if not self.IN_EPU_STATIONS.exists():
605
- logging.error("INPUT : The EPU stations directory does not exist")
606
- err = True
607
-
608
- if self.Study_area is not None:
609
- if not self.SA.exists():
610
- logging.error("INPUT : The study area file does not exist")
611
- err = True
612
-
613
- if not self.ORIGINAL_GDB.exists():
614
- logging.error("INPUT : The original gdb file does not exist - Please pull it from the SPW-ARNE")
615
- err = True
616
-
617
- if not self.CAPA_WALLOON.exists():
618
- logging.error("INPUT : The Cadastre Walloon file does not exist - Please pull it from the SPW")
619
- err = True
620
-
621
- if not self.PICC_WALLOON.exists():
622
- logging.error("INPUT : The PICC Walloon file does not exist - Please pull it from the SPW website")
623
- err = True
624
-
625
- if not self.CE_IGN_TOP10V.exists():
626
- logging.error("INPUT : The CE IGN top10v file does not exist - Please pull it from the IGN")
627
- err = True
628
-
629
- if self.scenario is None:
630
- logging.debug("The scenario has not been defined")
631
- else:
632
- if not self.IN_SCEN_DIR.exists():
633
- logging.error("The scenario directory does not exist")
634
- err = True
635
-
636
- return not err
637
-
638
- def check_temporary(self) -> bool:
639
- """
640
- Check if the temporary directories exist.
641
-
642
- If not, create them.
643
- """
644
-
645
- self.TMP_DIR.mkdir(parents=True, exist_ok=True)
646
-
647
- if self.Study_area is not None:
648
- self.TMP_STUDYAREA.mkdir(parents=True, exist_ok=True)
649
- self.TMP_DATABASE.mkdir(parents=True, exist_ok=True)
650
- self.TMP_CLIPGDB.mkdir(parents=True, exist_ok=True)
651
- self.TMP_CADASTER.mkdir(parents=True, exist_ok=True)
652
- self.TMP_WMODIF.mkdir(parents=True, exist_ok=True)
653
- self.TMP_CODEVULNE.mkdir(parents=True, exist_ok=True)
654
- self.TMP_PICC.mkdir(parents=True, exist_ok=True)
655
- self.TMP_IGNCE.mkdir(parents=True, exist_ok=True)
656
- self.TMP_VULN_DIR.mkdir(parents=True, exist_ok=True)
657
- self.TMP_RASTERS.mkdir(parents=True, exist_ok=True)
658
- self.TMP_RASTERS_CODE.mkdir(parents=True, exist_ok=True)
659
- self.TMP_RASTERS_VULNE.mkdir(parents=True, exist_ok=True)
660
-
661
- if self.scenario is not None:
662
- self.TMP_SCEN_DIR.mkdir(parents=True, exist_ok=True)
663
- self.TMP_RM_BUILD_DIR.mkdir(parents=True, exist_ok=True)
664
- self.TMP_QFILES.mkdir(parents=True, exist_ok=True)
665
-
666
- return True
667
-
668
- def check_outputs(self) -> bool:
669
- """
670
- Check if the output directories exist.
671
-
672
- If not, create them.
673
- """
674
-
675
- self.OUT_DIR.mkdir(parents=True, exist_ok=True)
676
-
677
- if self.Study_area is not None:
678
- self.OUT_STUDY_AREA.mkdir(parents=True, exist_ok=True)
679
-
680
- if self.scenario is not None:
681
- self.OUT_SCEN_DIR.mkdir(parents=True, exist_ok=True)
682
-
683
- return True
684
-
685
- def check_database_creation(self) -> bool:
686
- """
687
- Check if the database files have been created.
688
- """
689
-
690
- if not self.SA_DATABASE.exists():
691
- logging.error("The database file does not exist")
692
- return False
693
-
694
- if not self.SA_CAPA.exists():
695
- logging.error("The Cadastre Walloon file does not exist")
696
- return False
697
-
698
- if not self.SA_PICC.exists():
699
- logging.error("The PICC Walloon file does not exist")
700
- return False
701
-
702
- if not self.SA_FINAL.exists():
703
- logging.error("The final database file does not exist")
704
- return False
705
-
706
- if not self.SA_FINAL_V.exists():
707
- logging.error("The final database with vulnerability levels does not exist")
708
- return False
709
-
710
- return True
711
-
712
- def check_before_database_creation(self) -> bool:
713
- """ Check if the necessary files are present before the database creation"""
714
-
715
- if not self.is_valid_inputs:
716
- logging.error("Theere are missing input directories - Please check carefully the input directories and the logs")
717
- return False
718
-
719
- if not self.is_valid_study_area:
720
- logging.error("The study area file does not exist - Please create it")
721
- return False
722
-
723
- if not self.is_valid_vulnerability_csv:
724
- logging.error("The vulnerability CSV file does not exist - Please create it")
725
- return False
726
-
727
- return True
728
-
729
- def check_before_rasterize(self) -> bool:
730
-
731
- if not self.TMP_CODEVULNE.exists():
732
- logging.error("The final database with vulnerability levels does not exist")
733
- return False
734
-
735
- if not self.TMP_WMODIF.exists():
736
- logging.error("The vector data with modifications does not exist")
737
- return False
738
-
739
- return True
740
-
741
- def check_before_vulnerability(self) -> bool:
742
-
743
- if not self.SA.exists():
744
- logging.error("The area of interest does not exist")
745
- return False
746
-
747
- if not self.IN_WATER_DEPTH.exists():
748
- logging.error("The water depth directory does not exist")
749
- return False
750
-
751
- if not self.IN_SCEN_DIR.exists():
752
- logging.error("The scenario directory does not exist in the water depth directory")
753
- return False
754
-
755
- if not self.SA_MASKED_RIVER.exists():
756
- logging.error("The IGN raster does not exist")
757
- return False
758
-
759
- return True
760
-
761
- def check_vuln_code_sa(self) -> bool:
762
-
763
- if not self.SA_VULN.exists():
764
- logging.error("The vulnerability raster file does not exist")
765
- return False
766
-
767
- if not self.SA_CODE.exists():
768
- logging.error("The vulnerability code raster file does not exist")
769
- return False
770
-
771
- return True
772
-
773
- def check_vuln_code_scenario(self) -> bool:
774
-
775
- if not self.TMP_VULN.exists():
776
- logging.error("The vulnerability raster file does not exist")
777
- return False
778
-
779
- if not self.TMP_CODE.exists():
780
- logging.error("The vulnerability code raster file does not exist")
781
- return False
782
-
783
- return True
784
-
785
- def compare_original_clipped_layers(self) -> str:
786
- """ Compare the original layers with the clipped ones """
787
-
788
- layers = self.get_layers_in_gdb()
789
- layers_clip = self.get_layers_in_clipgdb()
790
-
791
- ret = 'These layers have not been clipped:\n'
792
- for layer in layers:
793
- if layer not in layers_clip:
794
- ret += " - {}\n".format(layer)
795
-
796
- ret += '\nThese layers have been clipped but are not present in the GDB:\n'
797
- for layer in layers_clip:
798
- if layer not in layers:
799
- ret += " - {}\n".format(layer)
800
-
801
- ret+='\n'
802
-
803
- return ret
804
-
805
- def compare_clipped_raster_layers(self) -> str:
806
- """ Compare the clipped layers with the rasterized ones """
807
-
808
- layers = self.get_layers_in_clipgdb()
809
- layers_rast = self.get_layers_in_codevulne()
810
-
811
- ret = 'These layers {} have not been rasterized:\n'
812
- for layer in layers:
813
- if layer not in layers_rast:
814
- ret += " - {}\n".format(layer)
815
-
816
- ret += '\nThese layers have been rasterized but are not in the orginal GDB:\n'
817
- for layer in layers_rast:
818
- if layer not in layers:
819
- ret += " - {}\n".format(layer)
820
-
821
- ret+='\n'
822
-
823
- return ret
824
-
825
- def get_operand(self, file:str) -> Modif_Type:
826
- """ Get the operand based on the layer name """
827
- LAYERS_WALOUS = ["WALOUS_2018_LB72_112",
828
- "WALOUS_2018_LB72_31",
829
- "WALOUS_2018_LB72_32",
830
- "WALOUS_2018_LB72_331",
831
- "WALOUS_2018_LB72_332",
832
- "WALOUS_2018_LB72_333",
833
- "WALOUS_2018_LB72_34"]
834
-
835
- ret, curtype = self.is_same_types(file)
836
- layer = Path(file).stem
837
-
838
- if not ret:
839
- raise ValueError("The layer contains different types of geometries")
840
-
841
- if layer in LAYERS_WALOUS:
842
- return Modif_Type.WALOUS
843
-
844
- elif curtype=="Point":
845
-
846
- self.points2polys.append(layer)
847
-
848
- if layer =="BDREF_DGO3_PASH__SCHEMA_STATIONS_EPU":
849
- return Modif_Type.POINT2POLY_EPURATION
850
- elif layer =="INFRASIG_SOINS_SANTE__ETAB_AINES":
851
- return Modif_Type.POINT2POLY_PICC
852
- else:
853
- return Modif_Type.POINT2POLY_CAPAPICC
854
-
855
- elif layer =="Hab_2018_CABU":
856
- return Modif_Type.INHABITED
857
-
858
- elif layer =="INFRASIG_ROUTE_RES_ROUTIER_TE_AXES":
859
-
860
- self.lines2polys.append(layer)
861
-
862
- return Modif_Type.ROAD
863
-
864
- else:
865
- return Modif_Type.COPY
866
-
867
- def check_origin_shape(self) -> list[str]:
868
-
869
- code = self.get_files_in_rasters_code()
870
- vuln = self.get_files_in_rasters_vulne()
871
-
872
- if len(code) == 0:
873
- logging.error("The code rasters do not exist")
874
- return False
875
-
876
- if len(vuln) == 0:
877
- logging.error("The vulnerability rasters do not exist")
878
- return False
879
-
880
- if len(code) != len(vuln):
881
- logging.error("The number of code and vulnerability rasters do not match")
882
- return False
883
-
884
- # we take a reference raster
885
- ref = gdal.Open(str(code[0]))
886
- band_ref = ref.GetRasterBand(1)
887
- proj_ref = ref.GetProjection()
888
- geo_ref = ref.GetGeoTransform()
889
- col_ref, row_ref = band_ref.XSize, band_ref.YSize
890
-
891
- # we compare the reference raster with the others
892
- diff = []
893
- for cur in code + vuln + [self.SA_MASKED_RIVER]:
894
- cur_ = gdal.Open(str(cur))
895
- band_cur = cur_.GetRasterBand(1)
896
- proj_cur = cur_.GetProjection()
897
- geo_cur = cur_.GetGeoTransform()
898
- col_cur, row_cur = band_cur.XSize, band_cur.YSize
899
-
900
- if geo_ref != geo_cur:
901
- logging.error("The geotransforms do not match {}".format(cur))
902
- diff.append(cur)
903
-
904
- if proj_ref != proj_cur:
905
- logging.error("The projections do not match {}".format(cur))
906
- diff.append(cur)
907
-
908
- if col_ref != col_cur or row_ref != row_cur:
909
- logging.error("The dimensions do not match {}".format(cur))
910
- diff.append(cur)
911
-
912
- return diff
913
-
914
-
915
- def clip_layer(layer:str,
916
- file_path:str,
917
- Study_Area:str,
918
- output_dir:str):
919
- """
920
- Clip the input data based on the selected bassin and saves it
921
- in separate shape files.
922
-
923
- As shape file doen not support DateTime, the columns with DateTime
924
- are converted to string.
925
-
926
- :param layer: the layer name in the GDB file
927
- :param file_path: the path to the GDB file
928
- :param Study_Area: the path to the study area shapefile
929
- :param output_dir: the path to the output directory
930
- """
931
-
932
- layer = str(layer)
933
- file_path = str(file_path)
934
- Study_Area = str(Study_Area)
935
- output_dir = Path(output_dir)
936
-
937
- St_Area = gpd.read_file(Study_Area, engine=ENGINE)
938
-
939
- logging.info(layer)
940
-
941
- # The data is clipped during the reading
942
- # **It is more efficient than reading the entire data and then clipping it**
943
- #
944
- # FIXME: "read_dataframe" is used directly rather than "gpd.read_file" cause
945
- # the "layer" parameter is well transmitted to the "read_dataframe" function...
946
- df:gpd.GeoDataFrame = read_dataframe(file_path, layer=layer, mask=St_Area['geometry'][0])
947
-
948
- if len(df) == 0:
949
- logging.warning("No data found for layer " + str(layer))
950
- return "No data found for layer " + str(layer)
951
-
952
- # Force Lambert72 -> EPSG:31370
953
- df.to_crs("EPSG:31370", inplace=True)
954
- try:
955
- date_columns = df.select_dtypes(include=['datetimetz']).columns.tolist()
956
- if len(date_columns)>0:
957
- df[date_columns] = df[date_columns].astype(str)
958
-
959
- df.to_file(str(output_dir / (layer+EXTENT)), mode='w', engine=ENGINE)
960
- except Exception as e:
961
- logging.error("Error while saving the clipped " + str(layer) + " to file")
962
- logging.error(e)
963
- pass
964
-
965
- logging.info("Saved the clipped " + str(layer) + " to file")
966
- return "Saved the clipped " +str(layer)+ " to file"
967
-
968
-
969
- def data_modification(layer:str,
970
- manager:Accept_Manager,
971
- picc:gpd.GeoDataFrame,
972
- capa:gpd.GeoDataFrame ):
973
- """
974
- Apply the data modifications as described in the LEMA report
975
-
976
- FIXME : Add more doc in this docstring
977
-
978
- :param input_database: the path to the input database
979
- :param layer: the layer name in the database
980
- :param output_database: the path to the output database
981
- :param picc: the PICC Walloon file -- Preloaded
982
- :param capa: the Cadastre Walloon file -- Preloaded
983
- """
984
-
985
- df1:gpd.GeoDataFrame
986
- df2:gpd.GeoDataFrame
987
-
988
- layer = str(layer)
989
-
990
- dir_input = manager.TMP_CLIPGDB
991
- dir_output = manager.TMP_WMODIF
992
-
993
- input_file = str(dir_input / (layer + EXTENT))
994
- output_file = str(dir_output / (layer + EXTENT))
995
-
996
- # Read the data
997
- df:gpd.GeoDataFrame = gpd.read_file(input_file, engine=ENGINE)
998
- nblines, _ = df.shape
999
-
1000
- if nblines>0:
1001
- op = manager.get_operand(input_file)
1002
-
1003
- if op == Modif_Type.WALOUS:
1004
- # Walous layers changed to PICC buidings
1005
-
1006
- assert picc.crs == df.crs, "CRS of PICC and input data do not match"
1007
-
1008
- assert "GEOREF_ID" in picc.columns, "The PICC file does not contain the GEOREF_ID column"
1009
- assert "NATUR_CODE" in picc.columns, "The PICC file does not contain the NATUR_CODE column"
1010
-
1011
- df1 = gpd.sjoin(picc, df, how="inner", predicate="intersects" )
1012
- cols = df.columns
1013
-
1014
- cols = np.append(cols, "GEOREF_ID")
1015
- cols = np.append(cols, "NATUR_CODE")
1016
-
1017
- df1 = df1[cols]
1018
-
1019
- if df1.shape[0] > 0:
1020
- assert manager.is_polygons(set(df1.geom_type)), f"The layer does not contains polygons - {op}"
1021
- df1.to_file(output_file, engine=ENGINE)
1022
- else:
1023
- logging.warning("No data found for layer " + str(layer))
1024
-
1025
- elif op == Modif_Type.POINT2POLY_EPURATION:
1026
- # Change BDREF based on AJOUT_PDET sent by Perrine (SPI)
1027
-
1028
- # The original layer is a point layer.
1029
- # The EPU_STATIONS shape file (from SPI) is a polygon layer.
1030
-
1031
- df1 = gpd.read_file(str(manager.EPU_STATIONS), engine=ENGINE)
1032
-
1033
- assert df1.crs == df.crs, "CRS of AJOUT_PDET and input data do not match"
1034
-
1035
- df2 = gpd.sjoin(picc, df1, how="inner", predicate="intersects" )
1036
-
1037
- if df2.shape[0] > 0:
1038
- assert manager.is_polygons(set(df2.geom_type)), f"The layer does not contains polygons - {op}"
1039
- df2.to_file(output_file, engine=ENGINE)
1040
- else:
1041
- logging.warning("No data found for layer " + str(layer))
1042
-
1043
- elif op == Modif_Type.POINT2POLY_PICC:
1044
- # Select the polygons that contains the points
1045
- # in theCadaster and PICC files
1046
-
1047
- assert capa.crs == df.crs, "CRS of CaPa and input data do not match"
1048
- assert "CaPaKey" in capa.columns, "The CaPa file does not contain the CaPaKey column"
1049
-
1050
- df1= gpd.sjoin(capa, df, how="inner", predicate="intersects" )
1051
- cols=df.columns
1052
-
1053
- cols = np.append(cols, "CaPaKey")
1054
- df1=df1[cols]
1055
- df2=gpd.sjoin(picc, df1, how="inner", predicate="intersects" )
1056
-
1057
- if df2.shape[0] > 0:
1058
- assert manager.is_polygons(set(df2.geom_type)), f"The layer does not contains polygons - {op}"
1059
- df2.to_file(output_file, engine=ENGINE)
1060
- else:
1061
- logging.warning("No data found for layer " + str(layer))
1062
-
1063
- elif op == Modif_Type.POINT2POLY_CAPAPICC:
1064
-
1065
- # Select the polygons that contains the points
1066
- # in theCadaster and PICC files
1067
-
1068
- assert capa.crs == df.crs, "CRS of CaPa and input data do not match"
1069
- assert picc.crs == df.crs, "CRS of PICC and input data do not match"
1070
-
1071
- # Join the Layer and CaPa DataFrames : https://geopandas.org/en/stable/docs/reference/api/geopandas.sjoin.html
1072
- # ‘inner’: use intersection of keys from both dfs; retain only left_df geometry column
1073
- # "intersects" : Binary predicate. Valid values are determined by the spatial index used.
1074
- df1= gpd.sjoin(capa, df, how="inner", predicate="intersects" )
1075
-
1076
- # Retain only the columns of the input data
1077
- cols = df.columns
1078
- # but add the CaPaKey
1079
- cols = np.append(cols, "CaPaKey")
1080
-
1081
- df1 = df1[cols]
1082
-
1083
- # Join the df1 and PICC DataFrames : https://geopandas.org/en/stable/docs/reference/api/geopandas.sjoin.html
1084
- df2 = gpd.sjoin(picc, df1, how="inner", predicate="intersects" )
1085
-
1086
- # Add only the GEOREF_ID and NATUR_CODE columns from PICC
1087
- cols = np.append(cols, "GEOREF_ID")
1088
- cols = np.append(cols, "NATUR_CODE")
1089
-
1090
- df2 = df2[cols]
1091
-
1092
- if df2.shape[0] > 0:
1093
- assert manager.is_polygons(set(df2.geom_type)), f"The layer does not contains polygons - {op}"
1094
- df2.to_file(output_file, engine=ENGINE)
1095
- else:
1096
- logging.warning("No data found for layer " + str(layer))
1097
-
1098
- elif op == Modif_Type.INHABITED:
1099
- # Select only the buildings with a number of inhabitants > 0
1100
- df1=df[df["NbsHabTOT"]>0]
1101
-
1102
- if df1.shape[0] > 0:
1103
- assert manager.is_polygons(set(df1.geom_type)), f"The layer does not contains polygons - {op}"
1104
- df1.to_file(output_file, engine=ENGINE)
1105
- else:
1106
- logging.warning("No data found for layer " + str(layer))
1107
-
1108
- elif op == Modif_Type.ROAD:
1109
- # Create a buffer around the roads
1110
- df1=df.buffer(distance=6, cap_style=2)
1111
-
1112
- if df1.shape[0] > 0:
1113
- assert set(df1.geom_type) == {'Polygon'}, f"The layer does not contains polygons - {op}"
1114
- df1.to_file(output_file, engine=ENGINE)
1115
- else:
1116
- logging.warning("No data found for layer " + str(layer))
1117
-
1118
- elif op == Modif_Type.COPY:
1119
- # just copy the data if it is polygons
1120
- if manager.is_polygons(set(df.geom_type)):
1121
- df.to_file(output_file, engine=ENGINE)
1122
- else:
1123
- logging.error("The layer does not contains polygons - " + str(layer))
1124
- else:
1125
- raise ValueError(f"The operand {op} is not recognized")
1126
-
1127
- return "Data modification done for " + str(layer)
1128
- else:
1129
- # Normally, phase 1 does not create empty files
1130
- # But it is better to check... ;-)
1131
- logging.error("skipped" + str(layer) + "due to no polygon in the study area")
1132
- return "skipped" + str(layer) + "due to no polygon in the study area"
1133
-
1134
- def compute_vulnerability(manager:Accept_Manager):
1135
- """
1136
- Compute the vulnerability for the Study Area
1137
-
1138
- This function **will not modify** the data by the removed buildings/scenarios.
1139
-
1140
- :param dirsnames: the Dirs_Names object from the calling function
1141
- """
1142
-
1143
- vuln_csv = Vulnerability_csv(manager.VULNERABILITY_CSV)
1144
-
1145
- rasters_vuln = manager.get_files_in_rasters_vulne()
1146
-
1147
- logging.info("Number of files",len(rasters_vuln))
1148
-
1149
- ds:gdal.Dataset = gdal.OpenEx(str(rasters_vuln[0]), gdal.GA_ReadOnly, open_options=["SPARSE_OK=TRUE"])
1150
-
1151
- tmp_vuln = ds.GetRasterBand(1)
1152
-
1153
- # REMARK: The XSize and YSize are the number of columns and rows
1154
- col, row = tmp_vuln.XSize, tmp_vuln.YSize
1155
-
1156
- logging.info("Computing Vulnerability")
1157
-
1158
- array_vuln = np.ones((row, col), dtype=np.int8)
1159
-
1160
- # Create a JIT function to update the arrays
1161
- # Faster than the classical Python loop or Numpy
1162
- @nb.jit(nopython=True, boundscheck=False, inline='always')
1163
- def update_arrays_jit(tmp_vuln, array_vuln):
1164
- for i in range(tmp_vuln.shape[0]):
1165
- for j in range(tmp_vuln.shape[1]):
1166
- if tmp_vuln[i, j] >= array_vuln[i, j]:
1167
- array_vuln[i, j] = tmp_vuln[i, j]
1168
-
1169
- return array_vuln
1170
-
1171
- @nb.jit(nopython=True, boundscheck=False, inline='always')
1172
- def update_arrays_jit_csr(row, col, locvuln, array_vuln):
1173
- for k in range(len(row)-1):
1174
- i = k
1175
- j1 = row[k]
1176
- j2 = row[k+1]
1177
- for j in col[j1:j2]:
1178
- if locvuln >= array_vuln[i, j]:
1179
- array_vuln[i, j] = locvuln
1180
-
1181
- return array_vuln
1182
-
1183
- for i in tqdm(range(len(rasters_vuln)), 'Computing Vulnerability : '):
1184
- logging.info("Computing layer {} / {}".format(i, len(rasters_vuln)))
1185
-
1186
- locvuln = vuln_csv.get_vulnerability_level(rasters_vuln[i].stem)
1187
-
1188
- if locvuln == 1:
1189
- logging.info("No need to apply the matrice, the vulnerability is 1 which is the lower value")
1190
- continue
1191
-
1192
- if rasters_vuln[i].with_suffix('.npz').exists():
1193
- ij_npz = np.load(rasters_vuln[i].with_suffix('.npz'))
1194
- ii = ij_npz['row']
1195
- jj = ij_npz['col']
1196
- # We use the jit
1197
- update_arrays_jit_csr(ii, jj, locvuln, array_vuln)
1198
-
1199
- else:
1200
- ds = gdal.OpenEx(str(rasters_vuln[i]), open_options=["SPARSE_OK=TRUE"])
1201
- tmp_vuln = ds.GetRasterBand(1).ReadAsArray()
1202
- # We use the jit
1203
- update_arrays_jit(tmp_vuln, array_vuln)
1204
-
1205
- logging.info("Saving the computed vulnerability")
1206
- dst_filename= str(manager.SA_VULN)
1207
- y_pixels, x_pixels = array_vuln.shape # number of pixels in x
1208
-
1209
- driver = gdal.GetDriverByName('GTiff')
1210
- dataset = driver.Create(dst_filename,
1211
- x_pixels, y_pixels,
1212
- gdal.GDT_Byte,
1213
- 1,
1214
- options=["COMPRESS=LZW"])
1215
-
1216
- dataset.GetRasterBand(1).WriteArray(array_vuln.astype(np.int8))
1217
- # follow code is adding GeoTranform and Projection
1218
- geotrans = ds.GetGeoTransform() # get GeoTranform from existed 'data0'
1219
- proj = ds.GetProjection() # you can get from a exsited tif or import
1220
- dataset.SetGeoTransform(geotrans)
1221
- dataset.SetProjection(proj)
1222
- dataset.FlushCache()
1223
- dataset = None
1224
-
1225
- logging.info("Computed Vulnerability for the Study Area - Done")
1226
-
1227
- def compute_code(manager:Accept_Manager):
1228
- """
1229
- Compute the code for the Study Area
1230
-
1231
- This function **will not modify** the data by the removed buildings/scenarios.
1232
-
1233
- :param dirsnames: the Dirs_Names object from the calling function
1234
- """
1235
-
1236
- vuln_csv = Vulnerability_csv(manager.VULNERABILITY_CSV)
1237
-
1238
- rasters_code = manager.get_files_in_rasters_code()
1239
-
1240
- logging.info("Number of files",len(rasters_code))
1241
-
1242
- ds:gdal.Dataset = gdal.OpenEx(str(rasters_code[0]), gdal.GA_ReadOnly, open_options=["SPARSE_OK=TRUE"])
1243
-
1244
- tmp_code = ds.GetRasterBand(1)
1245
-
1246
- # REMARK: The XSize and YSize are the number of columns and rows
1247
- col, row = tmp_code.XSize, tmp_code.YSize
1248
-
1249
- logging.info("Computing Code")
1250
-
1251
- array_code = np.ones((row, col), dtype=np.int8)
1252
-
1253
- # Create a JIT function to update the arrays
1254
- # Faster than the classical Python loop or Numpy
1255
- @nb.jit(nopython=True, boundscheck=False, inline='always')
1256
- def update_arrays_jit(tmp_code, loccode, array_code):
1257
- for i in range(tmp_code.shape[0]):
1258
- for j in range(tmp_code.shape[1]):
1259
- if tmp_code[i, j] >= array_code[i, j]:
1260
- array_code[i, j] = loccode
1261
-
1262
- return array_code
1263
-
1264
- @nb.jit(nopython=True, boundscheck=False, inline='always')
1265
- def update_arrays_jit_csr(row, col, loccode, array_code):
1266
- for k in range(len(row)-1):
1267
- i = k
1268
- j1 = row[k]
1269
- j2 = row[k+1]
1270
- for j in col[j1:j2]:
1271
- if loccode >= array_code[i, j]:
1272
- array_code[i, j] = loccode
1273
-
1274
- return array_code
1275
-
1276
- for i in tqdm(range(len(rasters_code)), 'Computing Code : '):
1277
- logging.info("Computing layer {} / {}".format(i, len(rasters_code)))
1278
-
1279
- loccode = vuln_csv.get_vulnerability_code(rasters_code[i].stem)
1280
-
1281
- if rasters_code[i].with_suffix('.npz').exists():
1282
- ij_npz = np.load(rasters_code[i].with_suffix('.npz'))
1283
- ii = ij_npz['row']
1284
- jj = ij_npz['col']
1285
- # We use the jit
1286
- update_arrays_jit_csr(ii, jj, loccode, array_code)
1287
-
1288
- else:
1289
- ds = gdal.OpenEx(str(rasters_code[i]), open_options=["SPARSE_OK=TRUE"])
1290
- tmp_code = ds.GetRasterBand(1).ReadAsArray()
1291
- # We use the jit
1292
- update_arrays_jit(tmp_code, loccode, array_code)
1293
-
1294
- logging.info("Saving the computed codes")
1295
- dst_filename= str(manager.SA_CODE)
1296
- y_pixels, x_pixels = array_code.shape # number of pixels in x
1297
- driver = gdal.GetDriverByName('GTiff')
1298
- dataset = driver.Create(dst_filename,
1299
- x_pixels, y_pixels,
1300
- gdal.GDT_Byte,
1301
- 1,
1302
- options=["COMPRESS=LZW"])
1303
-
1304
- dataset.GetRasterBand(1).WriteArray(array_code.astype(np.int8))
1305
- # follow code is adding GeoTranform and Projection
1306
- geotrans = ds.GetGeoTransform() # get GeoTranform from existed 'data0'
1307
- proj = ds.GetProjection() # you can get from a exsited tif or import
1308
- dataset.SetGeoTransform(geotrans)
1309
- dataset.SetProjection(proj)
1310
- dataset.FlushCache()
1311
- dataset = None
1312
-
1313
- logging.info("Computed Code for the Study Area - Done")
1314
-
1315
- def compute_vulnerability4scenario(manager:Accept_Manager):
1316
- """ Compute the vulnerability for the scenario
1317
-
1318
- This function **will modify** the data by the removed buildings/scenarios.
1319
-
1320
- FIXME: It could be interseting to permit the user to provide tiff files for the removed buildings and other scenarios.
1321
-
1322
- :param dirsnames: the Dirs_Names object from the calling function
1323
- """
1324
-
1325
- array_vuln = gdal.Open(str(manager.SA_VULN))
1326
- geotrans = array_vuln.GetGeoTransform() # get GeoTranform from existed 'data0'
1327
- proj = array_vuln.GetProjection() # you can get from a exsited tif or import
1328
-
1329
- array_vuln = np.array(array_vuln.GetRasterBand(1).ReadAsArray())
1330
-
1331
- array_code = gdal.Open(str(manager.SA_CODE))
1332
- array_code = np.array(array_code.GetRasterBand(1).ReadAsArray())
1333
-
1334
- Rbu = manager.get_files_in_rm_buildings()
1335
-
1336
- if len(Rbu)>0:
1337
- for curfile in Rbu:
1338
- array_mod = gdal.Open(str(curfile))
1339
- array_mod = np.array(array_mod.GetRasterBand(1).ReadAsArray())
1340
-
1341
- ij = np.argwhere(array_mod == 1)
1342
- array_vuln[ij[:,0], ij[:,1]] = 1
1343
- array_code[ij[:,0], ij[:,1]] = 1
1344
-
1345
- dst_filename= str(manager.TMP_VULN)
1346
- y_pixels, x_pixels = array_vuln.shape # number of pixels in x
1347
-
1348
- driver = gdal.GetDriverByName('GTiff')
1349
- dataset = driver.Create(dst_filename, x_pixels, y_pixels, gdal.GDT_Byte, 1, options=["COMPRESS=LZW"])
1350
- dataset.GetRasterBand(1).WriteArray(array_vuln.astype(np.int8))
1351
- # follow code is adding GeoTranform and Projection
1352
- dataset.SetGeoTransform(geotrans)
1353
- dataset.SetProjection(proj)
1354
- dataset.FlushCache()
1355
- dataset = None
1356
-
1357
-
1358
- dst_filename= str(manager.TMP_CODE)
1359
- y_pixels, x_pixels = array_code.shape # number of pixels in x
1360
- driver = gdal.GetDriverByName('GTiff')
1361
- dataset = driver.Create(dst_filename, x_pixels, y_pixels, gdal.GDT_Byte, 1, options=["COMPRESS=LZW"])
1362
- dataset.GetRasterBand(1).WriteArray(array_code.astype(np.int8))
1363
- # follow code is adding GeoTranform and Projection
1364
- dataset.SetGeoTransform(geotrans)
1365
- dataset.SetProjection(proj)
1366
- dataset.FlushCache()
1367
- dataset = None
1368
-
1369
- logging.info("Computed Vulnerability and code for the scenario")
1370
-
1371
- def match_vulnerability2sim(inRas:Path, outRas:Path, MODREC:Path):
1372
- """
1373
- Clip the raster to the MODREC/simulation extent
1374
-
1375
- :param inRas: the input raster file
1376
- :param outRas: the output raster file
1377
- :param MODREC: the MODREC/simulation extent file
1378
-
1379
- """
1380
-
1381
- inRas = str(inRas)
1382
- outRas = str(outRas)
1383
- MODREC = str(MODREC)
1384
-
1385
- data = gdal.Open(MODREC, gdalconst.GA_ReadOnly)
1386
- geoTransform = data.GetGeoTransform()
1387
- minx = geoTransform[0]
1388
- maxy = geoTransform[3]
1389
- maxx = minx + geoTransform[1] * data.RasterXSize
1390
- miny = maxy + geoTransform[5] * data.RasterYSize
1391
- ds = gdal.Open(inRas)
1392
- ds = gdal.Translate(outRas, ds, projWin = [minx, maxy, maxx, miny])
1393
- ds = None
1394
-
1395
-
1396
- @nb.jit(nopython=True, boundscheck=False, inline='always')
1397
- def update_accept(accept, model_h, ij, bounds, loc_accept):
1398
- for idx in range(len(bounds)):
1399
- for i,j in ij:
1400
- if bounds[idx,0] < model_h[i,j] <= bounds[idx,1]:
1401
- accept[i,j] = loc_accept[idx]
1402
-
1403
- def compute_acceptability(manager:Accept_Manager,
1404
- model_h:np.ndarray,
1405
- vulnerability:np.ndarray,
1406
- interval:int,
1407
- geo_projection:tuple,
1408
- save_to_file:bool=True) -> np.ndarray:
1409
-
1410
- """
1411
- Compute the local acceptability based on :
1412
- - the vulnerability
1413
- - the water depth
1414
- - the matrices
1415
-
1416
- :param manager: the Accept_Manager object from the calling function
1417
- :param model_h: the water depth matrix
1418
- :param vulnerability: the vulnerability matrix
1419
- :param interval: the return period
1420
- :param geo_projection: the geotransform and the projection - tuple extracted from another raster file
1421
-
1422
- """
1423
-
1424
- logging.info(interval)
1425
-
1426
- points_accept = pd.read_csv(manager.POINTS_CSV)
1427
-
1428
- points_accept = points_accept[points_accept["Interval"]==interval]
1429
- points_accept = points_accept.reset_index()
1430
-
1431
- accept = np.zeros(vulnerability.shape, dtype=np.float32)
1432
-
1433
- bounds = np.asarray([[0., 0.02], [0.02, 0.3], [0.3, 1], [1, 2.5], [2.5, 1000]], dtype=np.float32)
1434
-
1435
- for i in range(1,6):
1436
- ij = np.argwhere(vulnerability == i)
1437
-
1438
- idx_pts = 5-i
1439
- accept_pts = [points_accept["h-0"][idx_pts],
1440
- points_accept["h-0.02"][idx_pts],
1441
- points_accept["h-0.3"][idx_pts],
1442
- points_accept["h-1"][idx_pts],
1443
- points_accept["h-2.5"][idx_pts]]
1444
-
1445
- update_accept(accept, model_h, ij, bounds, accept_pts)
1446
-
1447
- if save_to_file:
1448
- #save raster
1449
- dst_filename = str(manager.TMP_QFILES / "Q{}.tif".format(interval))
1450
-
1451
- y_pixels, x_pixels = accept.shape # number of pixels in x
1452
- driver = gdal.GetDriverByName('GTiff')
1453
- dataset = driver.Create(dst_filename,
1454
- x_pixels, y_pixels,
1455
- 1,
1456
- gdal.GDT_Float32,
1457
- options=["COMPRESS=LZW"])
1458
-
1459
- dataset.GetRasterBand(1).WriteArray(accept.astype(np.float32))
1460
-
1461
- geotrans, proj = geo_projection
1462
- dataset.SetGeoTransform(geotrans)
1463
- dataset.SetProjection(proj)
1464
- dataset.FlushCache()
1465
- dataset = None
1466
-
1467
- return accept
1468
-
1469
- def shp_to_raster(vector_fn:str, raster_fn:str, pixel_size:float = 1., manager:Accept_Manager = None):
1470
- """
1471
- Convert a vector layer to a raster tiff file.
1472
-
1473
- The raster will contain only 2 values : 0 and 1
1474
-
1475
- - 1 : the inside of the vector layer
1476
- - 0 : the rest == NoData/NullValue
1477
-
1478
- :param vector_fn: the path to the vector file
1479
- :param raster_fn: the path to the raster file
1480
- :param pixel_size: the pixel size of the raster
1481
- """
1482
-
1483
- # Force the input to be a string
1484
- vector_fn = str(vector_fn)
1485
- raster_fn = str(raster_fn)
1486
-
1487
- if manager is None:
1488
- extent_fn = vector_fn
1489
- logging.warning("The extent file is not provided, the extent will be the same as the vector file")
1490
- else:
1491
- extent_fn = str(manager.SA)
1492
- logging.info("The extent file is provided")
1493
-
1494
- NoData_value = 0 # np.nan is not necessary a good idea
1495
-
1496
- # Open the data sources and read the extents
1497
- source_ds:ogr.DataSource = ogr.Open(vector_fn)
1498
- source_layer = source_ds.GetLayer()
1499
-
1500
- extent_ds:ogr.DataSource = ogr.Open(extent_fn)
1501
- extent_layer = extent_ds.GetLayer()
1502
- x_min, x_max, y_min, y_max = extent_layer.GetExtent()
1503
-
1504
- x_min = float(int(x_min))
1505
- x_max = float(np.ceil(x_max))
1506
- y_min = float(int(y_min))
1507
- y_max = float(np.ceil(y_max))
1508
-
1509
- # Create the destination data source
1510
- x_res = int((x_max - x_min) / pixel_size)
1511
- y_res = int((y_max - y_min) / pixel_size)
1512
-
1513
- target_ds = gdal.GetDriverByName('GTiff').Create(raster_fn,
1514
- x_res, y_res,
1515
- 1,
1516
- gdal.GDT_Byte,
1517
- options=["COMPRESS=LZW",
1518
- 'SPARSE_OK=TRUE'])
1519
-
1520
- target_ds.SetGeoTransform((x_min, pixel_size, 0, y_max, 0, -pixel_size))
1521
- srs = osr.SpatialReference()
1522
- srs.ImportFromEPSG(31370)
1523
- target_ds.SetProjection(srs.ExportToWkt())
1524
- band = target_ds.GetRasterBand(1)
1525
- band.SetNoDataValue(NoData_value)
1526
- # Rasterize the areas
1527
- gdal.RasterizeLayer(target_ds,
1528
- bands = [1],
1529
- layer = source_layer,
1530
- burn_values = [1],
1531
- options=["ALL_TOUCHED=TRUE"])
1532
- target_ds = None
1533
- vector_fn = raster_fn = None
1534
-
1535
- def vector_to_raster(layer:str,
1536
- manager:Accept_Manager,
1537
- attribute:str,
1538
- pixel_size:float,
1539
- convert_to_sparse:bool = True):
1540
- """
1541
- Convert a vector layer to a raster tiff file
1542
-
1543
- FIXME: Test de vulerability value and return immedialty if it is 1 if attribute == "Vulne"
1544
-
1545
- :param layer: the layer name in the GDB file
1546
- :param vector_input: the path to the vector file
1547
- :param extent: the path to the extent file
1548
- :param attribute: the attribute to rasterize
1549
- :param pixel_size: the pixel size of the raster
1550
-
1551
- """
1552
-
1553
- layer = str(layer)
1554
-
1555
- vector_input = str(manager.TMP_CODEVULNE / (layer + EXTENT))
1556
- extent = str(manager.SA)
1557
- attribute = str(attribute)
1558
- pixel_size = float(pixel_size)
1559
-
1560
- out_file = manager.TMP_RASTERS / attribute / (layer + ".tiff")
1561
-
1562
- if out_file.exists():
1563
- os.remove(out_file)
1564
-
1565
- out_file = str(out_file)
1566
-
1567
- NoData_value = 0
1568
-
1569
- extent_ds:ogr.DataSource = ogr.Open(extent)
1570
- extent_layer = extent_ds.GetLayer()
1571
-
1572
- x_min, x_max, y_min, y_max = extent_layer.GetExtent()
1573
-
1574
- x_min = float(int(x_min))
1575
- x_max = float(np.ceil(x_max))
1576
- y_min = float(int(y_min))
1577
- y_max = float(np.ceil(y_max))
1578
-
1579
- # Open the data sources and read the extents
1580
- source_ds:ogr.DataSource = ogr.Open(vector_input)
1581
- if source_ds is None:
1582
- logging.error(f"Could not open the data source {layer}")
1583
- return
1584
- source_layer = source_ds.GetLayer()
1585
-
1586
- # Create the destination data source
1587
- x_res = int((x_max - x_min) / pixel_size)
1588
- y_res = int((y_max - y_min) / pixel_size)
1589
- target_ds:gdal.Driver = gdal.GetDriverByName('GTiff').Create(out_file,
1590
- x_res, y_res, 1,
1591
- gdal.GDT_Byte,
1592
- options=["COMPRESS=DEFLATE",
1593
- 'SPARSE_OK=TRUE',])
1594
-
1595
- target_ds.SetGeoTransform((x_min, pixel_size, 0, y_max, 0, -pixel_size))
1596
- srs = osr.SpatialReference()
1597
- srs.ImportFromEPSG(31370)
1598
- target_ds.SetProjection(srs.ExportToWkt())
1599
-
1600
- band = target_ds.GetRasterBand(1)
1601
- band.SetNoDataValue(NoData_value)
1602
-
1603
- # Rasterize the areas
1604
- gdal.RasterizeLayer(target_ds, [1],
1605
- source_layer,
1606
- options=["ATTRIBUTE="+attribute,
1607
- "ALL_TOUCHED=TRUE"])
1608
-
1609
- if convert_to_sparse:
1610
- SPARSITY_THRESHOLD = 0.02
1611
- # Convert the raster to a npz containing the row and col of the non-null values
1612
- array = band.ReadAsArray()
1613
- ij = np.nonzero(array)
1614
-
1615
- if len(ij[0]) < int(x_res * y_res * SPARSITY_THRESHOLD):
1616
- i,j = convert_to_csr(ij[0], ij[1], y_res)
1617
- np.savez_compressed(Path(out_file).with_suffix('.npz'), row=np.asarray(i, dtype=np.int32), col=np.asarray(j, dtype=np.int32))
1618
- else:
1619
- logging.info("The raster is not sparse enough to be converted to a CSR forma {}".format(layer))
1620
-
1621
- target_ds = None
1622
-
1623
- return 0
1624
-
1625
- @nb.jit(nopython=True, boundscheck=False, inline='always')
1626
- def convert_to_csr(i_indices, j_indices, num_rows):
1627
- row_ptr = [0] * (num_rows + 1)
1628
- col_idx = []
1629
-
1630
- for i in range(len(i_indices)):
1631
- row_ptr[i_indices[i] + 1] += 1
1632
- col_idx.append(j_indices[i])
1633
-
1634
- for i in range(1, len(row_ptr)):
1635
- row_ptr[i] += row_ptr[i - 1]
1636
-
1637
- return row_ptr, col_idx
1
+ """
2
+ Author: University of Liege, HECE, LEMA
3
+ Date: 2024
4
+
5
+ Copyright (c) 2024 University of Liege. All rights reserved.
6
+
7
+ This script and its content are protected by copyright law. Unauthorized
8
+ copying or distribution of this file, via any medium, is strictly prohibited.
9
+ """
10
+
11
+ import geopandas as gpd
12
+ import pandas as pd
13
+ import numpy as np
14
+ from osgeo import gdal, ogr, osr, gdalconst
15
+ import os
16
+ import glob
17
+ from pathlib import Path
18
+ import logging
19
+ from tqdm import tqdm
20
+ from pyogrio import list_layers, read_dataframe
21
+ from enum import Enum
22
+ import numba as nb
23
+
24
+ ENGINE = 'pyogrio' # or 'Fiona -- Pyogrio is faster
25
+ EXTENT = '.gpkg'
26
+ class Modif_Type(Enum):
27
+ """
28
+ Enum class for the type of modification
29
+ """
30
+
31
+ WALOUS = 'Walous layers changed to PICC buidings'
32
+ POINT2POLY_EPURATION = 'Change BDREF based on AJOUT_PDET sent by Perrine (SPI)'
33
+ POINT2POLY_PICC = 'Convert the points to polygons based on PICC'
34
+ POINT2POLY_CAPAPICC = 'Convert the points to polygons based on PICC and CaPa'
35
+ INHABITED = 'Select only inhabited buildings'
36
+ ROAD = 'Create a buffer around the roads'
37
+ COPY = 'Copy the data'
38
+
39
+ class Vulnerability_csv():
40
+
41
+ def __init__(self, file:Path) -> None:
42
+ self.file = file
43
+ self.data = pd.read_csv(file, sep=",", encoding='latin-1')
44
+
45
+ def get_layers(self) -> list:
46
+ return [a[1] for a in self.data["Path"].str.split('/')]
47
+
48
+ def get_vulnerability_level(self, layer:str) -> str:
49
+ idx = self.get_layers().index(layer)
50
+ return self.data.iloc[idx]["Vulne"]
51
+
52
+ def get_vulnerability_code(self, layer:str) -> str:
53
+ idx = self.get_layers().index(layer)
54
+ return self.data.iloc[idx]["Code"]
55
+
56
+
57
+ def get_data_type(fname:Path):
58
+
59
+ fname = Path(fname)
60
+ """ Get the data type of the input file from extension """
61
+ if fname.name.endswith('.gpkg'):
62
+ return 'GPKG'
63
+ elif fname.name.endswith('.shp'):
64
+ return 'ESRI Shapefile'
65
+ elif fname.name.endswith('.gdb'):
66
+ return 'OpenfileGDB'
67
+ else:
68
+ return None
69
+
70
+ def cleaning_directory(dir:Path):
71
+ """ Cleaning the directory """
72
+
73
+ logging.info("Cleaning the directory {}".format(dir))
74
+
75
+ files_in_output = list(dir.iterdir())
76
+ for item in files_in_output:
77
+ if item.is_file():
78
+ os.remove(item)
79
+
80
+ class Accept_Manager():
81
+ """
82
+ Structure to store the directories and names of the files.
83
+
84
+ In the main directory, the following directories are mandatory/created:
85
+ - INPUT : filled by the user - contains the input data
86
+ - TEMP : created by the script - contains the temporary data for the study area
87
+ - OUTPUT: created by the script - contains the output data for each scenario of the study area
88
+
89
+ The INPUT directory contains the following subdirectories:
90
+ - DATABASE: contains the data for the **entire Walloon region**
91
+ - Cadastre_Walloon.gpkg: the Cadastre Walloon file
92
+ - GT_Resilence_dataRisques202010.gdb: the original gdb file from SPW - GT Resilience
93
+ - PICC-vDIFF.gdb: the PICC Walloon file
94
+ - CE_IGN_TOP10V: the IGN top10v shapefile
95
+ - EPU_STATIONS_NEW:
96
+ - AJOUT_PDET_EPU_DG03_STATIONS.shp: the EPU stations shapefile
97
+ - STUDY_AREA: contains the study area shapefiles - one for each study area - e.g. Bassin_Vesdre.shp
98
+ - CSVs: contains the CSV files
99
+ - Intermediate.csv: contains the matrices data for the acceptability computation
100
+ # - Ponderation.csv: contains the ponderation data for the acceptability computation
101
+ - Vulnerability.csv: contains the mapping between layers and vulnerability levels - a code value is also provided
102
+ - WATER_DEPTH: contains the water depth data for each scenario
103
+ - Study_area1:
104
+ - Scenario1
105
+ - Scenario2
106
+ -...
107
+ - ScenarioN
108
+ - Study_area2:
109
+ - Scenario1
110
+ - Scenario2
111
+ -...
112
+ - ScenarioN
113
+ -...
114
+ - Study_areaN:
115
+ - Scenario1
116
+ - Scenario2
117
+ -...
118
+ - ScenarioN
119
+
120
+ The TEMP directory contains the following subdirectories:
121
+ - DATABASES: contains the temporary data each study area
122
+ - Study_area1:
123
+ - database.gpkg: the clipped database
124
+ - CaPa.gpkg: the clipped Cadastre Walloon file
125
+ - PICC.gpkg: the clipped PICC Walloon file
126
+ - CE_IGN_TOP10V.tiff: the IGN top10v raster file
127
+ - Maske_River_extent.tiff: the river extent raster file from IGN
128
+ - VULNERABILITY: the vulnerability data
129
+ - RASTERS:
130
+ - Code : one file for each layer
131
+ - Vulne : one file for each layer
132
+ - Scenario1:
133
+
134
+ """
135
+
136
+ def __init__(self,
137
+ main_dir:str = 'Data',
138
+ Study_area:str = 'Bassin_Vesdre.shp',
139
+ scenario = None,
140
+ Original_gdb:str = 'GT_Resilence_dataRisques202010.gdb',
141
+ CaPa_Walloon:str = 'Cadastre_Walloon.gpkg',
142
+ PICC_Walloon:str = 'PICC_vDIFF.gdb',
143
+ CE_IGN_top10v:str = 'CE_IGN_TOP10V/CE_IGN_TOP10V.shp',
144
+ EPU_Stations:str = 'AJOUT_PDET_EPU_DG03_STATIONS.shp',
145
+ Ponderation_csv:str = 'Ponderation.csv',
146
+ Vuln_csv:str = 'Vulnerability.csv',
147
+ Intermediate_csv:str = 'Intermediate.csv'
148
+ ) -> None:
149
+
150
+ self.old_dir:Path = Path(os.getcwd())
151
+
152
+ self.main_dir:Path = Path(main_dir)
153
+
154
+ # If it is a string, concatenate it with the current directory
155
+ if not self.main_dir.is_absolute():
156
+ self.main_dir = Path(os.getcwd()) / self.main_dir
157
+
158
+ self._study_area = str(Study_area)
159
+
160
+ if Study_area is not None:
161
+ if not self._study_area.endswith('.shp'):
162
+ self._study_area += '.shp'
163
+
164
+ self._scenario = scenario
165
+ self._original_gdb = Original_gdb
166
+ self._capa_walloon = CaPa_Walloon
167
+ self._picc_walloon = PICC_Walloon
168
+ self._ce_ign_top10v = CE_IGN_top10v
169
+
170
+ self.IN_DIR = self.main_dir / "INPUT"
171
+ self.IN_DATABASE = self.IN_DIR / "DATABASE"
172
+ self.IN_STUDY_AREA = self.IN_DIR / "STUDY_AREA"
173
+ self.IN_CSV = self.IN_DIR / "CSVs"
174
+ self.IN_WATER_DEPTH = self.IN_DIR / "WATER_DEPTH"
175
+ self.IN_EPU_STATIONS= self.IN_DIR / "EPU_STATIONS_NEW"
176
+
177
+ self.ORIGINAL_GDB = self.IN_DATABASE / self._original_gdb
178
+ self.CAPA_WALLOON = self.IN_DATABASE / self._capa_walloon
179
+ self.PICC_WALLOON = self.IN_DATABASE / self._picc_walloon
180
+ self.CE_IGN_TOP10V = self.IN_DATABASE / self._ce_ign_top10v
181
+ self.EPU_STATIONS = self.IN_EPU_STATIONS / EPU_Stations
182
+
183
+ self.VULNERABILITY_CSV = self.IN_CSV / Vuln_csv
184
+ self.POINTS_CSV = self.IN_CSV / Intermediate_csv
185
+ self.PONDERATION_CSV = self.IN_CSV / Ponderation_csv
186
+
187
+ self._CSVs = [self.VULNERABILITY_CSV, self.POINTS_CSV]
188
+ self._GPKGs= [self.CAPA_WALLOON, self.PICC_WALLOON]
189
+ self._GDBs = [self.ORIGINAL_GDB]
190
+ self._SHPs = [self.CE_IGN_TOP10V, self.EPU_STATIONS]
191
+ self._ALLS = self._CSVs + self._GPKGs + self._GDBs + self._SHPs
192
+
193
+ self.TMP_DIR = self.main_dir / "TEMP"
194
+
195
+ self.OUT_DIR = self.main_dir / "OUTPUT"
196
+
197
+ self.points2polys = []
198
+ self.lines2polys = []
199
+
200
+ self.create_paths()
201
+ self.create_paths_scenario()
202
+
203
+ def create_paths(self):
204
+ """ Create the paths for the directories and files """
205
+
206
+ self.points2polys = []
207
+ self.lines2polys = []
208
+
209
+ if self._study_area is not None:
210
+
211
+ self.Study_area:Path = Path(self._study_area)
212
+
213
+ self.TMP_STUDYAREA = self.TMP_DIR / self.Study_area.stem
214
+ self.TMP_DATABASE = self.TMP_STUDYAREA / "DATABASES"
215
+
216
+ self.TMP_CLIPGDB = self.TMP_DATABASE / "CLIP_GDB"
217
+ self.TMP_CADASTER = self.TMP_DATABASE / "CLIP_CADASTER"
218
+ self.TMP_PICC = self.TMP_DATABASE / "CLIP_PICC"
219
+ self.TMP_IGNCE = self.TMP_DATABASE / "CLIP_IGN_CE"
220
+ self.TMP_WMODIF = self.TMP_DATABASE / "WITH_MODIF"
221
+ self.TMP_CODEVULNE = self.TMP_DATABASE / "CODE_VULNE"
222
+
223
+ self.TMP_VULN_DIR = self.TMP_STUDYAREA / "VULNERABILITY"
224
+ self.TMP_RASTERS = self.TMP_VULN_DIR / "RASTERS"
225
+ self.TMP_RASTERS_CODE = self.TMP_RASTERS / "Code"
226
+ self.TMP_RASTERS_VULNE = self.TMP_RASTERS / "Vulne"
227
+
228
+ self.OUT_STUDY_AREA = self.OUT_DIR / self.Study_area.stem
229
+
230
+ self.SA = self.IN_STUDY_AREA / self.Study_area
231
+ self.SA_MASKED_RIVER = self.TMP_IGNCE / "CE_IGN_TOP10V.tiff"
232
+ self.SA_VULN = self.TMP_VULN_DIR / "Vulnerability.tiff"
233
+ self.SA_CODE = self.TMP_VULN_DIR / "Vulnerability_Code.tiff"
234
+
235
+ else:
236
+ self.Study_area = None
237
+ self._scenario = None
238
+
239
+ self.TMP_STUDYAREA = None
240
+ self.TMP_DATABASE = None
241
+ self.TMP_CADASTER = None
242
+ self.TMP_PICC = None
243
+ self.TMP_IGNCE = None
244
+ self.TMP_WMODIF = None
245
+ self.TMP_CODEVULNE = None
246
+ self.TMP_VULN_DIR = None
247
+ self.TMP_RASTERS = None
248
+ self.TMP_RASTERS_CODE = None
249
+ self.TMP_RASTERS_VULNE = None
250
+
251
+ self.OUT_STUDY_AREA = None
252
+
253
+ self.SA = None
254
+ self.SA_MASKED_RIVER = None
255
+
256
+ self.SA_VULN = None
257
+ self.SA_CODE = None
258
+
259
+ self.create_paths_scenario()
260
+
261
+ self.check_inputs()
262
+ self.check_temporary()
263
+ self.check_outputs()
264
+
265
+ def create_paths_scenario(self):
266
+
267
+ if self._scenario is not None:
268
+
269
+ self.scenario:str = str(self._scenario)
270
+
271
+ self.IN_SCEN_DIR = self.IN_WATER_DEPTH / self.SA.stem / self.scenario
272
+ self.IN_RM_BUILD_DIR = self.IN_SCEN_DIR / "REMOVED_BUILDINGS"
273
+
274
+ self.TMP_SCEN_DIR = self.TMP_VULN_DIR / self.scenario
275
+ self.TMP_RM_BUILD_DIR = self.TMP_SCEN_DIR / "REMOVED_BUILDINGS"
276
+ self.TMP_QFILES = self.TMP_SCEN_DIR / "Q_FILES"
277
+
278
+ self.TMP_VULN = self.TMP_SCEN_DIR / "Vulnerability.tiff"
279
+ self.TMP_CODE = self.TMP_SCEN_DIR / "Vulnerability_Code.tiff"
280
+
281
+ self.OUT_SCEN_DIR = self.OUT_STUDY_AREA / self.scenario
282
+ self.OUT_VULN = self.OUT_SCEN_DIR / "Vulnerability.tiff"
283
+ self.OUT_CODE = self.OUT_SCEN_DIR / "Vulnerability_Code.tiff"
284
+ self.OUT_MASKED_RIVER = self.OUT_SCEN_DIR / "Masked_River_extent.tiff"
285
+ self.OUT_ACCEPT = self.OUT_SCEN_DIR / "Acceptability.tiff"
286
+ self.OUT_ACCEPT_100M = self.OUT_SCEN_DIR / "Acceptability_100m.tiff"
287
+
288
+ else:
289
+ self.scenario = None
290
+
291
+ self.IN_SCEN_DIR = None
292
+ self.IN_RM_BUILD_DIR = None
293
+
294
+ self.TMP_SCEN_DIR = None
295
+ self.TMP_RM_BUILD_DIR = None
296
+ self.TMP_QFILES = None
297
+
298
+ self.TMP_VULN = None
299
+ self.TMP_CODE = None
300
+
301
+ self.OUT_SCEN_DIR = None
302
+ self.OUT_VULN = None
303
+ self.OUT_CODE = None
304
+ self.OUT_MASKED_RIVER = None
305
+ self.OUT_ACCEPT = None
306
+ self.OUT_ACCEPT_100M = None
307
+
308
+ @property
309
+ def is_valid_inputs(self) -> bool:
310
+ return self.check_inputs()
311
+
312
+ @property
313
+ def is_valid_study_area(self) -> bool:
314
+ return self.SA.exists()
315
+
316
+ @property
317
+ def is_valid_vulnerability_csv(self) -> bool:
318
+ return self.VULNERABILITY_CSV.exists()
319
+
320
+ @property
321
+ def is_valid_points_csv(self) -> bool:
322
+ return self.POINTS_CSV.exists()
323
+
324
+ @property
325
+ def is_valid_ponderation_csv(self) -> bool:
326
+ return self.PONDERATION_CSV.exists()
327
+
328
+ def check_files(self) -> str:
329
+ """ Check the files in the directories """
330
+
331
+ files = ""
332
+ for a in self._ALLS:
333
+ if not a.exists():
334
+ files += str(a) + "\n"
335
+
336
+ return files
337
+
338
+ def change_studyarea(self, Study_area:str = None) -> None:
339
+
340
+ if Study_area is None:
341
+ self._study_area = None
342
+ self._scenario = None
343
+ else:
344
+ if Study_area in self.get_list_studyareas(with_suffix=True):
345
+ self._study_area = Path(Study_area)
346
+ else:
347
+ logging.error("The study area does not exist in the study area directory")
348
+
349
+ self.create_paths()
350
+
351
+ def change_scenario(self, scenario:str) -> None:
352
+
353
+ if scenario in self.get_list_scenarios():
354
+ self._scenario = scenario
355
+ self.create_paths_scenario()
356
+ self.check_temporary()
357
+ self.check_outputs()
358
+ else:
359
+ logging.error("The scenario does not exist in the water depth directory")
360
+
361
+ def get_files_in_rm_buildings(self) -> list[Path]:
362
+ return [Path(a) for a in glob.glob(str(self.IN_RM_BUILD_DIR / ("*"+ EXTENT)))]
363
+
364
+ def get_files_in_rasters_vulne(self) -> list[Path]:
365
+ return [Path(a) for a in glob.glob(str(self.TMP_RASTERS_VULNE / "*.tiff"))]
366
+
367
+ def get_layers_in_gdb(self) -> list[str]:
368
+ return [a[0] for a in list_layers(str(self.ORIGINAL_GDB))]
369
+
370
+ def get_layer_types_in_gdb(self) -> list[str]:
371
+ return [a[1] for a in list_layers(str(self.ORIGINAL_GDB))]
372
+
373
+ def get_layers_in_clipgdb(self) -> list[str]:
374
+ return [Path(a).stem for a in glob.glob(str(self.TMP_CLIPGDB / ("*"+ EXTENT)))]
375
+
376
+ def get_layers_in_wmodif(self) -> list[str]:
377
+ return [Path(a).stem for a in glob.glob(str(self.TMP_WMODIF / ("*"+ EXTENT)))]
378
+
379
+ def get_layers_in_codevulne(self) -> list[str]:
380
+ return [Path(a).stem for a in glob.glob(str(self.TMP_CODEVULNE / ("*"+ EXTENT)))]
381
+
382
+ def get_files_in_rasters_code(self) -> list[Path]:
383
+ return [Path(a) for a in glob.glob(str(self.TMP_RASTERS_CODE / "*.tiff"))]
384
+
385
+ def get_q_files(self) -> list[Path]:
386
+ return [Path(a) for a in glob.glob(str(self.TMP_QFILES / "*.tif"))]
387
+
388
+ def get_list_scenarios(self) -> list[str]:
389
+ return [Path(a).stem for a in glob.glob(str(self.IN_WATER_DEPTH / self.SA.stem / "Scenario*"))]
390
+
391
+ def get_list_studyareas(self, with_suffix:bool = False) -> list[str]:
392
+
393
+ if with_suffix:
394
+ return [Path(a).name for a in glob.glob(str(self.IN_STUDY_AREA / "*.shp"))]
395
+ else:
396
+ return [Path(a).stem for a in glob.glob(str(self.IN_STUDY_AREA / "*.shp"))]
397
+
398
+ def get_sims_files_for_scenario(self) -> list[Path]:
399
+
400
+ return [Path(a) for a in glob.glob(str(self.IN_SCEN_DIR / "*.tif"))]
401
+
402
+ def get_sim_file_for_return_period(self, return_period:int) -> Path:
403
+
404
+ sims = self.get_sims_files_for_scenario()
405
+
406
+ if len(sims)==0:
407
+ logging.error("No simulations found")
408
+ return None
409
+
410
+ if "_h.tif" in sims[0].name:
411
+ for cursim in sims:
412
+ if cursim.stem.find("_T{}_".format(return_period)) != -1:
413
+ return cursim
414
+ else:
415
+ for cursim in sims:
416
+ if cursim.stem.find("T{}".format(return_period)) != -1:
417
+ return cursim
418
+
419
+ return None
420
+
421
+ def get_types_in_file(self, file:str) -> list[str]:
422
+ """ Get the types of the geometries in the Shape file """
423
+
424
+ return [a[1] for a in list_layers(str(file))]
425
+
426
+ def is_type_unique(self, file:str) -> bool:
427
+ """ Check if the file contains only one type of geometry """
428
+
429
+ types = self.get_types_in_file(file)
430
+ return len(types) == 1
431
+
432
+ def is_polygons(self, set2test:set) -> bool:
433
+ """ Check if the set contains only polygons """
434
+
435
+ set2test = list(set2test)
436
+ firstone = set2test[0]
437
+ if 'Polygon' in firstone:
438
+ for curtype in set2test:
439
+ if 'Polygon' not in curtype:
440
+ return False
441
+ return True
442
+ else:
443
+ return False
444
+
445
+ def is_same_types(self, file:str) -> tuple[bool, str]:
446
+ """ Check if the file contains only the same type of geometry """
447
+
448
+ types = self.get_types_in_file(file)
449
+
450
+ if len(types) == 1:
451
+ if 'Point' in types[0]:
452
+ return True, 'Point'
453
+ elif 'Polygon' in types[0]:
454
+ return True, 'Polygon'
455
+ elif 'LineString' in types[0]:
456
+ return True, 'LineString'
457
+ else:
458
+ raise ValueError(f"The type of geometry {types[0]} is not recognized")
459
+ else:
460
+ firstone = types[0]
461
+ if 'Point' in firstone:
462
+ for curtype in types:
463
+ if 'Point' not in curtype:
464
+ return False, None
465
+ return True, 'Point'
466
+
467
+ elif 'Polygon' in firstone:
468
+ for curtype in types:
469
+ if 'Polygon' not in curtype:
470
+ return False, None
471
+
472
+ return True, 'Polygon'
473
+
474
+ elif 'LineString' in firstone:
475
+ for curtype in types:
476
+ if 'LineString' not in curtype:
477
+ return False, None
478
+
479
+ return True, 'LineString'
480
+ else:
481
+ raise ValueError(f"The type of geometry {firstone} is not recognized")
482
+
483
+
484
+ def get_return_periods(self) -> list[int]:
485
+ """
486
+ Get the return periods from the simulations
487
+
488
+ :return list[int]: the **sorted list** of return periods
489
+ """
490
+
491
+ # List files in directory
492
+ sims = self.get_sims_files_for_scenario()
493
+
494
+ if len(sims)==0:
495
+ logging.error("No simulations found")
496
+ return None
497
+
498
+ # Two cases:
499
+ # - Return periods are named as T2.tif, T5.tif, T10.tif, ...
500
+ # - Return periods are named as *_T2_h.tif, *_T5_h.tif, *_T10_h.tif, ...
501
+ if "_h.tif" in sims[0].name:
502
+
503
+ # Searching for the position of the return period in the name
504
+ idx_T = [cursim.name.find("_T") for cursim in sims]
505
+ idx_h = [cursim.name.find("_h.tif") for cursim in sims]
506
+
507
+ assert len(idx_T) == len(idx_h), "The number of T and h are not the same"
508
+ for curT, curh in zip(idx_T, idx_h):
509
+ assert curT != -1, "The T is not found"
510
+ assert curh != -1, "The h is not found"
511
+ assert curh > curT, "The h is before the T"
512
+
513
+ # Create the list of return periods -- only the numeric part
514
+ sims = [int(cursim.name[idx_T[i]+2:idx_h[i]]) for i, cursim in enumerate(sims)]
515
+ else:
516
+ # searching for the position of the return period in the name
517
+ idx_T = [cursim.name.find("T") for cursim in sims]
518
+ idx_h = [cursim.name.find(".tif") for cursim in sims]
519
+
520
+ assert len(idx_T) == len(idx_h), "The number of T and h are not the same"
521
+ for curT, curh in zip(idx_T, idx_h):
522
+ assert curT != -1, "The T is not found"
523
+ assert curh != -1, "The h is not found"
524
+ assert curh > curT, "The h is before the T"
525
+
526
+ # create the list of return periods -- only the numeric part
527
+ sims = [int(cursim.name[idx_T[i]+1:idx_h[i]]) for i, cursim in enumerate(sims)]
528
+
529
+ return sorted(sims)
530
+
531
+ def get_ponderations(self) -> pd.DataFrame:
532
+ """ Get the ponderation data from available simulations """
533
+
534
+ rt = self.get_return_periods()
535
+
536
+ if len(rt)==0:
537
+ logging.error("No simulations found")
538
+ return None
539
+
540
+ pond = []
541
+
542
+ pond.append(1./float(rt[0]) + (1./float(rt[0]) - 1./float(rt[1]))/2.)
543
+ for i in range(1, len(rt)-1):
544
+ # Full formula
545
+ # pond.append((1./float(rt[i-1]) - 1./float(rt[i]))/2. + (1./float(rt[i]) - 1./float(rt[i+1]))/2.)
546
+
547
+ # More compact formula
548
+ pond.append((1./float(rt[i-1]) - 1./float(rt[i+1]))/2.)
549
+
550
+ pond.append(1./float(rt[-1]) + (1./float(rt[-2]) - 1./float(rt[-1]))/2.)
551
+
552
+ return pd.DataFrame(pond, columns=["Ponderation"], index=rt)
553
+
554
+ def get_filepath_for_return_period(self, return_period:int) -> Path:
555
+
556
+ return self.get_sim_file_for_return_period(return_period)
557
+
558
+ def change_dir(self) -> None:
559
+ os.chdir(self.main_dir)
560
+ logging.info("Current directory: %s", os.getcwd())
561
+
562
+ def restore_dir(self) -> None:
563
+ os.chdir(self.old_dir)
564
+ logging.info("Current directory: %s", os.getcwd())
565
+
566
+ def check_inputs(self) -> bool:
567
+ """
568
+ Check if the input directories exist.
569
+
570
+ Inputs can not be created automatically. The user must provide them.
571
+
572
+ """
573
+
574
+ err = False
575
+ if not self.IN_DATABASE.exists():
576
+ logging.error("INPUT : The database directory does not exist")
577
+ err = True
578
+
579
+ if not self.IN_STUDY_AREA.exists():
580
+ logging.error("INPUT : The study area directory does not exist")
581
+ err = True
582
+
583
+ if not self.IN_CSV.exists():
584
+ logging.error("INPUT : The CSV directory does not exist")
585
+ err = True
586
+
587
+ if not self.IN_WATER_DEPTH.exists():
588
+ logging.error("INPUT : The water depth directory does not exist")
589
+ err = True
590
+
591
+ if not self.IN_EPU_STATIONS.exists():
592
+ logging.error("INPUT : The EPU stations directory does not exist")
593
+ err = True
594
+
595
+ if self.Study_area is not None:
596
+ if not self.SA.exists():
597
+ logging.error("INPUT : The study area file does not exist")
598
+ err = True
599
+
600
+ if not self.ORIGINAL_GDB.exists():
601
+ logging.error("INPUT : The original gdb file does not exist - Please pull it from the SPW-ARNE")
602
+ err = True
603
+
604
+ if not self.CAPA_WALLOON.exists():
605
+ logging.error("INPUT : The Cadastre Walloon file does not exist - Please pull it from the SPW")
606
+ err = True
607
+
608
+ if not self.PICC_WALLOON.exists():
609
+ logging.error("INPUT : The PICC Walloon file does not exist - Please pull it from the SPW website")
610
+ err = True
611
+
612
+ if not self.CE_IGN_TOP10V.exists():
613
+ logging.error("INPUT : The CE IGN top10v file does not exist - Please pull it from the IGN")
614
+ err = True
615
+
616
+ if self.scenario is None:
617
+ logging.debug("The scenario has not been defined")
618
+ else:
619
+ if not self.IN_SCEN_DIR.exists():
620
+ logging.error("The scenario directory does not exist")
621
+ err = True
622
+
623
+ return not err
624
+
625
+ def check_temporary(self) -> bool:
626
+ """
627
+ Check if the temporary directories exist.
628
+
629
+ If not, create them.
630
+ """
631
+
632
+ self.TMP_DIR.mkdir(parents=True, exist_ok=True)
633
+
634
+ if self.Study_area is not None:
635
+ self.TMP_STUDYAREA.mkdir(parents=True, exist_ok=True)
636
+ self.TMP_DATABASE.mkdir(parents=True, exist_ok=True)
637
+ self.TMP_CLIPGDB.mkdir(parents=True, exist_ok=True)
638
+ self.TMP_CADASTER.mkdir(parents=True, exist_ok=True)
639
+ self.TMP_WMODIF.mkdir(parents=True, exist_ok=True)
640
+ self.TMP_CODEVULNE.mkdir(parents=True, exist_ok=True)
641
+ self.TMP_PICC.mkdir(parents=True, exist_ok=True)
642
+ self.TMP_IGNCE.mkdir(parents=True, exist_ok=True)
643
+ self.TMP_VULN_DIR.mkdir(parents=True, exist_ok=True)
644
+ self.TMP_RASTERS.mkdir(parents=True, exist_ok=True)
645
+ self.TMP_RASTERS_CODE.mkdir(parents=True, exist_ok=True)
646
+ self.TMP_RASTERS_VULNE.mkdir(parents=True, exist_ok=True)
647
+
648
+ if self.scenario is not None:
649
+ self.TMP_SCEN_DIR.mkdir(parents=True, exist_ok=True)
650
+ self.TMP_RM_BUILD_DIR.mkdir(parents=True, exist_ok=True)
651
+ self.TMP_QFILES.mkdir(parents=True, exist_ok=True)
652
+
653
+ return True
654
+
655
+ def check_outputs(self) -> bool:
656
+ """
657
+ Check if the output directories exist.
658
+
659
+ If not, create them.
660
+ """
661
+
662
+ self.OUT_DIR.mkdir(parents=True, exist_ok=True)
663
+
664
+ if self.Study_area is not None:
665
+ self.OUT_STUDY_AREA.mkdir(parents=True, exist_ok=True)
666
+
667
+ if self.scenario is not None:
668
+ self.OUT_SCEN_DIR.mkdir(parents=True, exist_ok=True)
669
+
670
+ return True
671
+
672
+ def check_before_database_creation(self) -> bool:
673
+ """ Check if the necessary files are present before the database creation"""
674
+
675
+ if not self.is_valid_inputs:
676
+ logging.error("Theere are missing input directories - Please check carefully the input directories and the logs")
677
+ return False
678
+
679
+ if not self.is_valid_study_area:
680
+ logging.error("The study area file does not exist - Please create it")
681
+ return False
682
+
683
+ if not self.is_valid_vulnerability_csv:
684
+ logging.error("The vulnerability CSV file does not exist - Please create it")
685
+ return False
686
+
687
+ return True
688
+
689
+ def check_before_rasterize(self) -> bool:
690
+
691
+ if not self.TMP_CODEVULNE.exists():
692
+ logging.error("The final database with vulnerability levels does not exist")
693
+ return False
694
+
695
+ if not self.TMP_WMODIF.exists():
696
+ logging.error("The vector data with modifications does not exist")
697
+ return False
698
+
699
+ return True
700
+
701
+ def check_before_vulnerability(self) -> bool:
702
+
703
+ if self.SA is None:
704
+ logging.error("The area of interest does not exist")
705
+ return False
706
+
707
+ if self.IN_WATER_DEPTH is None:
708
+ logging.error("The water depth directory does not exist")
709
+ return False
710
+
711
+ if self.IN_SCEN_DIR is None:
712
+ logging.error("The scenario directory does not exist in the water depth directory")
713
+ return False
714
+
715
+ if self.SA_MASKED_RIVER is None:
716
+ logging.error("The IGN raster does not exist")
717
+ return False
718
+
719
+ return True
720
+
721
+ def check_vuln_code_sa(self) -> bool:
722
+
723
+ if not self.SA_VULN.exists():
724
+ logging.error("The vulnerability raster file does not exist")
725
+ return False
726
+
727
+ if not self.SA_CODE.exists():
728
+ logging.error("The vulnerability code raster file does not exist")
729
+ return False
730
+
731
+ return True
732
+
733
+ def check_vuln_code_scenario(self) -> bool:
734
+
735
+ if not self.TMP_VULN.exists():
736
+ logging.error("The vulnerability raster file does not exist")
737
+ return False
738
+
739
+ if not self.TMP_CODE.exists():
740
+ logging.error("The vulnerability code raster file does not exist")
741
+ return False
742
+
743
+ return True
744
+
745
+ def compare_original_clipped_layers(self) -> str:
746
+ """ Compare the original layers with the clipped ones """
747
+
748
+ layers = self.get_layers_in_gdb()
749
+ layers_clip = self.get_layers_in_clipgdb()
750
+
751
+ ret = 'These layers have not been clipped:\n'
752
+ for layer in layers:
753
+ if layer not in layers_clip:
754
+ ret += " - {}\n".format(layer)
755
+
756
+ ret += '\nThese layers have been clipped but are not present in the GDB:\n'
757
+ for layer in layers_clip:
758
+ if layer not in layers:
759
+ ret += " - {}\n".format(layer)
760
+
761
+ ret+='\n'
762
+
763
+ return ret
764
+
765
+ def compare_clipped_raster_layers(self) -> str:
766
+ """ Compare the clipped layers with the rasterized ones """
767
+
768
+ layers = self.get_layers_in_clipgdb()
769
+ layers_rast = self.get_layers_in_codevulne()
770
+
771
+ ret = 'These layers {} have not been rasterized:\n'
772
+ for layer in layers:
773
+ if layer not in layers_rast:
774
+ ret += " - {}\n".format(layer)
775
+
776
+ ret += '\nThese layers have been rasterized but are not in the orginal GDB:\n'
777
+ for layer in layers_rast:
778
+ if layer not in layers:
779
+ ret += " - {}\n".format(layer)
780
+
781
+ ret+='\n'
782
+
783
+ return ret
784
+
785
+ def get_operand(self, file:str) -> Modif_Type:
786
+ """ Get the operand based on the layer name """
787
+ LAYERS_WALOUS = ["WALOUS_2018_LB72_112",
788
+ "WALOUS_2018_LB72_31",
789
+ "WALOUS_2018_LB72_32",
790
+ "WALOUS_2018_LB72_331",
791
+ "WALOUS_2018_LB72_332",
792
+ "WALOUS_2018_LB72_333",
793
+ "WALOUS_2018_LB72_34"]
794
+
795
+ ret, curtype = self.is_same_types(file)
796
+ layer = Path(file).stem
797
+
798
+ if not ret:
799
+ raise ValueError("The layer contains different types of geometries")
800
+
801
+ if layer in LAYERS_WALOUS:
802
+ return Modif_Type.WALOUS
803
+
804
+ elif curtype=="Point":
805
+
806
+ self.points2polys.append(layer)
807
+
808
+ if layer =="BDREF_DGO3_PASH__SCHEMA_STATIONS_EPU":
809
+ return Modif_Type.POINT2POLY_EPURATION
810
+ elif layer =="INFRASIG_SOINS_SANTE__ETAB_AINES":
811
+ return Modif_Type.POINT2POLY_PICC
812
+ else:
813
+ return Modif_Type.POINT2POLY_CAPAPICC
814
+
815
+ elif layer =="Hab_2018_CABU":
816
+ return Modif_Type.INHABITED
817
+
818
+ elif layer =="INFRASIG_ROUTE_RES_ROUTIER_TE_AXES":
819
+
820
+ self.lines2polys.append(layer)
821
+
822
+ return Modif_Type.ROAD
823
+
824
+ else:
825
+ return Modif_Type.COPY
826
+
827
+ def check_origin_shape(self) -> list[str]:
828
+
829
+ code = self.get_files_in_rasters_code()
830
+ vuln = self.get_files_in_rasters_vulne()
831
+
832
+ if len(code) == 0:
833
+ logging.error("The code rasters do not exist")
834
+ return False
835
+
836
+ if len(vuln) == 0:
837
+ logging.error("The vulnerability rasters do not exist")
838
+ return False
839
+
840
+ if len(code) != len(vuln):
841
+ logging.error("The number of code and vulnerability rasters do not match")
842
+ return False
843
+
844
+ # we take a reference raster
845
+ ref = gdal.Open(str(code[0]))
846
+ band_ref = ref.GetRasterBand(1)
847
+ proj_ref = ref.GetProjection()
848
+ geo_ref = ref.GetGeoTransform()
849
+ col_ref, row_ref = band_ref.XSize, band_ref.YSize
850
+
851
+ # we compare the reference raster with the others
852
+ diff = []
853
+ for cur in code + vuln + [self.SA_MASKED_RIVER]:
854
+ cur_ = gdal.Open(str(cur))
855
+ band_cur = cur_.GetRasterBand(1)
856
+ proj_cur = cur_.GetProjection()
857
+ geo_cur = cur_.GetGeoTransform()
858
+ col_cur, row_cur = band_cur.XSize, band_cur.YSize
859
+
860
+ if geo_ref != geo_cur:
861
+ logging.error("The geotransforms do not match {}".format(cur))
862
+ diff.append(cur)
863
+
864
+ if proj_ref != proj_cur:
865
+ logging.error("The projections do not match {}".format(cur))
866
+ diff.append(cur)
867
+
868
+ if col_ref != col_cur or row_ref != row_cur:
869
+ logging.error("The dimensions do not match {}".format(cur))
870
+ diff.append(cur)
871
+
872
+ return diff
873
+
874
+
875
+ def clip_layer(layer:str,
876
+ file_path:str,
877
+ Study_Area:str,
878
+ output_dir:str):
879
+ """
880
+ Clip the input data based on the selected bassin and saves it
881
+ in separate shape files.
882
+
883
+ As shape file doen not support DateTime, the columns with DateTime
884
+ are converted to string.
885
+
886
+ :param layer: the layer name in the GDB file
887
+ :param file_path: the path to the GDB file
888
+ :param Study_Area: the path to the study area shapefile
889
+ :param output_dir: the path to the output directory
890
+ """
891
+
892
+ layer = str(layer)
893
+ file_path = str(file_path)
894
+ Study_Area = str(Study_Area)
895
+ output_dir = Path(output_dir)
896
+
897
+ St_Area = gpd.read_file(Study_Area, engine=ENGINE)
898
+
899
+ logging.info(layer)
900
+
901
+ # The data is clipped during the reading
902
+ # **It is more efficient than reading the entire data and then clipping it**
903
+ #
904
+ # FIXME: "read_dataframe" is used directly rather than "gpd.read_file" cause
905
+ # the "layer" parameter is well transmitted to the "read_dataframe" function...
906
+ df:gpd.GeoDataFrame = read_dataframe(file_path, layer=layer, mask=St_Area['geometry'][0])
907
+
908
+ if len(df) == 0:
909
+ logging.warning("No data found for layer " + str(layer))
910
+ return "No data found for layer " + str(layer)
911
+
912
+ # Force Lambert72 -> EPSG:31370
913
+ df.to_crs("EPSG:31370", inplace=True)
914
+ try:
915
+ date_columns = df.select_dtypes(include=['datetimetz']).columns.tolist()
916
+ if len(date_columns)>0:
917
+ df[date_columns] = df[date_columns].astype(str)
918
+
919
+ df.to_file(str(output_dir / (layer+EXTENT)), mode='w', engine=ENGINE)
920
+ except Exception as e:
921
+ logging.error("Error while saving the clipped " + str(layer) + " to file")
922
+ logging.error(e)
923
+ pass
924
+
925
+ logging.info("Saved the clipped " + str(layer) + " to file")
926
+ return "Saved the clipped " +str(layer)+ " to file"
927
+
928
+
929
+ def data_modification(layer:str,
930
+ manager:Accept_Manager,
931
+ picc:gpd.GeoDataFrame,
932
+ capa:gpd.GeoDataFrame ):
933
+ """
934
+ Apply the data modifications as described in the LEMA report
935
+
936
+ FIXME : Add more doc in this docstring
937
+
938
+ :param input_database: the path to the input database
939
+ :param layer: the layer name in the database
940
+ :param output_database: the path to the output database
941
+ :param picc: the PICC Walloon file -- Preloaded
942
+ :param capa: the Cadastre Walloon file -- Preloaded
943
+ """
944
+
945
+ df1:gpd.GeoDataFrame
946
+ df2:gpd.GeoDataFrame
947
+
948
+ layer = str(layer)
949
+
950
+ dir_input = manager.TMP_CLIPGDB
951
+ dir_output = manager.TMP_WMODIF
952
+
953
+ input_file = str(dir_input / (layer + EXTENT))
954
+ output_file = str(dir_output / (layer + EXTENT))
955
+
956
+ # Read the data
957
+ df:gpd.GeoDataFrame = gpd.read_file(input_file, engine=ENGINE)
958
+ nblines, _ = df.shape
959
+
960
+ if nblines>0:
961
+ op = manager.get_operand(input_file)
962
+
963
+ if op == Modif_Type.WALOUS:
964
+ # Walous layers changed to PICC buidings
965
+
966
+ assert picc.crs == df.crs, "CRS of PICC and input data do not match"
967
+
968
+ assert "GEOREF_ID" in picc.columns, "The PICC file does not contain the GEOREF_ID column"
969
+ assert "NATUR_CODE" in picc.columns, "The PICC file does not contain the NATUR_CODE column"
970
+
971
+ df1 = gpd.sjoin(picc, df, how="inner", predicate="intersects" )
972
+ cols = df.columns
973
+
974
+ cols = np.append(cols, "GEOREF_ID")
975
+ cols = np.append(cols, "NATUR_CODE")
976
+
977
+ df1 = df1[cols]
978
+
979
+ if df1.shape[0] > 0:
980
+ assert manager.is_polygons(set(df1.geom_type)), f"The layer does not contains polygons - {op}"
981
+ df1.to_file(output_file, engine=ENGINE)
982
+ else:
983
+ logging.warning("No data found for layer " + str(layer))
984
+
985
+ elif op == Modif_Type.POINT2POLY_EPURATION:
986
+ # Change BDREF based on AJOUT_PDET sent by Perrine (SPI)
987
+
988
+ # The original layer is a point layer.
989
+ # The EPU_STATIONS shape file (from SPI) is a polygon layer.
990
+
991
+ df1 = gpd.read_file(str(manager.EPU_STATIONS), engine=ENGINE)
992
+
993
+ assert df1.crs == df.crs, "CRS of AJOUT_PDET and input data do not match"
994
+
995
+ df2 = gpd.sjoin(picc, df1, how="inner", predicate="intersects" )
996
+
997
+ if df2.shape[0] > 0:
998
+ assert manager.is_polygons(set(df2.geom_type)), f"The layer does not contains polygons - {op}"
999
+ df2.to_file(output_file, engine=ENGINE)
1000
+ else:
1001
+ logging.warning("No data found for layer " + str(layer))
1002
+
1003
+ elif op == Modif_Type.POINT2POLY_PICC:
1004
+ # Select the polygons that contains the points
1005
+ # in theCadaster and PICC files
1006
+
1007
+ assert capa.crs == df.crs, "CRS of CaPa and input data do not match"
1008
+ assert "CaPaKey" in capa.columns, "The CaPa file does not contain the CaPaKey column"
1009
+
1010
+ df1= gpd.sjoin(capa, df, how="inner", predicate="intersects" )
1011
+ cols=df.columns
1012
+
1013
+ cols = np.append(cols, "CaPaKey")
1014
+ df1=df1[cols]
1015
+ df2=gpd.sjoin(picc, df1, how="inner", predicate="intersects" )
1016
+
1017
+ if df2.shape[0] > 0:
1018
+ assert manager.is_polygons(set(df2.geom_type)), f"The layer does not contains polygons - {op}"
1019
+ df2.to_file(output_file, engine=ENGINE)
1020
+ else:
1021
+ logging.warning("No data found for layer " + str(layer))
1022
+
1023
+ elif op == Modif_Type.POINT2POLY_CAPAPICC:
1024
+
1025
+ # Select the polygons that contains the points
1026
+ # in theCadaster and PICC files
1027
+
1028
+ assert capa.crs == df.crs, "CRS of CaPa and input data do not match"
1029
+ assert picc.crs == df.crs, "CRS of PICC and input data do not match"
1030
+
1031
+ # Join the Layer and CaPa DataFrames : https://geopandas.org/en/stable/docs/reference/api/geopandas.sjoin.html
1032
+ # ‘inner’: use intersection of keys from both dfs; retain only left_df geometry column
1033
+ # "intersects" : Binary predicate. Valid values are determined by the spatial index used.
1034
+ df1= gpd.sjoin(capa, df, how="inner", predicate="intersects" )
1035
+
1036
+ # Retain only the columns of the input data
1037
+ cols = df.columns
1038
+ # but add the CaPaKey
1039
+ cols = np.append(cols, "CaPaKey")
1040
+
1041
+ df1 = df1[cols]
1042
+
1043
+ # Join the df1 and PICC DataFrames : https://geopandas.org/en/stable/docs/reference/api/geopandas.sjoin.html
1044
+ df2 = gpd.sjoin(picc, df1, how="inner", predicate="intersects" )
1045
+
1046
+ # Add only the GEOREF_ID and NATUR_CODE columns from PICC
1047
+ cols = np.append(cols, "GEOREF_ID")
1048
+ cols = np.append(cols, "NATUR_CODE")
1049
+
1050
+ df2 = df2[cols]
1051
+
1052
+ if df2.shape[0] > 0:
1053
+ assert manager.is_polygons(set(df2.geom_type)), f"The layer does not contains polygons - {op}"
1054
+ df2.to_file(output_file, engine=ENGINE)
1055
+ else:
1056
+ logging.warning("No data found for layer " + str(layer))
1057
+
1058
+ elif op == Modif_Type.INHABITED:
1059
+ # Select only the buildings with a number of inhabitants > 0
1060
+ df1=df[df["NbsHabTOT"]>0]
1061
+
1062
+ if df1.shape[0] > 0:
1063
+ assert manager.is_polygons(set(df1.geom_type)), f"The layer does not contains polygons - {op}"
1064
+ df1.to_file(output_file, engine=ENGINE)
1065
+ else:
1066
+ logging.warning("No data found for layer " + str(layer))
1067
+
1068
+ elif op == Modif_Type.ROAD:
1069
+ # Create a buffer around the roads
1070
+ df1=df.buffer(distance=6, cap_style=2)
1071
+
1072
+ if df1.shape[0] > 0:
1073
+ assert set(df1.geom_type) == {'Polygon'}, f"The layer does not contains polygons - {op}"
1074
+ df1.to_file(output_file, engine=ENGINE)
1075
+ else:
1076
+ logging.warning("No data found for layer " + str(layer))
1077
+
1078
+ elif op == Modif_Type.COPY:
1079
+ # just copy the data if it is polygons
1080
+ if manager.is_polygons(set(df.geom_type)):
1081
+ df.to_file(output_file, engine=ENGINE)
1082
+ else:
1083
+ logging.error("The layer does not contains polygons - " + str(layer))
1084
+ else:
1085
+ raise ValueError(f"The operand {op} is not recognized")
1086
+
1087
+ return "Data modification done for " + str(layer)
1088
+ else:
1089
+ # Normally, phase 1 does not create empty files
1090
+ # But it is better to check... ;-)
1091
+ logging.error("skipped" + str(layer) + "due to no polygon in the study area")
1092
+ return "skipped" + str(layer) + "due to no polygon in the study area"
1093
+
1094
+ def compute_vulnerability(manager:Accept_Manager):
1095
+ """
1096
+ Compute the vulnerability for the Study Area
1097
+
1098
+ This function **will not modify** the data by the removed buildings/scenarios.
1099
+
1100
+ :param dirsnames: the Dirs_Names object from the calling function
1101
+ """
1102
+
1103
+ vuln_csv = Vulnerability_csv(manager.VULNERABILITY_CSV)
1104
+
1105
+ rasters_vuln = manager.get_files_in_rasters_vulne()
1106
+
1107
+ logging.info("Number of files",len(rasters_vuln))
1108
+
1109
+ ds:gdal.Dataset = gdal.OpenEx(str(rasters_vuln[0]), gdal.GA_ReadOnly, open_options=["SPARSE_OK=TRUE"])
1110
+
1111
+ tmp_vuln = ds.GetRasterBand(1)
1112
+
1113
+ # REMARK: The XSize and YSize are the number of columns and rows
1114
+ col, row = tmp_vuln.XSize, tmp_vuln.YSize
1115
+
1116
+ logging.info("Computing Vulnerability")
1117
+
1118
+ array_vuln = np.ones((row, col), dtype=np.int8)
1119
+
1120
+ # Create a JIT function to update the arrays
1121
+ # Faster than the classical Python loop or Numpy
1122
+ @nb.jit(nopython=True, boundscheck=False, inline='always')
1123
+ def update_arrays_jit(tmp_vuln, array_vuln):
1124
+ for i in range(tmp_vuln.shape[0]):
1125
+ for j in range(tmp_vuln.shape[1]):
1126
+ if tmp_vuln[i, j] >= array_vuln[i, j]:
1127
+ array_vuln[i, j] = tmp_vuln[i, j]
1128
+
1129
+ return array_vuln
1130
+
1131
+ @nb.jit(nopython=True, boundscheck=False, inline='always')
1132
+ def update_arrays_jit_csr(row, col, locvuln, array_vuln):
1133
+ for k in range(len(row)-1):
1134
+ i = k
1135
+ j1 = row[k]
1136
+ j2 = row[k+1]
1137
+ for j in col[j1:j2]:
1138
+ if locvuln >= array_vuln[i, j]:
1139
+ array_vuln[i, j] = locvuln
1140
+
1141
+ return array_vuln
1142
+
1143
+ for i in tqdm(range(len(rasters_vuln)), 'Computing Vulnerability : '):
1144
+ logging.info("Computing layer {} / {}".format(i, len(rasters_vuln)))
1145
+
1146
+ locvuln = vuln_csv.get_vulnerability_level(rasters_vuln[i].stem)
1147
+
1148
+ if locvuln == 1:
1149
+ logging.info("No need to apply the matrice, the vulnerability is 1 which is the lower value")
1150
+ continue
1151
+
1152
+ if rasters_vuln[i].with_suffix('.npz').exists():
1153
+ ij_npz = np.load(rasters_vuln[i].with_suffix('.npz'))
1154
+ ii = ij_npz['row']
1155
+ jj = ij_npz['col']
1156
+ # We use the jit
1157
+ update_arrays_jit_csr(ii, jj, locvuln, array_vuln)
1158
+
1159
+ else:
1160
+ ds = gdal.OpenEx(str(rasters_vuln[i]), open_options=["SPARSE_OK=TRUE"])
1161
+ tmp_vuln = ds.GetRasterBand(1).ReadAsArray()
1162
+ # We use the jit
1163
+ update_arrays_jit(tmp_vuln, array_vuln)
1164
+
1165
+ logging.info("Saving the computed vulnerability")
1166
+ dst_filename= str(manager.SA_VULN)
1167
+ y_pixels, x_pixels = array_vuln.shape # number of pixels in x
1168
+
1169
+ driver = gdal.GetDriverByName('GTiff')
1170
+ dataset = driver.Create(dst_filename,
1171
+ x_pixels, y_pixels,
1172
+ gdal.GDT_Byte,
1173
+ 1,
1174
+ options=["COMPRESS=LZW"])
1175
+
1176
+ dataset.GetRasterBand(1).WriteArray(array_vuln.astype(np.int8))
1177
+ # follow code is adding GeoTranform and Projection
1178
+ geotrans = ds.GetGeoTransform() # get GeoTranform from existed 'data0'
1179
+ proj = ds.GetProjection() # you can get from a exsited tif or import
1180
+ dataset.SetGeoTransform(geotrans)
1181
+ dataset.SetProjection(proj)
1182
+ dataset.FlushCache()
1183
+ dataset = None
1184
+
1185
+ logging.info("Computed Vulnerability for the Study Area - Done")
1186
+
1187
+ def compute_code(manager:Accept_Manager):
1188
+ """
1189
+ Compute the code for the Study Area
1190
+
1191
+ This function **will not modify** the data by the removed buildings/scenarios.
1192
+
1193
+ :param dirsnames: the Dirs_Names object from the calling function
1194
+ """
1195
+
1196
+ vuln_csv = Vulnerability_csv(manager.VULNERABILITY_CSV)
1197
+
1198
+ rasters_code = manager.get_files_in_rasters_code()
1199
+
1200
+ logging.info("Number of files",len(rasters_code))
1201
+
1202
+ ds:gdal.Dataset = gdal.OpenEx(str(rasters_code[0]), gdal.GA_ReadOnly, open_options=["SPARSE_OK=TRUE"])
1203
+
1204
+ tmp_code = ds.GetRasterBand(1)
1205
+
1206
+ # REMARK: The XSize and YSize are the number of columns and rows
1207
+ col, row = tmp_code.XSize, tmp_code.YSize
1208
+
1209
+ logging.info("Computing Code")
1210
+
1211
+ array_code = np.ones((row, col), dtype=np.int8)
1212
+
1213
+ # Create a JIT function to update the arrays
1214
+ # Faster than the classical Python loop or Numpy
1215
+ @nb.jit(nopython=True, boundscheck=False, inline='always')
1216
+ def update_arrays_jit(tmp_code, loccode, array_code):
1217
+ for i in range(tmp_code.shape[0]):
1218
+ for j in range(tmp_code.shape[1]):
1219
+ if tmp_code[i, j] >= array_code[i, j]:
1220
+ array_code[i, j] = loccode
1221
+
1222
+ return array_code
1223
+
1224
+ @nb.jit(nopython=True, boundscheck=False, inline='always')
1225
+ def update_arrays_jit_csr(row, col, loccode, array_code):
1226
+ for k in range(len(row)-1):
1227
+ i = k
1228
+ j1 = row[k]
1229
+ j2 = row[k+1]
1230
+ for j in col[j1:j2]:
1231
+ if loccode >= array_code[i, j]:
1232
+ array_code[i, j] = loccode
1233
+
1234
+ return array_code
1235
+
1236
+ for i in tqdm(range(len(rasters_code)), 'Computing Code : '):
1237
+ logging.info("Computing layer {} / {}".format(i, len(rasters_code)))
1238
+
1239
+ loccode = vuln_csv.get_vulnerability_code(rasters_code[i].stem)
1240
+
1241
+ if rasters_code[i].with_suffix('.npz').exists():
1242
+ ij_npz = np.load(rasters_code[i].with_suffix('.npz'))
1243
+ ii = ij_npz['row']
1244
+ jj = ij_npz['col']
1245
+ # We use the jit
1246
+ update_arrays_jit_csr(ii, jj, loccode, array_code)
1247
+
1248
+ else:
1249
+ ds = gdal.OpenEx(str(rasters_code[i]), open_options=["SPARSE_OK=TRUE"])
1250
+ tmp_code = ds.GetRasterBand(1).ReadAsArray()
1251
+ # We use the jit
1252
+ update_arrays_jit(tmp_code, loccode, array_code)
1253
+
1254
+ logging.info("Saving the computed codes")
1255
+ dst_filename= str(manager.SA_CODE)
1256
+ y_pixels, x_pixels = array_code.shape # number of pixels in x
1257
+ driver = gdal.GetDriverByName('GTiff')
1258
+ dataset = driver.Create(dst_filename,
1259
+ x_pixels, y_pixels,
1260
+ gdal.GDT_Byte,
1261
+ 1,
1262
+ options=["COMPRESS=LZW"])
1263
+
1264
+ dataset.GetRasterBand(1).WriteArray(array_code.astype(np.int8))
1265
+ # follow code is adding GeoTranform and Projection
1266
+ geotrans = ds.GetGeoTransform() # get GeoTranform from existed 'data0'
1267
+ proj = ds.GetProjection() # you can get from a exsited tif or import
1268
+ dataset.SetGeoTransform(geotrans)
1269
+ dataset.SetProjection(proj)
1270
+ dataset.FlushCache()
1271
+ dataset = None
1272
+
1273
+ logging.info("Computed Code for the Study Area - Done")
1274
+
1275
+ def compute_vulnerability4scenario(manager:Accept_Manager):
1276
+ """ Compute the vulnerability for the scenario
1277
+
1278
+ This function **will modify** the data by the removed buildings/scenarios.
1279
+
1280
+ FIXME: It could be interseting to permit the user to provide tiff files for the removed buildings and other scenarios.
1281
+
1282
+ :param dirsnames: the Dirs_Names object from the calling function
1283
+ """
1284
+
1285
+ array_vuln = gdal.Open(str(manager.SA_VULN))
1286
+ geotrans = array_vuln.GetGeoTransform() # get GeoTranform from existed 'data0'
1287
+ proj = array_vuln.GetProjection() # you can get from a exsited tif or import
1288
+
1289
+ array_vuln = np.array(array_vuln.GetRasterBand(1).ReadAsArray())
1290
+
1291
+ array_code = gdal.Open(str(manager.SA_CODE))
1292
+ array_code = np.array(array_code.GetRasterBand(1).ReadAsArray())
1293
+
1294
+ Rbu = manager.get_files_in_rm_buildings()
1295
+
1296
+ if len(Rbu)>0:
1297
+ for curfile in Rbu:
1298
+ array_mod = gdal.Open(str(curfile))
1299
+ array_mod = np.array(array_mod.GetRasterBand(1).ReadAsArray())
1300
+
1301
+ ij = np.argwhere(array_mod == 1)
1302
+ array_vuln[ij[:,0], ij[:,1]] = 1
1303
+ array_code[ij[:,0], ij[:,1]] = 1
1304
+
1305
+ dst_filename= str(manager.TMP_VULN)
1306
+ y_pixels, x_pixels = array_vuln.shape # number of pixels in x
1307
+
1308
+ driver = gdal.GetDriverByName('GTiff')
1309
+ dataset = driver.Create(dst_filename, x_pixels, y_pixels, gdal.GDT_Byte, 1, options=["COMPRESS=LZW"])
1310
+ dataset.GetRasterBand(1).WriteArray(array_vuln.astype(np.int8))
1311
+ # follow code is adding GeoTranform and Projection
1312
+ dataset.SetGeoTransform(geotrans)
1313
+ dataset.SetProjection(proj)
1314
+ dataset.FlushCache()
1315
+ dataset = None
1316
+
1317
+
1318
+ dst_filename= str(manager.TMP_CODE)
1319
+ y_pixels, x_pixels = array_code.shape # number of pixels in x
1320
+ driver = gdal.GetDriverByName('GTiff')
1321
+ dataset = driver.Create(dst_filename, x_pixels, y_pixels, gdal.GDT_Byte, 1, options=["COMPRESS=LZW"])
1322
+ dataset.GetRasterBand(1).WriteArray(array_code.astype(np.int8))
1323
+ # follow code is adding GeoTranform and Projection
1324
+ dataset.SetGeoTransform(geotrans)
1325
+ dataset.SetProjection(proj)
1326
+ dataset.FlushCache()
1327
+ dataset = None
1328
+
1329
+ logging.info("Computed Vulnerability and code for the scenario")
1330
+
1331
+ def match_vulnerability2sim(inRas:Path, outRas:Path, MODREC:Path):
1332
+ """
1333
+ Clip the raster to the MODREC/simulation extent
1334
+
1335
+ :param inRas: the input raster file
1336
+ :param outRas: the output raster file
1337
+ :param MODREC: the MODREC/simulation extent file
1338
+
1339
+ """
1340
+
1341
+ inRas = str(inRas)
1342
+ outRas = str(outRas)
1343
+ MODREC = str(MODREC)
1344
+
1345
+ data = gdal.Open(MODREC, gdalconst.GA_ReadOnly)
1346
+ geoTransform = data.GetGeoTransform()
1347
+ minx = geoTransform[0]
1348
+ maxy = geoTransform[3]
1349
+ maxx = minx + geoTransform[1] * data.RasterXSize
1350
+ miny = maxy + geoTransform[5] * data.RasterYSize
1351
+ ds = gdal.Open(inRas)
1352
+ ds = gdal.Translate(outRas, ds, projWin = [minx, maxy, maxx, miny])
1353
+ ds = None
1354
+
1355
+
1356
+ @nb.jit(nopython=True, boundscheck=False, inline='always')
1357
+ def update_accept(accept, model_h, ij, bounds, loc_accept):
1358
+ for idx in range(len(bounds)):
1359
+ for i,j in ij:
1360
+ if bounds[idx,0] < model_h[i,j] <= bounds[idx,1]: #lit dans wd vs Ti où on est et associe son score d'accept
1361
+ accept[i,j] = loc_accept[idx]
1362
+
1363
+ def compute_acceptability(manager:Accept_Manager,
1364
+ model_h:np.ndarray,
1365
+ vulnerability:np.ndarray,
1366
+ interval:int,
1367
+ geo_projection:tuple,
1368
+ save_to_file:bool=True) -> np.ndarray:
1369
+
1370
+ """
1371
+ Compute the local acceptability based on :
1372
+ - the vulnerability
1373
+ - the water depth
1374
+ - the matrices
1375
+
1376
+ :param manager: the Accept_Manager object from the calling function
1377
+ :param model_h: the water depth matrix
1378
+ :param vulnerability: the vulnerability matrix
1379
+ :param interval: the return period
1380
+ :param geo_projection: the geotransform and the projection - tuple extracted from another raster file
1381
+
1382
+ """
1383
+
1384
+ logging.info(interval)
1385
+
1386
+ points_accept = pd.read_csv(manager.POINTS_CSV)
1387
+
1388
+ points_accept = points_accept[points_accept["Interval"]==interval] #les wd vs Ti matrices
1389
+ points_accept = points_accept.reset_index()
1390
+
1391
+ accept = np.zeros(vulnerability.shape, dtype=np.float32)
1392
+
1393
+ bounds = np.asarray([[0., 0.02], [0.02, 0.3], [0.3, 1], [1, 2.5], [2.5, 1000]], dtype=np.float32)
1394
+
1395
+ for i in range(1,6):
1396
+ ij = np.argwhere(vulnerability == i)
1397
+
1398
+ idx_pts = 5-i
1399
+ accept_pts = [points_accept["h-0"][idx_pts],
1400
+ points_accept["h-0.02"][idx_pts],
1401
+ points_accept["h-0.3"][idx_pts],
1402
+ points_accept["h-1"][idx_pts],
1403
+ points_accept["h-2.5"][idx_pts]]
1404
+
1405
+ update_accept(accept, model_h, ij, bounds, accept_pts)
1406
+
1407
+ if save_to_file:
1408
+ #save raster
1409
+ dst_filename = str(manager.TMP_QFILES / "Q{}.tif".format(interval)) #les Qi
1410
+
1411
+ y_pixels, x_pixels = accept.shape # number of pixels in x
1412
+ driver = gdal.GetDriverByName('GTiff')
1413
+ dataset = driver.Create(dst_filename,
1414
+ x_pixels, y_pixels,
1415
+ 1,
1416
+ gdal.GDT_Float32,
1417
+ options=["COMPRESS=LZW"])
1418
+
1419
+ dataset.GetRasterBand(1).WriteArray(accept.astype(np.float32))
1420
+
1421
+ geotrans, proj = geo_projection
1422
+ dataset.SetGeoTransform(geotrans)
1423
+ dataset.SetProjection(proj)
1424
+ dataset.FlushCache()
1425
+ dataset = None
1426
+
1427
+ return accept
1428
+
1429
+ def shp_to_raster(vector_fn:str, raster_fn:str, pixel_size:float = 1., manager:Accept_Manager = None):
1430
+ """
1431
+ Convert a vector layer to a raster tiff file.
1432
+
1433
+ The raster will contain only 2 values : 0 and 1
1434
+
1435
+ - 1 : the inside of the vector layer
1436
+ - 0 : the rest == NoData/NullValue
1437
+
1438
+ :param vector_fn: the path to the vector file
1439
+ :param raster_fn: the path to the raster file
1440
+ :param pixel_size: the pixel size of the raster
1441
+ """
1442
+
1443
+ # Force the input to be a string
1444
+ vector_fn = str(vector_fn)
1445
+ raster_fn = str(raster_fn)
1446
+
1447
+ if manager is None:
1448
+ extent_fn = vector_fn
1449
+ logging.warning("The extent file is not provided, the extent will be the same as the vector file")
1450
+ else:
1451
+ extent_fn = str(manager.SA)
1452
+ logging.info("The extent file is provided")
1453
+
1454
+ NoData_value = 0 # np.nan is not necessary a good idea
1455
+
1456
+ # Open the data sources and read the extents
1457
+ source_ds:ogr.DataSource = ogr.Open(vector_fn)
1458
+ source_layer = source_ds.GetLayer()
1459
+
1460
+ extent_ds:ogr.DataSource = ogr.Open(extent_fn)
1461
+ extent_layer = extent_ds.GetLayer()
1462
+ x_min, x_max, y_min, y_max = extent_layer.GetExtent()
1463
+
1464
+ x_min = float(int(x_min))
1465
+ x_max = float(np.ceil(x_max))
1466
+ y_min = float(int(y_min))
1467
+ y_max = float(np.ceil(y_max))
1468
+
1469
+ # Create the destination data source
1470
+ x_res = int((x_max - x_min) / pixel_size)
1471
+ y_res = int((y_max - y_min) / pixel_size)
1472
+
1473
+ target_ds = gdal.GetDriverByName('GTiff').Create(raster_fn,
1474
+ x_res, y_res,
1475
+ 1,
1476
+ gdal.GDT_Byte,
1477
+ options=["COMPRESS=LZW",
1478
+ 'SPARSE_OK=TRUE'])
1479
+
1480
+ target_ds.SetGeoTransform((x_min, pixel_size, 0, y_max, 0, -pixel_size))
1481
+ srs = osr.SpatialReference()
1482
+ srs.ImportFromEPSG(31370)
1483
+ target_ds.SetProjection(srs.ExportToWkt())
1484
+ band = target_ds.GetRasterBand(1)
1485
+ band.SetNoDataValue(NoData_value)
1486
+ # Rasterize the areas
1487
+ gdal.RasterizeLayer(target_ds,
1488
+ bands = [1],
1489
+ layer = source_layer,
1490
+ burn_values = [1],
1491
+ options=["ALL_TOUCHED=TRUE"])
1492
+ target_ds = None
1493
+ vector_fn = raster_fn = None
1494
+
1495
+ def vector_to_raster(layer:str,
1496
+ manager:Accept_Manager,
1497
+ attribute:str,
1498
+ pixel_size:float,
1499
+ convert_to_sparse:bool = True):
1500
+ """
1501
+ Convert a vector layer to a raster tiff file
1502
+
1503
+ FIXME: Test de vulerability value and return immedialty if it is 1 if attribute == "Vulne"
1504
+
1505
+ :param layer: the layer name in the GDB file
1506
+ :param vector_input: the path to the vector file
1507
+ :param extent: the path to the extent file
1508
+ :param attribute: the attribute to rasterize
1509
+ :param pixel_size: the pixel size of the raster
1510
+
1511
+ """
1512
+
1513
+ layer = str(layer)
1514
+
1515
+ vector_input = str(manager.TMP_CODEVULNE / (layer + EXTENT))
1516
+ extent = str(manager.SA)
1517
+ attribute = str(attribute)
1518
+ pixel_size = float(pixel_size)
1519
+
1520
+ out_file = manager.TMP_RASTERS / attribute / (layer + ".tiff")
1521
+
1522
+ if out_file.exists():
1523
+ os.remove(out_file)
1524
+
1525
+ out_file = str(out_file)
1526
+
1527
+ NoData_value = 0
1528
+
1529
+ extent_ds:ogr.DataSource = ogr.Open(extent)
1530
+ extent_layer = extent_ds.GetLayer()
1531
+
1532
+ x_min, x_max, y_min, y_max = extent_layer.GetExtent()
1533
+
1534
+ x_min = float(int(x_min))
1535
+ x_max = float(np.ceil(x_max))
1536
+ y_min = float(int(y_min))
1537
+ y_max = float(np.ceil(y_max))
1538
+
1539
+ # Open the data sources and read the extents
1540
+ source_ds:ogr.DataSource = ogr.Open(vector_input)
1541
+ if source_ds is None:
1542
+ logging.error(f"Could not open the data source {layer}")
1543
+ return
1544
+ source_layer = source_ds.GetLayer()
1545
+
1546
+ # Create the destination data source
1547
+ x_res = int((x_max - x_min) / pixel_size)
1548
+ y_res = int((y_max - y_min) / pixel_size)
1549
+ target_ds:gdal.Driver = gdal.GetDriverByName('GTiff').Create(out_file,
1550
+ x_res, y_res, 1,
1551
+ gdal.GDT_Byte,
1552
+ options=["COMPRESS=DEFLATE",
1553
+ 'SPARSE_OK=TRUE',])
1554
+
1555
+ target_ds.SetGeoTransform((x_min, pixel_size, 0, y_max, 0, -pixel_size))
1556
+ srs = osr.SpatialReference()
1557
+ srs.ImportFromEPSG(31370)
1558
+ target_ds.SetProjection(srs.ExportToWkt())
1559
+
1560
+ band = target_ds.GetRasterBand(1)
1561
+ band.SetNoDataValue(NoData_value)
1562
+
1563
+ # Rasterize the areas
1564
+ gdal.RasterizeLayer(target_ds, [1],
1565
+ source_layer,
1566
+ options=["ATTRIBUTE="+attribute,
1567
+ "ALL_TOUCHED=TRUE"])
1568
+
1569
+ if convert_to_sparse:
1570
+ SPARSITY_THRESHOLD = 0.02
1571
+ # Convert the raster to a npz containing the row and col of the non-null values
1572
+ array = band.ReadAsArray()
1573
+ ij = np.nonzero(array)
1574
+
1575
+ if len(ij[0]) < int(x_res * y_res * SPARSITY_THRESHOLD):
1576
+ i,j = convert_to_csr(ij[0], ij[1], y_res)
1577
+ np.savez_compressed(Path(out_file).with_suffix('.npz'), row=np.asarray(i, dtype=np.int32), col=np.asarray(j, dtype=np.int32))
1578
+ else:
1579
+ logging.info("The raster is not sparse enough to be converted to a CSR forma {}".format(layer))
1580
+
1581
+ target_ds = None
1582
+
1583
+ return 0
1584
+
1585
+ @nb.jit(nopython=True, boundscheck=False, inline='always')
1586
+ def convert_to_csr(i_indices, j_indices, num_rows):
1587
+ row_ptr = [0] * (num_rows + 1)
1588
+ col_idx = []
1589
+
1590
+ for i in range(len(i_indices)):
1591
+ row_ptr[i_indices[i] + 1] += 1
1592
+ col_idx.append(j_indices[i])
1593
+
1594
+ for i in range(1, len(row_ptr)):
1595
+ row_ptr[i] += row_ptr[i - 1]
1596
+
1597
+ return row_ptr, col_idx