flood-adapt 0.3.10__py3-none-any.whl → 0.3.12__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.
- flood_adapt/__init__.py +3 -1
- flood_adapt/adapter/fiat_adapter.py +35 -9
- flood_adapt/adapter/sfincs_adapter.py +192 -93
- flood_adapt/adapter/sfincs_offshore.py +1 -7
- flood_adapt/config/gui.py +1 -0
- flood_adapt/database_builder/database_builder.py +316 -223
- flood_adapt/dbs_classes/database.py +100 -3
- flood_adapt/dbs_classes/dbs_benefit.py +1 -0
- flood_adapt/dbs_classes/dbs_event.py +1 -0
- flood_adapt/dbs_classes/dbs_measure.py +1 -0
- flood_adapt/dbs_classes/dbs_projection.py +1 -0
- flood_adapt/dbs_classes/dbs_scenario.py +1 -0
- flood_adapt/dbs_classes/dbs_strategy.py +1 -0
- flood_adapt/dbs_classes/dbs_template.py +2 -1
- flood_adapt/flood_adapt.py +23 -1
- flood_adapt/misc/log.py +20 -12
- flood_adapt/objects/events/events.py +5 -3
- flood_adapt/objects/events/historical.py +3 -3
- flood_adapt/objects/events/hurricane.py +1 -1
- flood_adapt/objects/forcing/plotting.py +7 -34
- flood_adapt/objects/measures/measures.py +88 -66
- {flood_adapt-0.3.10.dist-info → flood_adapt-0.3.12.dist-info}/METADATA +2 -1
- {flood_adapt-0.3.10.dist-info → flood_adapt-0.3.12.dist-info}/RECORD +26 -26
- {flood_adapt-0.3.10.dist-info → flood_adapt-0.3.12.dist-info}/LICENSE +0 -0
- {flood_adapt-0.3.10.dist-info → flood_adapt-0.3.12.dist-info}/WHEEL +0 -0
- {flood_adapt-0.3.10.dist-info → flood_adapt-0.3.12.dist-info}/top_level.txt +0 -0
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import datetime
|
|
2
|
+
import logging
|
|
2
3
|
import math
|
|
3
4
|
import os
|
|
4
5
|
import shutil
|
|
6
|
+
import time
|
|
5
7
|
import warnings
|
|
6
8
|
from enum import Enum
|
|
9
|
+
from functools import wraps
|
|
7
10
|
from pathlib import Path
|
|
8
11
|
from typing import Optional, Union
|
|
9
12
|
from urllib.request import urlretrieve
|
|
@@ -80,6 +83,29 @@ from flood_adapt.objects.projections.projections import (
|
|
|
80
83
|
)
|
|
81
84
|
from flood_adapt.objects.strategies.strategies import Strategy
|
|
82
85
|
|
|
86
|
+
logger = FloodAdaptLogging.getLogger("DatabaseBuilder")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def debug_timer(func):
|
|
90
|
+
@wraps(func)
|
|
91
|
+
def wrapper(*args, **kwargs):
|
|
92
|
+
logger = FloodAdaptLogging.getLogger("DatabaseBuilder") # No forced log level
|
|
93
|
+
if logger.isEnabledFor(logging.DEBUG):
|
|
94
|
+
logger.debug(f"Started '{func.__name__}'")
|
|
95
|
+
start_time = time.perf_counter()
|
|
96
|
+
|
|
97
|
+
result = func(*args, **kwargs)
|
|
98
|
+
|
|
99
|
+
end_time = time.perf_counter()
|
|
100
|
+
elapsed = end_time - start_time
|
|
101
|
+
logger.debug(f"Finished '{func.__name__}' in {elapsed:.4f} seconds")
|
|
102
|
+
else:
|
|
103
|
+
result = func(*args, **kwargs)
|
|
104
|
+
|
|
105
|
+
return result
|
|
106
|
+
|
|
107
|
+
return wrapper
|
|
108
|
+
|
|
83
109
|
|
|
84
110
|
def path_check(str_path: str, config_path: Optional[Path] = None) -> str:
|
|
85
111
|
"""
|
|
@@ -243,32 +269,52 @@ class ConfigModel(BaseModel):
|
|
|
243
269
|
----------
|
|
244
270
|
name : str
|
|
245
271
|
The name of the site.
|
|
246
|
-
description : Optional[str], default
|
|
272
|
+
description : Optional[str], default None
|
|
247
273
|
The description of the site.
|
|
248
274
|
database_path : Optional[str], default None
|
|
249
275
|
The path to the database where all the sites are located.
|
|
250
|
-
sfincs : str
|
|
251
|
-
The SFINCS model path.
|
|
252
|
-
sfincs_offshore : Optional[str], default None
|
|
253
|
-
The offshore SFINCS model path.
|
|
254
|
-
fiat : str
|
|
255
|
-
The FIAT model path.
|
|
256
276
|
unit_system : UnitSystems
|
|
257
277
|
The unit system.
|
|
258
|
-
gui :
|
|
278
|
+
gui : GuiConfigModel
|
|
259
279
|
The GUI model representing scaling values for the layers.
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
The
|
|
264
|
-
|
|
265
|
-
The
|
|
280
|
+
infographics : Optional[bool], default True
|
|
281
|
+
Indicates if infographics are enabled.
|
|
282
|
+
fiat : str
|
|
283
|
+
The FIAT model path.
|
|
284
|
+
aggregation_areas : Optional[list[SpatialJoinModel]], default None
|
|
285
|
+
The list of aggregation area models.
|
|
286
|
+
building_footprints : Optional[SpatialJoinModel | FootprintsOptions], default FootprintsOptions.OSM
|
|
287
|
+
The building footprints model or OSM option.
|
|
288
|
+
fiat_buildings_name : Optional[str], default "buildings"
|
|
289
|
+
The name of the buildings geometry in the FIAT model.
|
|
290
|
+
fiat_roads_name : Optional[str], default "roads"
|
|
291
|
+
The name of the roads geometry in the FIAT model.
|
|
266
292
|
bfe : Optional[SpatialJoinModel], default None
|
|
267
293
|
The BFE model.
|
|
268
|
-
svi : Optional[
|
|
294
|
+
svi : Optional[SviConfigModel], default None
|
|
269
295
|
The SVI model.
|
|
270
|
-
road_width : Optional[float], default
|
|
296
|
+
road_width : Optional[float], default 5
|
|
271
297
|
The road width in meters.
|
|
298
|
+
return_periods : list[int], default []
|
|
299
|
+
The list of return periods for risk calculations.
|
|
300
|
+
floodmap_type : Optional[FloodmapType], default None
|
|
301
|
+
The type of floodmap to use.
|
|
302
|
+
references : WaterlevelReferenceModel, default WaterlevelReferenceModel(...)
|
|
303
|
+
The water level reference model.
|
|
304
|
+
sfincs_overland : FloodModel
|
|
305
|
+
The overland SFINCS model.
|
|
306
|
+
sfincs_offshore : Optional[FloodModel], default None
|
|
307
|
+
The offshore SFINCS model.
|
|
308
|
+
dem : Optional[DemModel], default None
|
|
309
|
+
The DEM model.
|
|
310
|
+
excluded_datums : list[str], default []
|
|
311
|
+
List of datums to exclude from plotting.
|
|
312
|
+
slr_scenarios : Optional[SlrScenariosModel], default None
|
|
313
|
+
The sea level rise scenarios model.
|
|
314
|
+
scs : Optional[SCSModel], default None
|
|
315
|
+
The SCS model.
|
|
316
|
+
tide_gauge : Optional[TideGaugeConfigModel], default None
|
|
317
|
+
The tide gauge model.
|
|
272
318
|
cyclones : Optional[bool], default True
|
|
273
319
|
Indicates if cyclones are enabled.
|
|
274
320
|
cyclone_basin : Optional[Basins], default None
|
|
@@ -277,8 +323,6 @@ class ConfigModel(BaseModel):
|
|
|
277
323
|
The list of observation point models.
|
|
278
324
|
probabilistic_set : Optional[str], default None
|
|
279
325
|
The probabilistic set path.
|
|
280
|
-
infographics : Optional[bool], default True
|
|
281
|
-
Indicates if infographics are enabled.
|
|
282
326
|
"""
|
|
283
327
|
|
|
284
328
|
# General
|
|
@@ -295,12 +339,13 @@ class ConfigModel(BaseModel):
|
|
|
295
339
|
building_footprints: Optional[SpatialJoinModel | FootprintsOptions] = (
|
|
296
340
|
FootprintsOptions.OSM
|
|
297
341
|
)
|
|
298
|
-
fiat_buildings_name:
|
|
342
|
+
fiat_buildings_name: str | list[str] = "buildings"
|
|
299
343
|
fiat_roads_name: Optional[str] = "roads"
|
|
300
344
|
bfe: Optional[SpatialJoinModel] = None
|
|
301
345
|
svi: Optional[SviConfigModel] = None
|
|
302
346
|
road_width: Optional[float] = 5
|
|
303
347
|
return_periods: list[int] = Field(default_factory=list)
|
|
348
|
+
floodmap_type: Optional[FloodmapType] = None
|
|
304
349
|
|
|
305
350
|
# SFINCS
|
|
306
351
|
references: WaterlevelReferenceModel = WaterlevelReferenceModel(
|
|
@@ -379,8 +424,6 @@ class ConfigModel(BaseModel):
|
|
|
379
424
|
|
|
380
425
|
|
|
381
426
|
class DatabaseBuilder:
|
|
382
|
-
logger = FloodAdaptLogging.getLogger("DatabaseBuilder")
|
|
383
|
-
|
|
384
427
|
_has_roads: bool = False
|
|
385
428
|
_aggregation_areas: Optional[list] = None
|
|
386
429
|
_probabilistic_set_name: Optional[str] = None
|
|
@@ -406,6 +449,7 @@ class DatabaseBuilder:
|
|
|
406
449
|
def static_path(self) -> Path:
|
|
407
450
|
return self.root / "static"
|
|
408
451
|
|
|
452
|
+
@debug_timer
|
|
409
453
|
def build(self, overwrite: bool = False) -> None:
|
|
410
454
|
# Check if database already exists
|
|
411
455
|
if self.root.exists() and not overwrite:
|
|
@@ -423,9 +467,7 @@ class DatabaseBuilder:
|
|
|
423
467
|
with FloodAdaptLogging.to_file(
|
|
424
468
|
file_path=self.root.joinpath("database_builder.log")
|
|
425
469
|
):
|
|
426
|
-
|
|
427
|
-
f"Creating a FloodAdapt database in '{self.root.as_posix()}'"
|
|
428
|
-
)
|
|
470
|
+
logger.info(f"Creating a FloodAdapt database in '{self.root.as_posix()}'")
|
|
429
471
|
|
|
430
472
|
# Make folder structure and read models
|
|
431
473
|
self.setup()
|
|
@@ -441,8 +483,9 @@ class DatabaseBuilder:
|
|
|
441
483
|
self.create_standard_objects()
|
|
442
484
|
|
|
443
485
|
# Save log file
|
|
444
|
-
|
|
486
|
+
logger.info("FloodAdapt database creation finished!")
|
|
445
487
|
|
|
488
|
+
@debug_timer
|
|
446
489
|
def setup(self) -> None:
|
|
447
490
|
# Create the models
|
|
448
491
|
self.make_folder_structure()
|
|
@@ -452,6 +495,7 @@ class DatabaseBuilder:
|
|
|
452
495
|
self.read_template_sfincs_overland_model()
|
|
453
496
|
self.read_template_sfincs_offshore_model()
|
|
454
497
|
|
|
498
|
+
@debug_timer
|
|
455
499
|
def set_standard_objects(self):
|
|
456
500
|
# Define name and create object
|
|
457
501
|
self._no_measures_strategy_name = "no_measures"
|
|
@@ -467,14 +511,13 @@ class DatabaseBuilder:
|
|
|
467
511
|
)
|
|
468
512
|
return std_obj
|
|
469
513
|
|
|
514
|
+
@debug_timer
|
|
470
515
|
def create_standard_objects(self):
|
|
471
516
|
with modified_environ(
|
|
472
517
|
DATABASE_ROOT=str(self.root.parent),
|
|
473
518
|
DATABASE_NAME=self.root.name,
|
|
474
519
|
):
|
|
475
|
-
|
|
476
|
-
"Creating `no measures` strategy and `current` projection."
|
|
477
|
-
)
|
|
520
|
+
logger.info("Creating `no measures` strategy and `current` projection.")
|
|
478
521
|
# Create database instance
|
|
479
522
|
db = Database(self.root.parent, self.config.name)
|
|
480
523
|
# Create no measures strategy
|
|
@@ -506,6 +549,7 @@ class DatabaseBuilder:
|
|
|
506
549
|
)
|
|
507
550
|
|
|
508
551
|
### TEMPLATE READERS ###
|
|
552
|
+
@debug_timer
|
|
509
553
|
def read_template_fiat_model(self):
|
|
510
554
|
user_provided = self._check_exists_and_absolute(self.config.fiat)
|
|
511
555
|
|
|
@@ -543,6 +587,7 @@ class DatabaseBuilder:
|
|
|
543
587
|
|
|
544
588
|
self.fiat_model = in_db
|
|
545
589
|
|
|
590
|
+
@debug_timer
|
|
546
591
|
def read_template_sfincs_overland_model(self):
|
|
547
592
|
user_provided = self._check_exists_and_absolute(
|
|
548
593
|
self.config.sfincs_overland.name
|
|
@@ -560,6 +605,7 @@ class DatabaseBuilder:
|
|
|
560
605
|
in_db.read()
|
|
561
606
|
self.sfincs_overland_model = in_db
|
|
562
607
|
|
|
608
|
+
@debug_timer
|
|
563
609
|
def read_template_sfincs_offshore_model(self):
|
|
564
610
|
if self.config.sfincs_offshore is None:
|
|
565
611
|
self.sfincs_offshore_model = None
|
|
@@ -582,6 +628,7 @@ class DatabaseBuilder:
|
|
|
582
628
|
self.sfincs_offshore_model = in_db
|
|
583
629
|
|
|
584
630
|
### FIAT ###
|
|
631
|
+
@debug_timer
|
|
585
632
|
def create_fiat_model(self) -> FiatModel:
|
|
586
633
|
fiat = FiatModel(
|
|
587
634
|
config=self.create_fiat_config(),
|
|
@@ -590,17 +637,18 @@ class DatabaseBuilder:
|
|
|
590
637
|
)
|
|
591
638
|
return fiat
|
|
592
639
|
|
|
640
|
+
@debug_timer
|
|
593
641
|
def create_risk_model(self) -> Optional[RiskModel]:
|
|
594
642
|
# Check if return periods are provided
|
|
595
643
|
if not self.config.return_periods:
|
|
596
644
|
if self._probabilistic_set_name:
|
|
597
645
|
risk = RiskModel()
|
|
598
|
-
|
|
646
|
+
logger.warning(
|
|
599
647
|
f"No return periods provided, but a probabilistic set is available. Using default return periods {risk.return_periods}."
|
|
600
648
|
)
|
|
601
649
|
return risk
|
|
602
650
|
else:
|
|
603
|
-
|
|
651
|
+
logger.warning(
|
|
604
652
|
"No return periods provided and no probabilistic set available. Risk calculations will not be performed."
|
|
605
653
|
)
|
|
606
654
|
return None
|
|
@@ -608,9 +656,10 @@ class DatabaseBuilder:
|
|
|
608
656
|
risk = RiskModel(return_periods=self.config.return_periods)
|
|
609
657
|
return risk
|
|
610
658
|
|
|
659
|
+
@debug_timer
|
|
611
660
|
def create_benefit_config(self) -> Optional[BenefitsModel]:
|
|
612
661
|
if self._probabilistic_set_name is None:
|
|
613
|
-
|
|
662
|
+
logger.warning(
|
|
614
663
|
"No probabilistic set found in the config, benefits will not be available."
|
|
615
664
|
)
|
|
616
665
|
return None
|
|
@@ -621,14 +670,10 @@ class DatabaseBuilder:
|
|
|
621
670
|
event_set=self._probabilistic_set_name,
|
|
622
671
|
)
|
|
623
672
|
|
|
673
|
+
@debug_timer
|
|
624
674
|
def create_fiat_config(self) -> FiatConfigModel:
|
|
625
675
|
# Make sure only csv objects have geometries
|
|
626
|
-
|
|
627
|
-
keep = geoms[_FIAT_COLUMNS.object_id].isin(
|
|
628
|
-
self.fiat_model.exposure.exposure_db[_FIAT_COLUMNS.object_id]
|
|
629
|
-
)
|
|
630
|
-
geoms = geoms[keep].reset_index(drop=True)
|
|
631
|
-
self.fiat_model.exposure.exposure_geoms[i] = geoms
|
|
676
|
+
self._delete_extra_geometries()
|
|
632
677
|
|
|
633
678
|
footprints = self.create_footprints()
|
|
634
679
|
if footprints is not None:
|
|
@@ -642,9 +687,16 @@ class DatabaseBuilder:
|
|
|
642
687
|
self._aggregation_areas = self.create_aggregation_areas()
|
|
643
688
|
|
|
644
689
|
roads_gpkg = self.create_roads()
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
690
|
+
|
|
691
|
+
# Get classes of non-building objects
|
|
692
|
+
non_buildings = ~self.fiat_model.exposure.exposure_db[
|
|
693
|
+
_FIAT_COLUMNS.object_id
|
|
694
|
+
].isin(self._get_fiat_building_geoms()[_FIAT_COLUMNS.object_id])
|
|
695
|
+
non_building_names = list(
|
|
696
|
+
self.fiat_model.exposure.exposure_db[_FIAT_COLUMNS.primary_object_type][
|
|
697
|
+
non_buildings
|
|
698
|
+
].unique()
|
|
699
|
+
)
|
|
648
700
|
|
|
649
701
|
# Update elevations
|
|
650
702
|
self.update_fiat_elevation()
|
|
@@ -676,11 +728,18 @@ class DatabaseBuilder:
|
|
|
676
728
|
self.fiat_model.config["exposure"]["geom"][key]
|
|
677
729
|
).name
|
|
678
730
|
self.fiat_model.config["output"]["geom"] = output_geom
|
|
731
|
+
# Make sure objects are ordered based on object id
|
|
732
|
+
self.fiat_model.exposure.exposure_db = (
|
|
733
|
+
self.fiat_model.exposure.exposure_db.sort_values(
|
|
734
|
+
by=[_FIAT_COLUMNS.object_id], ignore_index=True
|
|
735
|
+
)
|
|
736
|
+
)
|
|
679
737
|
# Update FIAT model with the new config
|
|
680
738
|
self.fiat_model.write()
|
|
681
739
|
|
|
682
740
|
return config
|
|
683
741
|
|
|
742
|
+
@debug_timer
|
|
684
743
|
def update_fiat_elevation(self):
|
|
685
744
|
"""
|
|
686
745
|
Update the ground elevations of FIAT objects based on the SFINCS ground elevation map.
|
|
@@ -691,7 +750,7 @@ class DatabaseBuilder:
|
|
|
691
750
|
dem_file = self._dem_path
|
|
692
751
|
# TODO resolve issue with double geometries in hydromt-FIAT and use update_ground_elevation method instead
|
|
693
752
|
# self.fiat_model.update_ground_elevation(dem_file, grnd_elev_unit="meters")
|
|
694
|
-
|
|
753
|
+
logger.info(
|
|
695
754
|
"Updating FIAT objects ground elevations from SFINCS ground elevation map."
|
|
696
755
|
)
|
|
697
756
|
SFINCS_units = us.UnitfulLength(
|
|
@@ -701,88 +760,60 @@ class DatabaseBuilder:
|
|
|
701
760
|
conversion_factor = SFINCS_units.convert(FIAT_units)
|
|
702
761
|
|
|
703
762
|
if not math.isclose(conversion_factor, 1):
|
|
704
|
-
|
|
763
|
+
logger.info(
|
|
705
764
|
f"Ground elevation for FIAT objects is in '{FIAT_units}', while SFINCS ground elevation is in 'meters'. Values in the exposure csv will be converted by a factor of {conversion_factor}"
|
|
706
765
|
)
|
|
707
766
|
|
|
708
767
|
exposure = self.fiat_model.exposure.exposure_db
|
|
709
768
|
dem = rxr.open_rasterio(dem_file)
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
x_points = xr.DataArray(roads["centroid"].x, dims="points")
|
|
718
|
-
y_points = xr.DataArray(roads["centroid"].y, dims="points")
|
|
719
|
-
roads["elev"] = (
|
|
720
|
-
dem.sel(x=x_points, y=y_points, band=1, method="nearest").to_numpy()
|
|
721
|
-
* conversion_factor
|
|
722
|
-
)
|
|
723
|
-
|
|
724
|
-
exposure.loc[
|
|
725
|
-
exposure[_FIAT_COLUMNS.primary_object_type] == "road",
|
|
726
|
-
_FIAT_COLUMNS.ground_floor_height,
|
|
727
|
-
] = 0
|
|
728
|
-
exposure = exposure.merge(
|
|
729
|
-
roads[[_FIAT_COLUMNS.object_id, "elev"]],
|
|
730
|
-
on=_FIAT_COLUMNS.object_id,
|
|
731
|
-
how="left",
|
|
732
|
-
)
|
|
733
|
-
exposure.loc[
|
|
734
|
-
exposure[_FIAT_COLUMNS.primary_object_type] == "road",
|
|
735
|
-
_FIAT_COLUMNS.ground_elevation,
|
|
736
|
-
] = exposure.loc[
|
|
737
|
-
exposure[_FIAT_COLUMNS.primary_object_type] == "road", "elev"
|
|
738
|
-
]
|
|
739
|
-
del exposure["elev"]
|
|
740
|
-
self.fiat_model.exposure.exposure_db = exposure
|
|
741
|
-
|
|
742
|
-
buildings = self.fiat_model.exposure.exposure_geoms[
|
|
743
|
-
self._get_fiat_building_index()
|
|
744
|
-
].to_crs(dem.spatial_ref.crs_wkt)
|
|
745
|
-
buildings["geometry"] = buildings.geometry.centroid
|
|
746
|
-
x_points = xr.DataArray(buildings["geometry"].x, dims="points")
|
|
747
|
-
y_points = xr.DataArray(buildings["geometry"].y, dims="points")
|
|
748
|
-
buildings["elev"] = (
|
|
769
|
+
|
|
770
|
+
gdf = self._get_fiat_gdf_full()
|
|
771
|
+
gdf["centroid"] = gdf.geometry.centroid
|
|
772
|
+
x_points = xr.DataArray(gdf["centroid"].x, dims="points")
|
|
773
|
+
y_points = xr.DataArray(gdf["centroid"].y, dims="points")
|
|
774
|
+
gdf["elev"] = (
|
|
749
775
|
dem.sel(x=x_points, y=y_points, band=1, method="nearest").to_numpy()
|
|
750
776
|
* conversion_factor
|
|
751
777
|
)
|
|
778
|
+
|
|
752
779
|
exposure = exposure.merge(
|
|
753
|
-
|
|
780
|
+
gdf[[_FIAT_COLUMNS.object_id, "elev"]],
|
|
754
781
|
on=_FIAT_COLUMNS.object_id,
|
|
755
782
|
how="left",
|
|
756
783
|
)
|
|
757
|
-
exposure.
|
|
758
|
-
exposure[_FIAT_COLUMNS.primary_object_type] != "road",
|
|
759
|
-
_FIAT_COLUMNS.ground_elevation,
|
|
760
|
-
] = exposure.loc[exposure[_FIAT_COLUMNS.primary_object_type] != "road", "elev"]
|
|
784
|
+
exposure[_FIAT_COLUMNS.ground_elevation] = exposure["elev"]
|
|
761
785
|
del exposure["elev"]
|
|
762
786
|
|
|
787
|
+
self.fiat_model.exposure.exposure_db = exposure
|
|
788
|
+
|
|
763
789
|
def read_damage_unit(self) -> str:
|
|
764
790
|
if self.fiat_model.exposure.damage_unit is not None:
|
|
765
791
|
return self.fiat_model.exposure.damage_unit
|
|
766
792
|
else:
|
|
767
|
-
|
|
793
|
+
logger.warning(
|
|
768
794
|
"Delft-FIAT model was missing damage units so '$' was assumed."
|
|
769
795
|
)
|
|
770
796
|
return "$"
|
|
771
797
|
|
|
798
|
+
@debug_timer
|
|
772
799
|
def read_floodmap_type(self) -> FloodmapType:
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
self.fiat_model.exposure.exposure_db[_FIAT_COLUMNS.extraction_method]
|
|
776
|
-
== "area"
|
|
777
|
-
).any():
|
|
778
|
-
return FloodmapType.water_depth
|
|
800
|
+
if self.config.floodmap_type is not None:
|
|
801
|
+
return self.config.floodmap_type
|
|
779
802
|
else:
|
|
780
|
-
|
|
803
|
+
# If there is at least on object that uses the area method, use water depths for FA calcs
|
|
804
|
+
if (
|
|
805
|
+
self.fiat_model.exposure.exposure_db[_FIAT_COLUMNS.extraction_method]
|
|
806
|
+
== "area"
|
|
807
|
+
).any():
|
|
808
|
+
return FloodmapType.water_depth
|
|
809
|
+
else:
|
|
810
|
+
return FloodmapType.water_level
|
|
781
811
|
|
|
812
|
+
@debug_timer
|
|
782
813
|
def create_roads(self) -> Optional[str]:
|
|
783
814
|
# Make sure that FIAT roads are polygons
|
|
784
815
|
if self.config.fiat_roads_name not in self.fiat_model.exposure.geom_names:
|
|
785
|
-
|
|
816
|
+
logger.warning(
|
|
786
817
|
"Road objects are not available in the FIAT model and thus would not be available in FloodAdapt."
|
|
787
818
|
)
|
|
788
819
|
# TODO check how this naming of output geoms should become more explicit!
|
|
@@ -795,7 +826,7 @@ class DatabaseBuilder:
|
|
|
795
826
|
_FIAT_COLUMNS.segment_length
|
|
796
827
|
not in self.fiat_model.exposure.exposure_db.columns
|
|
797
828
|
):
|
|
798
|
-
|
|
829
|
+
logger.warning(
|
|
799
830
|
f"'{_FIAT_COLUMNS.segment_length}' column not present in the FIAT exposure csv. Road impact infometrics cannot be produced."
|
|
800
831
|
)
|
|
801
832
|
|
|
@@ -807,16 +838,18 @@ class DatabaseBuilder:
|
|
|
807
838
|
)
|
|
808
839
|
roads = roads.to_crs(self.fiat_model.exposure.crs)
|
|
809
840
|
self.fiat_model.exposure.exposure_geoms[self._get_fiat_road_index()] = roads
|
|
810
|
-
|
|
841
|
+
logger.info(
|
|
811
842
|
f"FIAT road objects transformed from lines to polygons assuming a road width of {self.config.road_width} meters."
|
|
812
843
|
)
|
|
813
844
|
|
|
814
845
|
self._has_roads = True
|
|
815
846
|
return f"{self.config.fiat_roads_name}.gpkg"
|
|
816
847
|
|
|
848
|
+
@debug_timer
|
|
817
849
|
def create_new_developments(self) -> Optional[str]:
|
|
818
850
|
return "new_development_area.gpkg"
|
|
819
851
|
|
|
852
|
+
@debug_timer
|
|
820
853
|
def create_footprints(self) -> Optional[Path]:
|
|
821
854
|
if isinstance(self.config.building_footprints, SpatialJoinModel):
|
|
822
855
|
# Use the provided building footprints
|
|
@@ -824,7 +857,7 @@ class DatabaseBuilder:
|
|
|
824
857
|
self.config.building_footprints.file
|
|
825
858
|
)
|
|
826
859
|
|
|
827
|
-
|
|
860
|
+
logger.info(
|
|
828
861
|
f"Using building footprints from {Path(building_footprints_file).as_posix()}."
|
|
829
862
|
)
|
|
830
863
|
# Spatially join buildings and map
|
|
@@ -835,7 +868,7 @@ class DatabaseBuilder:
|
|
|
835
868
|
)
|
|
836
869
|
return path
|
|
837
870
|
elif self.config.building_footprints == FootprintsOptions.OSM:
|
|
838
|
-
|
|
871
|
+
logger.info(
|
|
839
872
|
"Building footprint data will be downloaded from Open Street Maps."
|
|
840
873
|
)
|
|
841
874
|
region = self.fiat_model.region
|
|
@@ -857,12 +890,10 @@ class DatabaseBuilder:
|
|
|
857
890
|
return path
|
|
858
891
|
# Then check if geometries are already footprints
|
|
859
892
|
elif isinstance(
|
|
860
|
-
self.
|
|
861
|
-
self._get_fiat_building_index()
|
|
862
|
-
].geometry.iloc[0],
|
|
893
|
+
self._get_fiat_building_geoms().geometry.iloc[0],
|
|
863
894
|
(Polygon, MultiPolygon),
|
|
864
895
|
):
|
|
865
|
-
|
|
896
|
+
logger.info(
|
|
866
897
|
"Building footprints are already available in the FIAT model geometry files."
|
|
867
898
|
)
|
|
868
899
|
return None
|
|
@@ -885,21 +916,20 @@ class DatabaseBuilder:
|
|
|
885
916
|
f"While 'BF_FID' column exists, building footprints file {footprints_path} not found."
|
|
886
917
|
)
|
|
887
918
|
|
|
888
|
-
|
|
889
|
-
f"Using the building footprints located at {footprints_path}."
|
|
890
|
-
)
|
|
919
|
+
logger.info(f"Using the building footprints located at {footprints_path}.")
|
|
891
920
|
return footprints_path.relative_to(self.static_path)
|
|
892
921
|
|
|
893
922
|
# Other methods
|
|
894
923
|
else:
|
|
895
|
-
|
|
924
|
+
logger.warning(
|
|
896
925
|
"No building footprints are available. Buildings will be plotted with a default shape in FloodAdapt."
|
|
897
926
|
)
|
|
898
927
|
return None
|
|
899
928
|
|
|
929
|
+
@debug_timer
|
|
900
930
|
def create_bfe(self) -> Optional[BFEModel]:
|
|
901
931
|
if self.config.bfe is None:
|
|
902
|
-
|
|
932
|
+
logger.warning(
|
|
903
933
|
"No base flood elevation provided. Elevating building relative to base flood elevation will not be possible in FloodAdapt."
|
|
904
934
|
)
|
|
905
935
|
return None
|
|
@@ -907,13 +937,13 @@ class DatabaseBuilder:
|
|
|
907
937
|
# TODO can we use hydromt-FIAT?
|
|
908
938
|
bfe_file = self._check_exists_and_absolute(self.config.bfe.file)
|
|
909
939
|
|
|
910
|
-
|
|
940
|
+
logger.info(
|
|
911
941
|
f"Using map from {Path(bfe_file).as_posix()} as base flood elevation."
|
|
912
942
|
)
|
|
913
943
|
|
|
914
944
|
# Spatially join buildings and map
|
|
915
945
|
buildings_joined, bfe = self.spatial_join(
|
|
916
|
-
self.
|
|
946
|
+
self._get_fiat_building_geoms(),
|
|
917
947
|
bfe_file,
|
|
918
948
|
self.config.bfe.field_name,
|
|
919
949
|
)
|
|
@@ -940,6 +970,7 @@ class DatabaseBuilder:
|
|
|
940
970
|
field_name=self.config.bfe.field_name,
|
|
941
971
|
)
|
|
942
972
|
|
|
973
|
+
@debug_timer
|
|
943
974
|
def create_aggregation_areas(self) -> list[AggregationModel]:
|
|
944
975
|
# TODO split this to 3 methods?
|
|
945
976
|
aggregation_areas = []
|
|
@@ -982,7 +1013,7 @@ class DatabaseBuilder:
|
|
|
982
1013
|
)
|
|
983
1014
|
aggregation_areas.append(aggr)
|
|
984
1015
|
|
|
985
|
-
|
|
1016
|
+
logger.info(
|
|
986
1017
|
f"Aggregation areas: {aggr.name} from the FIAT model are going to be used."
|
|
987
1018
|
)
|
|
988
1019
|
|
|
@@ -1002,10 +1033,9 @@ class DatabaseBuilder:
|
|
|
1002
1033
|
)
|
|
1003
1034
|
# Do spatial join of FIAT objects and aggregation areas
|
|
1004
1035
|
exposure_csv = self.fiat_model.exposure.exposure_db
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
],
|
|
1036
|
+
gdf = self._get_fiat_gdf_full()
|
|
1037
|
+
gdf_joined, aggr_areas = self.spatial_join(
|
|
1038
|
+
objects=gdf[[_FIAT_COLUMNS.object_id, "geometry"]],
|
|
1009
1039
|
layer=str(self._check_exists_and_absolute(aggr.file)),
|
|
1010
1040
|
field_name=aggr.field_name,
|
|
1011
1041
|
rename=_FIAT_COLUMNS.aggregation_label.format(name=aggr_name),
|
|
@@ -1016,7 +1046,7 @@ class DatabaseBuilder:
|
|
|
1016
1046
|
aggr_path.parent.mkdir(parents=True, exist_ok=True)
|
|
1017
1047
|
aggr_areas.to_file(aggr_path)
|
|
1018
1048
|
exposure_csv = exposure_csv.merge(
|
|
1019
|
-
|
|
1049
|
+
gdf_joined, on=_FIAT_COLUMNS.object_id, how="left"
|
|
1020
1050
|
)
|
|
1021
1051
|
self.fiat_model.exposure.exposure_db = exposure_csv
|
|
1022
1052
|
# Update spatial joins in FIAT model
|
|
@@ -1067,31 +1097,29 @@ class DatabaseBuilder:
|
|
|
1067
1097
|
aggregation_areas.append(aggr)
|
|
1068
1098
|
|
|
1069
1099
|
# Add column in FIAT
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
],
|
|
1100
|
+
gdf = self._get_fiat_gdf_full()
|
|
1101
|
+
gdf_joined, aggr_areas = self.spatial_join(
|
|
1102
|
+
objects=gdf[[_FIAT_COLUMNS.object_id, "geometry"]],
|
|
1074
1103
|
layer=region,
|
|
1075
1104
|
field_name="aggr_id",
|
|
1076
1105
|
rename=_FIAT_COLUMNS.aggregation_label.format(name="region"),
|
|
1077
1106
|
)
|
|
1078
1107
|
exposure_csv = exposure_csv.merge(
|
|
1079
|
-
|
|
1108
|
+
gdf_joined, on=_FIAT_COLUMNS.object_id, how="left"
|
|
1080
1109
|
)
|
|
1081
1110
|
self.fiat_model.exposure.exposure_db = exposure_csv
|
|
1082
|
-
|
|
1111
|
+
logger.warning(
|
|
1083
1112
|
"No aggregation areas were available in the FIAT model and none were provided in the config file. The region file will be used as a mock aggregation area."
|
|
1084
1113
|
)
|
|
1085
1114
|
return aggregation_areas
|
|
1086
1115
|
|
|
1116
|
+
@debug_timer
|
|
1087
1117
|
def create_svi(self) -> Optional[SVIModel]:
|
|
1088
1118
|
if self.config.svi:
|
|
1089
1119
|
svi_file = self._check_exists_and_absolute(self.config.svi.file)
|
|
1090
1120
|
exposure_csv = self.fiat_model.exposure.exposure_db
|
|
1091
1121
|
buildings_joined, svi = self.spatial_join(
|
|
1092
|
-
self.
|
|
1093
|
-
self._get_fiat_building_index()
|
|
1094
|
-
],
|
|
1122
|
+
self._get_fiat_building_geoms(),
|
|
1095
1123
|
svi_file,
|
|
1096
1124
|
self.config.svi.field_name,
|
|
1097
1125
|
rename="SVI",
|
|
@@ -1099,12 +1127,12 @@ class DatabaseBuilder:
|
|
|
1099
1127
|
)
|
|
1100
1128
|
# Add column to exposure
|
|
1101
1129
|
if "SVI" in exposure_csv.columns:
|
|
1102
|
-
|
|
1130
|
+
logger.info(
|
|
1103
1131
|
f"'SVI' column in the FIAT exposure csv will be replaced by {svi_file.as_posix()}."
|
|
1104
1132
|
)
|
|
1105
1133
|
del exposure_csv["SVI"]
|
|
1106
1134
|
else:
|
|
1107
|
-
|
|
1135
|
+
logger.info(
|
|
1108
1136
|
f"'SVI' column in the FIAT exposure csv will be filled by {svi_file.as_posix()}."
|
|
1109
1137
|
)
|
|
1110
1138
|
exposure_csv = exposure_csv.merge(
|
|
@@ -1116,7 +1144,7 @@ class DatabaseBuilder:
|
|
|
1116
1144
|
svi_path = self.static_path / "templates" / "fiat" / "svi" / "svi.gpkg"
|
|
1117
1145
|
svi_path.parent.mkdir(parents=True, exist_ok=True)
|
|
1118
1146
|
svi.to_file(svi_path)
|
|
1119
|
-
|
|
1147
|
+
logger.info(
|
|
1120
1148
|
f"An SVI map can be shown in FloodAdapt GUI using '{self.config.svi.field_name}' column from {svi_file.as_posix()}"
|
|
1121
1149
|
)
|
|
1122
1150
|
|
|
@@ -1125,19 +1153,17 @@ class DatabaseBuilder:
|
|
|
1125
1153
|
field_name="SVI",
|
|
1126
1154
|
)
|
|
1127
1155
|
elif "SVI" in self.fiat_model.exposure.exposure_db.columns:
|
|
1128
|
-
|
|
1156
|
+
logger.info(
|
|
1129
1157
|
"'SVI' column present in the FIAT exposure csv. Vulnerability type infometrics can be produced."
|
|
1130
1158
|
)
|
|
1131
1159
|
add_attrs = self.fiat_model.spatial_joins["additional_attributes"]
|
|
1132
1160
|
if "SVI" not in [attr["name"] for attr in add_attrs]:
|
|
1133
|
-
|
|
1134
|
-
"No SVI map found to display in the FloodAdapt GUI!"
|
|
1135
|
-
)
|
|
1161
|
+
logger.warning("No SVI map found to display in the FloodAdapt GUI!")
|
|
1136
1162
|
|
|
1137
1163
|
ind = [attr["name"] for attr in add_attrs].index("SVI")
|
|
1138
1164
|
svi = add_attrs[ind]
|
|
1139
1165
|
svi_path = self.static_path / "templates" / "fiat" / svi["file"]
|
|
1140
|
-
|
|
1166
|
+
logger.info(
|
|
1141
1167
|
f"An SVI map can be shown in FloodAdapt GUI using '{svi['field_name']}' column from {svi['file']}"
|
|
1142
1168
|
)
|
|
1143
1169
|
# Save site attributes
|
|
@@ -1147,12 +1173,13 @@ class DatabaseBuilder:
|
|
|
1147
1173
|
)
|
|
1148
1174
|
|
|
1149
1175
|
else:
|
|
1150
|
-
|
|
1176
|
+
logger.warning(
|
|
1151
1177
|
"'SVI' column not present in the FIAT exposure csv. Vulnerability type infometrics cannot be produced."
|
|
1152
1178
|
)
|
|
1153
1179
|
return None
|
|
1154
1180
|
|
|
1155
1181
|
### SFINCS ###
|
|
1182
|
+
@debug_timer
|
|
1156
1183
|
def create_sfincs_config(self) -> SfincsModel:
|
|
1157
1184
|
# call these functions before others to make sure water level references are updated
|
|
1158
1185
|
config = self.create_sfincs_model_config()
|
|
@@ -1172,9 +1199,10 @@ class DatabaseBuilder:
|
|
|
1172
1199
|
|
|
1173
1200
|
return sfincs
|
|
1174
1201
|
|
|
1202
|
+
@debug_timer
|
|
1175
1203
|
def create_cyclone_track_database(self) -> Optional[CycloneTrackDatabaseModel]:
|
|
1176
1204
|
if not self.config.cyclones or not self.config.sfincs_offshore:
|
|
1177
|
-
|
|
1205
|
+
logger.warning("No cyclones will be available in the database.")
|
|
1178
1206
|
return None
|
|
1179
1207
|
|
|
1180
1208
|
if self.config.cyclone_basin:
|
|
@@ -1184,7 +1212,7 @@ class DatabaseBuilder:
|
|
|
1184
1212
|
|
|
1185
1213
|
name = f"IBTrACS.{basin.value}.v04r01.nc"
|
|
1186
1214
|
url = f"https://www.ncei.noaa.gov/data/international-best-track-archive-for-climate-stewardship-ibtracs/v04r01/access/netcdf/{name}"
|
|
1187
|
-
|
|
1215
|
+
logger.info(f"Downloading cyclone track database from {url}")
|
|
1188
1216
|
fn = Path(self.root) / "static" / "cyclone_track_database" / name
|
|
1189
1217
|
fn.parent.mkdir(parents=True, exist_ok=True)
|
|
1190
1218
|
|
|
@@ -1195,6 +1223,7 @@ class DatabaseBuilder:
|
|
|
1195
1223
|
|
|
1196
1224
|
return CycloneTrackDatabaseModel(file=name)
|
|
1197
1225
|
|
|
1226
|
+
@debug_timer
|
|
1198
1227
|
def create_scs_model(self) -> Optional[SCSModel]:
|
|
1199
1228
|
if self.config.scs is None:
|
|
1200
1229
|
return None
|
|
@@ -1205,11 +1234,12 @@ class DatabaseBuilder:
|
|
|
1205
1234
|
|
|
1206
1235
|
return SCSModel(file=scs_file.name, type=self.config.scs.type)
|
|
1207
1236
|
|
|
1237
|
+
@debug_timer
|
|
1208
1238
|
def create_dem_model(self) -> DemModel:
|
|
1209
1239
|
if self.config.dem:
|
|
1210
1240
|
subgrid_sfincs = Path(self.config.dem.filename)
|
|
1211
1241
|
else:
|
|
1212
|
-
|
|
1242
|
+
logger.warning(
|
|
1213
1243
|
"No subgrid depth geotiff file provided in the config file. Using the one from the SFINCS model."
|
|
1214
1244
|
)
|
|
1215
1245
|
subgrid_sfincs = (
|
|
@@ -1227,7 +1257,7 @@ class DatabaseBuilder:
|
|
|
1227
1257
|
shutil.move(tiles_sfincs, fa_tiles_path)
|
|
1228
1258
|
if (fa_tiles_path / "index").exists():
|
|
1229
1259
|
os.rename(fa_tiles_path / "index", fa_tiles_path / "indices")
|
|
1230
|
-
|
|
1260
|
+
logger.info(
|
|
1231
1261
|
"Tiles were already available in the SFINCS model and will directly be used in FloodAdapt."
|
|
1232
1262
|
)
|
|
1233
1263
|
else:
|
|
@@ -1239,7 +1269,7 @@ class DatabaseBuilder:
|
|
|
1239
1269
|
zoom_range=[0, 13],
|
|
1240
1270
|
fmt="png",
|
|
1241
1271
|
)
|
|
1242
|
-
|
|
1272
|
+
logger.info(
|
|
1243
1273
|
f"Tiles were created using the {subgrid_sfincs.as_posix()} as the elevation map."
|
|
1244
1274
|
)
|
|
1245
1275
|
|
|
@@ -1249,6 +1279,7 @@ class DatabaseBuilder:
|
|
|
1249
1279
|
filename=fa_subgrid_path.name, units=us.UnitTypesLength.meters
|
|
1250
1280
|
) # always in meters
|
|
1251
1281
|
|
|
1282
|
+
@debug_timer
|
|
1252
1283
|
def create_sfincs_model_config(self) -> SfincsConfigModel:
|
|
1253
1284
|
config = SfincsConfigModel(
|
|
1254
1285
|
csname=self.sfincs_overland_model.crs.name,
|
|
@@ -1263,6 +1294,7 @@ class DatabaseBuilder:
|
|
|
1263
1294
|
|
|
1264
1295
|
return config
|
|
1265
1296
|
|
|
1297
|
+
@debug_timer
|
|
1266
1298
|
def create_slr(self) -> Optional[SlrScenariosModel]:
|
|
1267
1299
|
if self.config.slr_scenarios is None:
|
|
1268
1300
|
return None
|
|
@@ -1280,17 +1312,19 @@ class DatabaseBuilder:
|
|
|
1280
1312
|
relative_to_year=self.config.slr_scenarios.relative_to_year,
|
|
1281
1313
|
)
|
|
1282
1314
|
|
|
1315
|
+
@debug_timer
|
|
1283
1316
|
def create_observation_points(self) -> Union[list[ObsPointModel], None]:
|
|
1284
1317
|
if self.config.obs_point is None:
|
|
1285
1318
|
return None
|
|
1286
1319
|
|
|
1287
|
-
|
|
1320
|
+
logger.info("Observation points were provided in the config file.")
|
|
1288
1321
|
return self.config.obs_point
|
|
1289
1322
|
|
|
1323
|
+
@debug_timer
|
|
1290
1324
|
def create_rivers(self) -> list[RiverModel]:
|
|
1291
1325
|
src_file = Path(self.sfincs_overland_model.root) / "sfincs.src"
|
|
1292
1326
|
if not src_file.exists():
|
|
1293
|
-
|
|
1327
|
+
logger.warning("No rivers found in the SFINCS model.")
|
|
1294
1328
|
return []
|
|
1295
1329
|
|
|
1296
1330
|
df = pd.read_csv(src_file, delim_whitespace=True, header=None, names=["x", "y"])
|
|
@@ -1310,7 +1344,7 @@ class DatabaseBuilder:
|
|
|
1310
1344
|
)
|
|
1311
1345
|
else:
|
|
1312
1346
|
discharge = 0
|
|
1313
|
-
|
|
1347
|
+
logger.warning(
|
|
1314
1348
|
f"No river discharge conditions were found in the SFINCS model for river {idx}. A default value of 0 will be used."
|
|
1315
1349
|
)
|
|
1316
1350
|
|
|
@@ -1324,18 +1358,19 @@ class DatabaseBuilder:
|
|
|
1324
1358
|
)
|
|
1325
1359
|
rivers.append(river)
|
|
1326
1360
|
|
|
1327
|
-
|
|
1361
|
+
logger.info(
|
|
1328
1362
|
f"{len(river_locs)} river(s) were identified from the SFINCS model and will be available in FloodAdapt for discharge input."
|
|
1329
1363
|
)
|
|
1330
1364
|
|
|
1331
1365
|
return rivers
|
|
1332
1366
|
|
|
1367
|
+
@debug_timer
|
|
1333
1368
|
def create_tide_gauge(self) -> Optional[TideGauge]:
|
|
1334
1369
|
if self.config.tide_gauge is None:
|
|
1335
|
-
|
|
1370
|
+
logger.warning(
|
|
1336
1371
|
"Tide gauge information not provided. Historical events will not have an option to use gauged data in FloodAdapt!"
|
|
1337
1372
|
)
|
|
1338
|
-
|
|
1373
|
+
logger.warning(
|
|
1339
1374
|
"No water level references were found. It is assumed that MSL is equal to the datum used in the SFINCS overland model. You can provide these values with the tide_gauge.ref attribute in the site.toml."
|
|
1340
1375
|
)
|
|
1341
1376
|
return None
|
|
@@ -1346,7 +1381,7 @@ class DatabaseBuilder:
|
|
|
1346
1381
|
"Tide gauge file needs to be provided when 'file' is selected as the source."
|
|
1347
1382
|
)
|
|
1348
1383
|
if self.config.tide_gauge.ref is None:
|
|
1349
|
-
|
|
1384
|
+
logger.warning(
|
|
1350
1385
|
"Tide gauge reference not provided. MSL is assumed as the reference of the water levels in the file."
|
|
1351
1386
|
)
|
|
1352
1387
|
self.config.tide_gauge.ref = "MSL"
|
|
@@ -1360,7 +1395,7 @@ class DatabaseBuilder:
|
|
|
1360
1395
|
shutil.copyfile(self.config.tide_gauge.file, db_file_path)
|
|
1361
1396
|
|
|
1362
1397
|
rel_db_path = Path(db_file_path.relative_to(self.static_path))
|
|
1363
|
-
|
|
1398
|
+
logger.warning(
|
|
1364
1399
|
f"Tide gauge from file {rel_db_path} assumed to be in {self.unit_system.default_length_units}!"
|
|
1365
1400
|
)
|
|
1366
1401
|
tide_gauge = TideGauge(
|
|
@@ -1387,12 +1422,12 @@ class DatabaseBuilder:
|
|
|
1387
1422
|
|
|
1388
1423
|
if self.config.tide_gauge.id is None:
|
|
1389
1424
|
station_id = self._get_closest_station()
|
|
1390
|
-
|
|
1425
|
+
logger.info(
|
|
1391
1426
|
"The closest NOAA tide gauge station to the site will be searched."
|
|
1392
1427
|
)
|
|
1393
1428
|
else:
|
|
1394
1429
|
station_id = self.config.tide_gauge.id
|
|
1395
|
-
|
|
1430
|
+
logger.info(
|
|
1396
1431
|
f"The NOAA tide gauge station with the provided ID {station_id} will be used."
|
|
1397
1432
|
)
|
|
1398
1433
|
station = self._get_station_metadata(station_id=station_id, ref=ref)
|
|
@@ -1448,11 +1483,12 @@ class DatabaseBuilder:
|
|
|
1448
1483
|
self.water_level_references.datums.append(wl_info)
|
|
1449
1484
|
return tide_gauge
|
|
1450
1485
|
else:
|
|
1451
|
-
|
|
1486
|
+
logger.warning(
|
|
1452
1487
|
f"Tide gauge source not recognized: {self.config.tide_gauge.source}. Historical events will not have an option to use gauged data in FloodAdapt!"
|
|
1453
1488
|
)
|
|
1454
1489
|
return None
|
|
1455
1490
|
|
|
1491
|
+
@debug_timer
|
|
1456
1492
|
def create_offshore_model(self) -> Optional[FloodModel]:
|
|
1457
1493
|
if self.sfincs_offshore_model is None:
|
|
1458
1494
|
return None
|
|
@@ -1477,7 +1513,7 @@ class DatabaseBuilder:
|
|
|
1477
1513
|
index=False,
|
|
1478
1514
|
header=False,
|
|
1479
1515
|
)
|
|
1480
|
-
|
|
1516
|
+
logger.info(
|
|
1481
1517
|
"Output points of the offshore SFINCS model were reconfigured to the boundary points of the overland SFINCS model."
|
|
1482
1518
|
)
|
|
1483
1519
|
|
|
@@ -1487,6 +1523,7 @@ class DatabaseBuilder:
|
|
|
1487
1523
|
vertical_offset=self.config.sfincs_offshore.vertical_offset,
|
|
1488
1524
|
)
|
|
1489
1525
|
|
|
1526
|
+
@debug_timer
|
|
1490
1527
|
def create_overland_model(self) -> FloodModel:
|
|
1491
1528
|
return FloodModel(
|
|
1492
1529
|
name="overland",
|
|
@@ -1494,6 +1531,7 @@ class DatabaseBuilder:
|
|
|
1494
1531
|
)
|
|
1495
1532
|
|
|
1496
1533
|
### SITE ###
|
|
1534
|
+
@debug_timer
|
|
1497
1535
|
def create_site_config(self) -> Site:
|
|
1498
1536
|
"""Create the site configuration for the FloodAdapt model.
|
|
1499
1537
|
|
|
@@ -1533,18 +1571,16 @@ class DatabaseBuilder:
|
|
|
1533
1571
|
)
|
|
1534
1572
|
return config
|
|
1535
1573
|
|
|
1574
|
+
@debug_timer
|
|
1536
1575
|
def read_location(self) -> tuple[float, float]:
|
|
1537
1576
|
# Get center of area of interest
|
|
1538
1577
|
if not self.fiat_model.region.empty:
|
|
1539
1578
|
center = self.fiat_model.region.dissolve().centroid.to_crs(4326)[0]
|
|
1540
1579
|
else:
|
|
1541
|
-
center = (
|
|
1542
|
-
self.fiat_model.exposure.exposure_geoms[self._get_fiat_building_index()]
|
|
1543
|
-
.dissolve()
|
|
1544
|
-
.centroid.to_crs(4326)[0]
|
|
1545
|
-
)
|
|
1580
|
+
center = self._get_fiat_building_geoms().dissolve().centroid.to_crs(4326)[0]
|
|
1546
1581
|
return center.x, center.y
|
|
1547
1582
|
|
|
1583
|
+
@debug_timer
|
|
1548
1584
|
def create_gui_config(self) -> GuiModel:
|
|
1549
1585
|
gui = GuiModel(
|
|
1550
1586
|
units=self.unit_system,
|
|
@@ -1555,6 +1591,7 @@ class DatabaseBuilder:
|
|
|
1555
1591
|
|
|
1556
1592
|
return gui
|
|
1557
1593
|
|
|
1594
|
+
@debug_timer
|
|
1558
1595
|
def create_default_units(self) -> GuiUnitModel:
|
|
1559
1596
|
if self.config.unit_system == UnitSystems.imperial:
|
|
1560
1597
|
return GuiUnitModel.imperial()
|
|
@@ -1565,6 +1602,7 @@ class DatabaseBuilder:
|
|
|
1565
1602
|
f"Unit system {self.config.unit_system} not recognized. Please choose 'imperial' or 'metric'."
|
|
1566
1603
|
)
|
|
1567
1604
|
|
|
1605
|
+
@debug_timer
|
|
1568
1606
|
def create_visualization_layers(self) -> VisualizationLayers:
|
|
1569
1607
|
visualization_layers = VisualizationLayers()
|
|
1570
1608
|
if self._svi is not None:
|
|
@@ -1578,6 +1616,7 @@ class DatabaseBuilder:
|
|
|
1578
1616
|
)
|
|
1579
1617
|
return visualization_layers
|
|
1580
1618
|
|
|
1619
|
+
@debug_timer
|
|
1581
1620
|
def create_output_layers_config(self) -> OutputLayers:
|
|
1582
1621
|
# Read default colors from template
|
|
1583
1622
|
fd_max = self.config.gui.max_flood_depth
|
|
@@ -1633,6 +1672,7 @@ class DatabaseBuilder:
|
|
|
1633
1672
|
)
|
|
1634
1673
|
return output_layers
|
|
1635
1674
|
|
|
1675
|
+
@debug_timer
|
|
1636
1676
|
def create_hazard_plotting_config(self) -> PlottingModel:
|
|
1637
1677
|
datum_names = [datum.name for datum in self.water_level_references.datums]
|
|
1638
1678
|
if "MHHW" in datum_names:
|
|
@@ -1640,20 +1680,20 @@ class DatabaseBuilder:
|
|
|
1640
1680
|
self.water_level_references.get_datum("MHHW").height
|
|
1641
1681
|
- self.water_level_references.get_datum("MSL").height
|
|
1642
1682
|
)
|
|
1643
|
-
|
|
1683
|
+
logger.info(
|
|
1644
1684
|
f"The default tidal amplitude in the GUI will be {amplitude.transform(self.unit_system.default_length_units)}, calculated as the difference between MHHW and MSL from the tide gauge data."
|
|
1645
1685
|
)
|
|
1646
1686
|
else:
|
|
1647
1687
|
amplitude = us.UnitfulLength(
|
|
1648
1688
|
value=0.0, units=self.unit_system.default_length_units
|
|
1649
1689
|
)
|
|
1650
|
-
|
|
1690
|
+
logger.warning(
|
|
1651
1691
|
"The default tidal amplitude in the GUI will be 0.0, since no tide-gauge water levels are available. You can change this in the site.toml with the 'gui.tide_harmonic_amplitude' attribute."
|
|
1652
1692
|
)
|
|
1653
1693
|
|
|
1654
1694
|
ref = "MSL"
|
|
1655
1695
|
if ref not in datum_names:
|
|
1656
|
-
|
|
1696
|
+
logger.warning(
|
|
1657
1697
|
f"The Mean Sea Level (MSL) datum is not available in the site.toml. The synthetic tide will be created relative to the main reference: {self.water_level_references.reference}."
|
|
1658
1698
|
)
|
|
1659
1699
|
ref = self.water_level_references.reference
|
|
@@ -1668,6 +1708,7 @@ class DatabaseBuilder:
|
|
|
1668
1708
|
|
|
1669
1709
|
return plotting
|
|
1670
1710
|
|
|
1711
|
+
@debug_timer
|
|
1671
1712
|
def create_infometrics(self):
|
|
1672
1713
|
"""
|
|
1673
1714
|
Copy the infometrics and infographics templates to the appropriate location and modifies the metrics_config.toml files.
|
|
@@ -1728,6 +1769,7 @@ class DatabaseBuilder:
|
|
|
1728
1769
|
with open(file, "wb") as f:
|
|
1729
1770
|
tomli_w.dump(attrs, f)
|
|
1730
1771
|
|
|
1772
|
+
@debug_timer
|
|
1731
1773
|
def _create_optional_infometrics(self, templates_path: Path, path_im: Path):
|
|
1732
1774
|
# If infographics are going to be created in FA, get template metric configurations
|
|
1733
1775
|
if not self.config.infographics:
|
|
@@ -1736,14 +1778,10 @@ class DatabaseBuilder:
|
|
|
1736
1778
|
# Check what type of infographics should be used
|
|
1737
1779
|
if self.config.unit_system == UnitSystems.imperial:
|
|
1738
1780
|
metrics_folder_name = "US_NSI"
|
|
1739
|
-
|
|
1740
|
-
"Default NSI infometrics and infographics will be created."
|
|
1741
|
-
)
|
|
1781
|
+
logger.info("Default NSI infometrics and infographics will be created.")
|
|
1742
1782
|
elif self.config.unit_system == UnitSystems.metric:
|
|
1743
1783
|
metrics_folder_name = "OSM"
|
|
1744
|
-
|
|
1745
|
-
"Default OSM infometrics and infographics will be created."
|
|
1746
|
-
)
|
|
1784
|
+
logger.info("Default OSM infometrics and infographics will be created.")
|
|
1747
1785
|
else:
|
|
1748
1786
|
raise ValueError(
|
|
1749
1787
|
f"Unit system {self.config.unit_system} is not recognized. Please choose 'imperial' or 'metric'."
|
|
@@ -1790,6 +1828,7 @@ class DatabaseBuilder:
|
|
|
1790
1828
|
path_1 = self.root.joinpath("static", "templates", "infographics", "images")
|
|
1791
1829
|
shutil.copytree(path_0, path_1)
|
|
1792
1830
|
|
|
1831
|
+
@debug_timer
|
|
1793
1832
|
def add_static_files(self):
|
|
1794
1833
|
"""
|
|
1795
1834
|
Copy static files from the 'templates' folder to the 'static' folder.
|
|
@@ -1804,10 +1843,11 @@ class DatabaseBuilder:
|
|
|
1804
1843
|
path_1 = self.static_path / folder
|
|
1805
1844
|
shutil.copytree(path_0, path_1)
|
|
1806
1845
|
|
|
1846
|
+
@debug_timer
|
|
1807
1847
|
def add_probabilistic_set(self):
|
|
1808
1848
|
# Copy prob set if given
|
|
1809
1849
|
if self.config.probabilistic_set:
|
|
1810
|
-
|
|
1850
|
+
logger.info(
|
|
1811
1851
|
f"Probabilistic event set imported from {self.config.probabilistic_set}"
|
|
1812
1852
|
)
|
|
1813
1853
|
prob_event_name = Path(self.config.probabilistic_set).name
|
|
@@ -1815,7 +1855,7 @@ class DatabaseBuilder:
|
|
|
1815
1855
|
shutil.copytree(self.config.probabilistic_set, path_db)
|
|
1816
1856
|
self._probabilistic_set_name = prob_event_name
|
|
1817
1857
|
else:
|
|
1818
|
-
|
|
1858
|
+
logger.warning(
|
|
1819
1859
|
"Probabilistic event set not provided. Risk scenarios cannot be run in FloodAdapt."
|
|
1820
1860
|
)
|
|
1821
1861
|
self._probabilistic_set_name = None
|
|
@@ -1829,7 +1869,7 @@ class DatabaseBuilder:
|
|
|
1829
1869
|
the input and static folders. It also creates subfolders within the input and
|
|
1830
1870
|
static folders based on a predefined list of names.
|
|
1831
1871
|
"""
|
|
1832
|
-
|
|
1872
|
+
logger.info("Preparing the database folder structure.")
|
|
1833
1873
|
inputs = [
|
|
1834
1874
|
"events",
|
|
1835
1875
|
"projections",
|
|
@@ -1856,6 +1896,23 @@ class DatabaseBuilder:
|
|
|
1856
1896
|
else:
|
|
1857
1897
|
raise ValueError(f"Path {path} is not absolute.")
|
|
1858
1898
|
|
|
1899
|
+
def _get_fiat_building_geoms(self) -> gpd.GeoDataFrame:
|
|
1900
|
+
"""
|
|
1901
|
+
Get the building geometries from the FIAT model.
|
|
1902
|
+
|
|
1903
|
+
Returns
|
|
1904
|
+
-------
|
|
1905
|
+
gpd.GeoDataFrame
|
|
1906
|
+
A GeoDataFrame containing the building geometries.
|
|
1907
|
+
"""
|
|
1908
|
+
building_indices = self._get_fiat_building_index()
|
|
1909
|
+
buildings = pd.concat(
|
|
1910
|
+
[self.fiat_model.exposure.exposure_geoms[i] for i in building_indices],
|
|
1911
|
+
ignore_index=True,
|
|
1912
|
+
)
|
|
1913
|
+
return buildings
|
|
1914
|
+
|
|
1915
|
+
@debug_timer
|
|
1859
1916
|
def _join_building_footprints(
|
|
1860
1917
|
self, building_footprints: gpd.GeoDataFrame, field_name: str
|
|
1861
1918
|
) -> Path:
|
|
@@ -1878,12 +1935,10 @@ class DatabaseBuilder:
|
|
|
1878
1935
|
7. Updates the site attributes with the relative path to the saved building footprints.
|
|
1879
1936
|
8. Logs the location where the building footprints are saved.
|
|
1880
1937
|
"""
|
|
1881
|
-
buildings = self.
|
|
1882
|
-
self._get_fiat_building_index()
|
|
1883
|
-
]
|
|
1938
|
+
buildings = self._get_fiat_building_geoms()
|
|
1884
1939
|
exposure_csv = self.fiat_model.exposure.exposure_db
|
|
1885
1940
|
if "BF_FID" in exposure_csv.columns:
|
|
1886
|
-
|
|
1941
|
+
logger.warning(
|
|
1887
1942
|
"Column 'BF_FID' already exists in the exposure columns and will be replaced."
|
|
1888
1943
|
)
|
|
1889
1944
|
del exposure_csv["BF_FID"]
|
|
@@ -1919,12 +1974,13 @@ class DatabaseBuilder:
|
|
|
1919
1974
|
|
|
1920
1975
|
# Save site attributes
|
|
1921
1976
|
buildings_path = geo_path.relative_to(self.static_path)
|
|
1922
|
-
|
|
1977
|
+
logger.info(
|
|
1923
1978
|
f"Building footprints saved at {(self.static_path / buildings_path).resolve().as_posix()}"
|
|
1924
1979
|
)
|
|
1925
1980
|
|
|
1926
1981
|
return buildings_path
|
|
1927
1982
|
|
|
1983
|
+
@debug_timer
|
|
1928
1984
|
def _clip_hazard_extend(self, clip_footprints=True):
|
|
1929
1985
|
"""
|
|
1930
1986
|
Clip the exposure data to the bounding box of the hazard data.
|
|
@@ -1943,9 +1999,8 @@ class DatabaseBuilder:
|
|
|
1943
1999
|
-------
|
|
1944
2000
|
None
|
|
1945
2001
|
"""
|
|
1946
|
-
gdf = self.
|
|
1947
|
-
|
|
1948
|
-
)
|
|
2002
|
+
gdf = self._get_fiat_gdf_full()
|
|
2003
|
+
|
|
1949
2004
|
crs = gdf.crs
|
|
1950
2005
|
sfincs_extend = self.sfincs_overland_model.region
|
|
1951
2006
|
sfincs_extend = sfincs_extend.to_crs(crs)
|
|
@@ -1955,55 +2010,21 @@ class DatabaseBuilder:
|
|
|
1955
2010
|
self.fiat_model.geoms["region"] = clipped_region
|
|
1956
2011
|
|
|
1957
2012
|
# Clip the exposure geometries
|
|
1958
|
-
|
|
1959
|
-
road_inds = gdf[_FIAT_COLUMNS.primary_object_type].str.contains("road")
|
|
1960
|
-
# Ensure road_inds is a boolean Series
|
|
1961
|
-
if not road_inds.dtype == bool:
|
|
1962
|
-
road_inds = road_inds.astype(bool)
|
|
1963
|
-
# Clip buildings
|
|
1964
|
-
gdf_buildings = gdf[~road_inds]
|
|
1965
|
-
gdf_buildings = self._clip_gdf(
|
|
1966
|
-
gdf_buildings, clipped_region, predicate="within"
|
|
1967
|
-
).reset_index(drop=True)
|
|
1968
|
-
|
|
1969
|
-
if road_inds.any():
|
|
1970
|
-
# Clip roads
|
|
1971
|
-
gdf_roads = gdf[road_inds]
|
|
1972
|
-
gdf_roads = self._clip_gdf(
|
|
1973
|
-
gdf_roads, clipped_region, predicate="within"
|
|
1974
|
-
).reset_index(drop=True)
|
|
1975
|
-
|
|
1976
|
-
idx_buildings = self.fiat_model.exposure.geom_names.index(
|
|
1977
|
-
self.config.fiat_buildings_name
|
|
1978
|
-
)
|
|
1979
|
-
idx_roads = self.fiat_model.exposure.geom_names.index(
|
|
1980
|
-
self.config.fiat_roads_name
|
|
1981
|
-
)
|
|
1982
|
-
self.fiat_model.exposure.exposure_geoms[idx_buildings] = gdf_buildings[
|
|
1983
|
-
[_FIAT_COLUMNS.object_id, "geometry"]
|
|
1984
|
-
]
|
|
1985
|
-
self.fiat_model.exposure.exposure_geoms[idx_roads] = gdf_roads[
|
|
1986
|
-
[_FIAT_COLUMNS.object_id, "geometry"]
|
|
1987
|
-
]
|
|
1988
|
-
gdf = pd.concat([gdf_buildings, gdf_roads])
|
|
1989
|
-
else:
|
|
1990
|
-
gdf = gdf_buildings
|
|
1991
|
-
self.fiat_model.exposure.exposure_geoms[0] = gdf[
|
|
1992
|
-
[_FIAT_COLUMNS.object_id, "geometry"]
|
|
1993
|
-
]
|
|
2013
|
+
gdf = self._clip_gdf(gdf, sfincs_extend, predicate="within")
|
|
1994
2014
|
|
|
1995
2015
|
# Save exposure dataframe
|
|
1996
2016
|
del gdf["geometry"]
|
|
1997
2017
|
self.fiat_model.exposure.exposure_db = gdf.reset_index(drop=True)
|
|
1998
2018
|
|
|
2019
|
+
# Make
|
|
2020
|
+
self._delete_extra_geometries()
|
|
2021
|
+
|
|
1999
2022
|
# Clip the building footprints
|
|
2000
2023
|
fieldname = "BF_FID"
|
|
2001
2024
|
if clip_footprints and not self.fiat_model.building_footprint.empty:
|
|
2002
2025
|
# Get buildings after filtering and their footprint id
|
|
2003
2026
|
self.fiat_model.building_footprint = self.fiat_model.building_footprint[
|
|
2004
|
-
self.fiat_model.building_footprint[fieldname].isin(
|
|
2005
|
-
gdf_buildings[fieldname]
|
|
2006
|
-
)
|
|
2027
|
+
self.fiat_model.building_footprint[fieldname].isin(gdf[fieldname])
|
|
2007
2028
|
].reset_index(drop=True)
|
|
2008
2029
|
|
|
2009
2030
|
@staticmethod
|
|
@@ -2022,6 +2043,7 @@ class DatabaseBuilder:
|
|
|
2022
2043
|
return gdf_new
|
|
2023
2044
|
|
|
2024
2045
|
@staticmethod
|
|
2046
|
+
@debug_timer
|
|
2025
2047
|
def spatial_join(
|
|
2026
2048
|
objects: gpd.GeoDataFrame,
|
|
2027
2049
|
layer: Union[str, gpd.GeoDataFrame],
|
|
@@ -2070,14 +2092,25 @@ class DatabaseBuilder:
|
|
|
2070
2092
|
layer = layer.rename(columns={field_name: rename})
|
|
2071
2093
|
return objects_joined, layer
|
|
2072
2094
|
|
|
2073
|
-
def _get_fiat_building_index(self) -> int:
|
|
2074
|
-
|
|
2075
|
-
|
|
2095
|
+
def _get_fiat_building_index(self) -> list[int]:
|
|
2096
|
+
names = self.config.fiat_buildings_name
|
|
2097
|
+
if isinstance(names, str):
|
|
2098
|
+
names = [names]
|
|
2099
|
+
indices = [
|
|
2100
|
+
self.fiat_model.exposure.geom_names.index(name)
|
|
2101
|
+
for name in names
|
|
2102
|
+
if name in self.fiat_model.exposure.geom_names
|
|
2103
|
+
]
|
|
2104
|
+
if indices:
|
|
2105
|
+
return indices
|
|
2106
|
+
raise ValueError(
|
|
2107
|
+
f"None of the specified building geometry names {names} found in FIAT model exposure geom_names."
|
|
2076
2108
|
)
|
|
2077
2109
|
|
|
2078
2110
|
def _get_fiat_road_index(self) -> int:
|
|
2079
2111
|
return self.fiat_model.exposure.geom_names.index(self.config.fiat_roads_name)
|
|
2080
2112
|
|
|
2113
|
+
@debug_timer
|
|
2081
2114
|
def _get_closest_station(self):
|
|
2082
2115
|
# Get available stations from source
|
|
2083
2116
|
obs_data = obs.source(self.config.tide_gauge.source)
|
|
@@ -2099,7 +2132,7 @@ class DatabaseBuilder:
|
|
|
2099
2132
|
)
|
|
2100
2133
|
|
|
2101
2134
|
distance = us.UnitfulLength(value=distance, units=us.UnitTypesLength.meters)
|
|
2102
|
-
|
|
2135
|
+
logger.info(
|
|
2103
2136
|
f"The closest tide gauge from {self.config.tide_gauge.source} is located {distance.transform(self.unit_system.default_length_units)} from the SFINCS domain"
|
|
2104
2137
|
)
|
|
2105
2138
|
# Check if user provided max distance
|
|
@@ -2110,7 +2143,7 @@ class DatabaseBuilder:
|
|
|
2110
2143
|
value=distance.convert(units_new), units=units_new
|
|
2111
2144
|
)
|
|
2112
2145
|
if distance_new.value > self.config.tide_gauge.max_distance.value:
|
|
2113
|
-
|
|
2146
|
+
logger.warning(
|
|
2114
2147
|
f"This distance is larger than the 'max_distance' value of {self.config.tide_gauge.max_distance.value} {units_new} provided in the config file. The station cannot be used."
|
|
2115
2148
|
)
|
|
2116
2149
|
return None
|
|
@@ -2120,6 +2153,7 @@ class DatabaseBuilder:
|
|
|
2120
2153
|
|
|
2121
2154
|
return station_id
|
|
2122
2155
|
|
|
2156
|
+
@debug_timer
|
|
2123
2157
|
def _get_station_metadata(self, station_id: str, ref: str = "MLLW"):
|
|
2124
2158
|
"""
|
|
2125
2159
|
Find the closest tide gauge station to the SFINCS domain and retrieves its metadata.
|
|
@@ -2166,11 +2200,11 @@ class DatabaseBuilder:
|
|
|
2166
2200
|
"lat": station_metadata["lat"],
|
|
2167
2201
|
}
|
|
2168
2202
|
|
|
2169
|
-
|
|
2203
|
+
logger.info(
|
|
2170
2204
|
f"The tide gauge station '{station_metadata['name']}' from {self.config.tide_gauge.source} will be used to download nearshore historical water level time-series."
|
|
2171
2205
|
)
|
|
2172
2206
|
|
|
2173
|
-
|
|
2207
|
+
logger.info(
|
|
2174
2208
|
f"The station metadata will be used to fill in the water_level attribute in the site.toml. The reference level will be {ref}."
|
|
2175
2209
|
)
|
|
2176
2210
|
|
|
@@ -2191,14 +2225,69 @@ class DatabaseBuilder:
|
|
|
2191
2225
|
bin_colors = tomli.load(f)
|
|
2192
2226
|
return bin_colors
|
|
2193
2227
|
|
|
2228
|
+
def _delete_extra_geometries(self) -> None:
|
|
2229
|
+
"""
|
|
2230
|
+
Remove extra geometries from the exposure_geoms list that do not have a corresponding object_id in the exposure_db DataFrame.
|
|
2194
2231
|
|
|
2195
|
-
|
|
2232
|
+
Returns
|
|
2233
|
+
-------
|
|
2234
|
+
None
|
|
2235
|
+
"""
|
|
2236
|
+
# Make sure only csv objects have geometries
|
|
2237
|
+
for i, geoms in enumerate(self.fiat_model.exposure.exposure_geoms):
|
|
2238
|
+
keep = geoms[_FIAT_COLUMNS.object_id].isin(
|
|
2239
|
+
self.fiat_model.exposure.exposure_db[_FIAT_COLUMNS.object_id]
|
|
2240
|
+
)
|
|
2241
|
+
geoms = geoms[keep].reset_index(drop=True)
|
|
2242
|
+
self.fiat_model.exposure.exposure_geoms[i] = geoms
|
|
2243
|
+
|
|
2244
|
+
def _get_fiat_gdf_full(self) -> gpd.GeoDataFrame:
|
|
2245
|
+
"""
|
|
2246
|
+
Get the full GeoDataFrame of the Fiat model.
|
|
2247
|
+
|
|
2248
|
+
Returns
|
|
2249
|
+
-------
|
|
2250
|
+
gpd.GeoDataFrame: The full GeoDataFrame of the Fiat model.
|
|
2251
|
+
"""
|
|
2252
|
+
gdf = self.fiat_model.exposure.get_full_gdf(
|
|
2253
|
+
self.fiat_model.exposure.exposure_db
|
|
2254
|
+
)
|
|
2255
|
+
# Keep only unique "object_id" rows, keeping the first occurrence
|
|
2256
|
+
gdf = gdf.drop_duplicates(
|
|
2257
|
+
subset=_FIAT_COLUMNS.object_id, keep="first"
|
|
2258
|
+
).reset_index(drop=True)
|
|
2259
|
+
|
|
2260
|
+
return gdf
|
|
2261
|
+
|
|
2262
|
+
|
|
2263
|
+
def main():
|
|
2196
2264
|
while True:
|
|
2197
2265
|
config_path = Path(
|
|
2198
2266
|
input(
|
|
2199
2267
|
"Please provide the path to the database creation configuration toml: \n"
|
|
2200
2268
|
)
|
|
2201
2269
|
)
|
|
2270
|
+
print(
|
|
2271
|
+
"Please select the log verbosity level for the database creation process.\n"
|
|
2272
|
+
"From most verbose to least verbose: `DEBUG`, `INFO`, `WARNING`.'n"
|
|
2273
|
+
)
|
|
2274
|
+
log_level = input("Enter log level: ")
|
|
2275
|
+
match log_level:
|
|
2276
|
+
case "DEBUG":
|
|
2277
|
+
level = logging.DEBUG
|
|
2278
|
+
case "INFO":
|
|
2279
|
+
level = logging.INFO
|
|
2280
|
+
case "WARNING":
|
|
2281
|
+
level = logging.WARNING
|
|
2282
|
+
case _:
|
|
2283
|
+
print(
|
|
2284
|
+
f"Log level `{log_level}` not recognized. Defaulting to INFO. Please choose from: `DEBUG`, `INFO`, `WARNING`."
|
|
2285
|
+
)
|
|
2286
|
+
log_level = "INFO"
|
|
2287
|
+
level = logging.INFO
|
|
2288
|
+
|
|
2289
|
+
FloodAdaptLogging(level=level)
|
|
2290
|
+
|
|
2202
2291
|
try:
|
|
2203
2292
|
config = ConfigModel.read(config_path)
|
|
2204
2293
|
dbs = DatabaseBuilder(config)
|
|
@@ -2208,3 +2297,7 @@ if __name__ == "__main__":
|
|
|
2208
2297
|
quit = input("Do you want to quit? (y/n)")
|
|
2209
2298
|
if quit == "y":
|
|
2210
2299
|
exit()
|
|
2300
|
+
|
|
2301
|
+
|
|
2302
|
+
if __name__ == "__main__":
|
|
2303
|
+
main()
|