flood-adapt 1.0.6__py3-none-any.whl → 1.1.1__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.
Files changed (42) hide show
  1. flood_adapt/__init__.py +1 -2
  2. flood_adapt/adapter/fiat_adapter.py +12 -7
  3. flood_adapt/adapter/sfincs_adapter.py +20 -23
  4. flood_adapt/config/fiat.py +1 -1
  5. flood_adapt/config/gui.py +185 -8
  6. flood_adapt/config/hazard.py +1 -1
  7. flood_adapt/database_builder/database_builder.py +155 -129
  8. flood_adapt/database_builder/metrics_utils.py +1834 -0
  9. flood_adapt/dbs_classes/database.py +4 -4
  10. flood_adapt/dbs_classes/dbs_static.py +2 -2
  11. flood_adapt/flood_adapt.py +65 -14
  12. flood_adapt/misc/utils.py +29 -10
  13. flood_adapt/objects/forcing/plotting.py +4 -4
  14. flood_adapt/objects/measures/measures.py +3 -1
  15. flood_adapt/workflows/benefit_runner.py +3 -2
  16. {flood_adapt-1.0.6.dist-info → flood_adapt-1.1.1.dist-info}/METADATA +13 -124
  17. {flood_adapt-1.0.6.dist-info → flood_adapt-1.1.1.dist-info}/RECORD +21 -41
  18. flood_adapt/database_builder/templates/infographics/OSM/config_charts.toml +0 -90
  19. flood_adapt/database_builder/templates/infographics/OSM/config_people.toml +0 -57
  20. flood_adapt/database_builder/templates/infographics/OSM/config_risk_charts.toml +0 -121
  21. flood_adapt/database_builder/templates/infographics/OSM/config_roads.toml +0 -65
  22. flood_adapt/database_builder/templates/infographics/US_NSI/config_charts.toml +0 -126
  23. flood_adapt/database_builder/templates/infographics/US_NSI/config_people.toml +0 -60
  24. flood_adapt/database_builder/templates/infographics/US_NSI/config_risk_charts.toml +0 -121
  25. flood_adapt/database_builder/templates/infographics/US_NSI/config_roads.toml +0 -65
  26. flood_adapt/database_builder/templates/infographics/US_NSI/styles.css +0 -45
  27. flood_adapt/database_builder/templates/infometrics/OSM/metrics_additional_risk_configs.toml +0 -4
  28. flood_adapt/database_builder/templates/infometrics/OSM/with_SVI/infographic_metrics_config.toml +0 -143
  29. flood_adapt/database_builder/templates/infometrics/OSM/with_SVI/infographic_metrics_config_risk.toml +0 -153
  30. flood_adapt/database_builder/templates/infometrics/OSM/without_SVI/infographic_metrics_config.toml +0 -127
  31. flood_adapt/database_builder/templates/infometrics/OSM/without_SVI/infographic_metrics_config_risk.toml +0 -57
  32. flood_adapt/database_builder/templates/infometrics/US_NSI/metrics_additional_risk_configs.toml +0 -4
  33. flood_adapt/database_builder/templates/infometrics/US_NSI/with_SVI/infographic_metrics_config.toml +0 -191
  34. flood_adapt/database_builder/templates/infometrics/US_NSI/with_SVI/infographic_metrics_config_risk.toml +0 -153
  35. flood_adapt/database_builder/templates/infometrics/US_NSI/without_SVI/infographic_metrics_config.toml +0 -178
  36. flood_adapt/database_builder/templates/infometrics/US_NSI/without_SVI/infographic_metrics_config_risk.toml +0 -57
  37. flood_adapt/database_builder/templates/infometrics/mandatory_metrics_config.toml +0 -9
  38. flood_adapt/database_builder/templates/infometrics/mandatory_metrics_config_risk.toml +0 -65
  39. /flood_adapt/database_builder/templates/infographics/{OSM/styles.css → styles.css} +0 -0
  40. {flood_adapt-1.0.6.dist-info → flood_adapt-1.1.1.dist-info}/LICENSE +0 -0
  41. {flood_adapt-1.0.6.dist-info → flood_adapt-1.1.1.dist-info}/WHEEL +0 -0
  42. {flood_adapt-1.0.6.dist-info → flood_adapt-1.1.1.dist-info}/top_level.txt +0 -0
@@ -8,7 +8,7 @@ import shutil
8
8
  import warnings
9
9
  from enum import Enum
10
10
  from pathlib import Path
11
- from typing import Optional, Union
11
+ from typing import Literal, Optional, Union
12
12
  from urllib.request import urlretrieve
13
13
 
14
14
  import cht_observations.observation_stations as obs
@@ -17,7 +17,6 @@ import numpy as np
17
17
  import pandas as pd
18
18
  import rioxarray as rxr
19
19
  import tomli
20
- import tomli_w
21
20
  import xarray as xr
22
21
  from hydromt_fiat import FiatModel as HydromtFiatModel
23
22
  from hydromt_fiat.data_apis.open_street_maps import get_buildings_from_osm
@@ -88,6 +87,16 @@ from flood_adapt.objects.projections.projections import (
88
87
  )
89
88
  from flood_adapt.objects.strategies.strategies import Strategy
90
89
 
90
+ from .metrics_utils import (
91
+ BuildingsInfographicModel,
92
+ EventInfographicModel,
93
+ HomesInfographicModel,
94
+ MetricModel,
95
+ Metrics,
96
+ RiskInfographicModel,
97
+ RoadsInfographicModel,
98
+ )
99
+
91
100
  logger = FloodAdaptLogging.getLogger("DatabaseBuilder")
92
101
 
93
102
 
@@ -262,7 +271,8 @@ class TideGaugeConfigModel(BaseModel):
262
271
 
263
272
 
264
273
  class ConfigModel(BaseModel):
265
- """Represents the configuration model for FloodAdapt.
274
+ """
275
+ Represents the configuration model for FloodAdapt.
266
276
 
267
277
  Attributes
268
278
  ----------
@@ -276,8 +286,16 @@ class ConfigModel(BaseModel):
276
286
  The unit system.
277
287
  gui : GuiConfigModel
278
288
  The GUI model representing scaling values for the layers.
279
- infographics : Optional[bool], default True
289
+ infographics : bool, default True
280
290
  Indicates if infographics are enabled.
291
+ event_infographics : Optional[EventInfographicModel], default None
292
+ Event infographic configuration.
293
+ risk_infographics : Optional[RiskInfographicModel], default None
294
+ Risk infographic configuration.
295
+ event_additional_infometrics : Optional[list[MetricModel]], default None
296
+ Additional event infometrics.
297
+ risk_additional_infometrics : Optional[list[MetricModel]], default None
298
+ Additional risk infometrics.
281
299
  fiat : str
282
300
  The FIAT model path.
283
301
  aggregation_areas : Optional[list[SpatialJoinModel]], default None
@@ -292,13 +310,13 @@ class ConfigModel(BaseModel):
292
310
  The BFE model.
293
311
  svi : Optional[SviConfigModel], default None
294
312
  The SVI model.
295
- road_width : Optional[float], default 5
296
- The road width in meters.
297
- return_periods : list[int], default []
313
+ road_width : us.UnitfulLength, default 5 meters
314
+ The road width.
315
+ return_periods : Optional[list[int]], default None
298
316
  The list of return periods for risk calculations.
299
317
  floodmap_type : Optional[FloodmapType], default None
300
318
  The type of floodmap to use.
301
- references : WaterlevelReferenceModel, default WaterlevelReferenceModel(...)
319
+ references : Optional[WaterlevelReferenceModel], default None
302
320
  The water level reference model.
303
321
  sfincs_overland : FloodModel
304
322
  The overland SFINCS model.
@@ -330,7 +348,11 @@ class ConfigModel(BaseModel):
330
348
  database_path: Optional[str] = None
331
349
  unit_system: UnitSystems
332
350
  gui: GuiConfigModel
333
- infographics: Optional[bool] = True
351
+ infographics: bool = True
352
+ event_infographics: Optional[EventInfographicModel] = None
353
+ risk_infographics: Optional[RiskInfographicModel] = None
354
+ event_additional_infometrics: Optional[list[MetricModel]] = None
355
+ risk_additional_infometrics: Optional[list[MetricModel]] = None
334
356
 
335
357
  # FIAT
336
358
  fiat: str
@@ -345,7 +367,7 @@ class ConfigModel(BaseModel):
345
367
  road_width: us.UnitfulLength = us.UnitfulLength(
346
368
  value=5.0, units=us.UnitTypesLength.meters
347
369
  )
348
- return_periods: list[int] = Field(default_factory=list)
370
+ return_periods: Optional[list[int]] = None
349
371
  floodmap_type: Optional[FloodmapType] = None
350
372
 
351
373
  # SFINCS
@@ -631,20 +653,13 @@ class DatabaseBuilder:
631
653
  return fiat
632
654
 
633
655
  @debug_timer
634
- def create_risk_model(self) -> Optional[RiskModel]:
656
+ def create_risk_model(self) -> RiskModel:
635
657
  # Check if return periods are provided
636
658
  if not self.config.return_periods:
637
- if self._probabilistic_set_name:
638
- risk = RiskModel()
639
- logger.warning(
640
- f"No return periods provided, but a probabilistic set is available. Using default return periods {risk.return_periods}."
641
- )
642
- return risk
643
- else:
644
- logger.warning(
645
- "No return periods provided and no probabilistic set available. Risk calculations will not be performed."
646
- )
647
- return None
659
+ risk = RiskModel()
660
+ logger.warning(
661
+ f"No return periods provided for performing risk calculations. Using default return periods {risk.return_periods}."
662
+ )
648
663
  else:
649
664
  risk = RiskModel(return_periods=self.config.return_periods)
650
665
  return risk
@@ -1839,123 +1854,118 @@ class DatabaseBuilder:
1839
1854
 
1840
1855
  @debug_timer
1841
1856
  def create_infometrics(self):
1842
- """
1843
- Copy the infometrics and infographics templates to the appropriate location and modifies the metrics_config.toml files.
1844
-
1845
- This method copies the templates from the 'infometrics' and 'infographics' folders to the 'static/templates' folder in the root directory.
1846
- It then modifies the 'metrics_config.toml' and 'metrics_config_risk.toml' files by updating the 'aggregateBy' attribute with the names
1847
- of the aggregations defined in the 'fiat' section of the 'site_attrs' attribute.
1848
- """
1849
- # TODO there should be generalized infometric queries with NSI or OSM, and with SVI or without. Then Based on the user input these should be chosen automatically
1850
- templates_path = Path(__file__).parent.resolve().joinpath("templates")
1851
-
1852
- # Create template folder
1857
+ # Define paths for infometrics and infographics templates
1853
1858
  path_im = self.root.joinpath("static", "templates", "infometrics")
1854
- path_im.mkdir()
1855
-
1856
- # Copy mandatory metric configs
1857
- path_im_temp = templates_path.joinpath("infometrics")
1858
- for file in path_im_temp.glob("*.toml"):
1859
- shutil.copy2(file, path_im)
1860
-
1861
- self._create_optional_infometrics(templates_path, path_im)
1862
-
1863
- files = list(path_im.glob("*metrics_config*.toml"))
1864
- # Update aggregation areas in metrics config
1865
- for file in files:
1866
- file = path_im.joinpath(file)
1867
- with open(file, "rb") as f:
1868
- attrs = tomli.load(f)
1869
-
1870
- # add aggration levels
1871
- if self._aggregation_areas is None:
1872
- self._aggregation_areas = self.create_aggregation_areas()
1873
- attrs["aggregateBy"] = [aggr.name for aggr in self._aggregation_areas]
1874
-
1875
- # take out road metrics if needed
1876
- if not self._has_roads:
1877
- attrs["queries"] = [
1878
- query
1879
- for query in attrs["queries"]
1880
- if "road" not in query["name"].lower()
1881
- ]
1882
-
1883
- # Replace Damage Unit
1884
- # TODO do this in a better manner
1885
- for i, query in enumerate(attrs["queries"]):
1886
- if "$" in query["long_name"]:
1887
- query["long_name"] = query["long_name"].replace(
1888
- "$", self.read_damage_unit()
1889
- )
1890
-
1891
- # replace the SVI threshold if needed
1892
- if self.config.svi:
1893
- for i, query in enumerate(attrs["queries"]):
1894
- query["filter"] = query["filter"].replace(
1895
- "SVI_threshold", str(self.config.svi.threshold)
1896
- )
1897
-
1898
- with open(file, "wb") as f:
1899
- tomli_w.dump(attrs, f)
1900
-
1901
- @debug_timer
1902
- def _create_optional_infometrics(self, templates_path: Path, path_im: Path):
1903
- # If infographics are going to be created in FA, get template metric configurations
1904
- if not self.config.infographics:
1905
- return
1859
+ path_ig = self.root.joinpath("static", "templates", "infographics")
1906
1860
 
1907
- # Check what type of infographics should be used
1908
- if self.config.unit_system == UnitSystems.imperial:
1909
- metrics_folder_name = "US_NSI"
1910
- logger.info("Default NSI infometrics and infographics will be created.")
1911
- elif self.config.unit_system == UnitSystems.metric:
1912
- metrics_folder_name = "OSM"
1913
- logger.info("Default OSM infometrics and infographics will be created.")
1914
- else:
1915
- raise ValueError(
1916
- f"Unit system {self.config.unit_system} is not recognized. Please choose 'imperial' or 'metric'."
1917
- )
1861
+ # Create directories and ensure they are empty
1862
+ for path in [path_im, path_ig]:
1863
+ if path.exists():
1864
+ shutil.rmtree(path)
1865
+ path.mkdir(parents=True, exist_ok=True)
1918
1866
 
1919
- if self.config.svi is not None:
1920
- svi_folder_name = "with_SVI"
1921
- else:
1922
- svi_folder_name = "without_SVI"
1923
-
1924
- # Copy metrics config for infographics
1925
- path_0 = templates_path.joinpath(
1926
- "infometrics", metrics_folder_name, svi_folder_name
1867
+ # Use images and css from the database builder templates
1868
+ templates_path = (
1869
+ Path(__file__).parent.resolve().joinpath("templates", "infographics")
1927
1870
  )
1928
- for file in path_0.glob("*.toml"):
1929
- shutil.copy2(file, path_im)
1930
-
1931
- # Copy additional risk config
1932
- file = templates_path.joinpath(
1933
- "infometrics",
1934
- metrics_folder_name,
1935
- "metrics_additional_risk_configs.toml",
1871
+ shutil.copytree(templates_path, path_ig)
1872
+
1873
+ # Create Metrics object
1874
+ metrics = Metrics(
1875
+ dmg_unit=self.read_damage_unit(),
1876
+ return_periods=self.create_risk_model().return_periods,
1936
1877
  )
1937
- shutil.copy2(file, path_im)
1938
1878
 
1939
- # Copy infographics config
1940
- path_ig_temp = templates_path.joinpath("infographics", metrics_folder_name)
1941
- path_ig = self.root.joinpath("static", "templates", "infographics")
1942
- path_ig.mkdir()
1943
- files_ig = ["styles.css", "config_charts.toml"]
1879
+ # First define the mandatory metrics
1880
+ self._create_mandatory_metrics(metrics)
1944
1881
 
1945
- if self.config.svi is not None:
1946
- files_ig.append("config_risk_charts.toml")
1947
- files_ig.append("config_people.toml")
1882
+ # Then the infographic metrics if needed
1883
+ if self.config.infographics:
1884
+ self._create_event_infographics(metrics)
1885
+ if self._probabilistic_set_name is not None:
1886
+ self._create_risk_infographics(metrics)
1948
1887
 
1949
- if self._has_roads:
1950
- files_ig.append("config_roads.toml")
1888
+ # Then the additional metrics if needed
1889
+ self._add_additional_event_metrics(metrics)
1890
+ if self._probabilistic_set_name is not None:
1891
+ self._add_additional_risk_metrics(metrics)
1951
1892
 
1952
- for file in files_ig:
1953
- shutil.copy2(path_ig_temp.joinpath(file), path_ig.joinpath(file))
1893
+ # Write the metrics config files
1894
+ self._write_infometrics(metrics, path_im, path_ig)
1954
1895
 
1955
- # Copy images
1956
- path_0 = templates_path.joinpath("infographics", "images")
1957
- path_1 = self.root.joinpath("static", "templates", "infographics", "images")
1958
- shutil.copytree(path_0, path_1)
1896
+ def _create_mandatory_metrics(self, metrics):
1897
+ metrics.create_mandatory_metrics_event()
1898
+ if self._probabilistic_set_name is not None:
1899
+ metrics.create_mandatory_metrics_risk()
1900
+
1901
+ def _create_event_infographics(self, metrics):
1902
+ exposure_type = self._get_exposure_type()
1903
+ # If not specific infographic config is given, create a standard one
1904
+ if not self.config.event_infographics:
1905
+ buildings, svi, roads = None, None, None
1906
+ if exposure_type is None:
1907
+ logger.warning(
1908
+ "No exposure type could be identified from the FIAT model. Standard event building infographics cannot be created."
1909
+ )
1910
+ else:
1911
+ buildings = BuildingsInfographicModel.get_template(type=exposure_type)
1912
+ if self.config.svi is not None:
1913
+ svi = HomesInfographicModel.get_template(
1914
+ svi_threshold=self.config.svi.threshold, type=exposure_type
1915
+ )
1916
+ else:
1917
+ logger.warning(
1918
+ "No SVI information available. Standard event SVI infographics cannot be created."
1919
+ )
1920
+ if self._has_roads:
1921
+ roads = RoadsInfographicModel.get_template(
1922
+ unit_system=self.config.unit_system.value
1923
+ )
1924
+ else:
1925
+ logger.warning(
1926
+ "No roads available in the exposure. Standard event road infographics cannot be created."
1927
+ )
1928
+ self.config.event_infographics = EventInfographicModel(
1929
+ buildings=buildings, svi=svi, roads=roads
1930
+ )
1931
+ metrics.create_infographics_metrics_event(config=self.config.event_infographics)
1932
+
1933
+ def _create_risk_infographics(self, metrics):
1934
+ exposure_type = self._get_exposure_type()
1935
+ # If not specific infographic config is given, create a standard one
1936
+ if not self.config.risk_infographics:
1937
+ if exposure_type is None:
1938
+ logger.warning(
1939
+ "No exposure type could be identified from the FIAT model. Standard risk building infographics cannot be created."
1940
+ )
1941
+ else:
1942
+ self.config.risk_infographics = RiskInfographicModel.get_template(
1943
+ svi_threshold=self.config.svi.threshold
1944
+ if self.config.svi
1945
+ else None,
1946
+ type=exposure_type,
1947
+ )
1948
+ metrics.create_infographics_metrics_risk(config=self.config.risk_infographics)
1949
+
1950
+ def _add_additional_event_metrics(self, metrics):
1951
+ if self.config.event_additional_infometrics:
1952
+ for metric in self.config.event_additional_infometrics:
1953
+ metrics.add_event_metric(metric)
1954
+
1955
+ def _add_additional_risk_metrics(self, metrics):
1956
+ if self.config.risk_additional_infometrics:
1957
+ for metric in self.config.risk_additional_infometrics:
1958
+ metrics.add_risk_metric(metric)
1959
+
1960
+ def _write_infometrics(self, metrics, path_im, path_ig):
1961
+ if self._aggregation_areas is None:
1962
+ self._aggregation_areas = self.create_aggregation_areas()
1963
+ aggr_levels = [aggr.name for aggr in self._aggregation_areas]
1964
+ metrics.write(
1965
+ metrics_path=path_im,
1966
+ aggregation_levels=aggr_levels,
1967
+ infographics_path=path_ig,
1968
+ )
1959
1969
 
1960
1970
  @debug_timer
1961
1971
  def add_static_files(self):
@@ -2399,6 +2409,22 @@ class DatabaseBuilder:
2399
2409
  geoms = geoms[keep].reset_index(drop=True)
2400
2410
  self.fiat_model.exposure.exposure_geoms[i] = geoms
2401
2411
 
2412
+ def _get_exposure_type(self) -> Literal["OSM", "NSI", None]:
2413
+ # Define the allowed types for OSM
2414
+ osm_types = ["residential", "commercial", "industrial", "road"]
2415
+ nsi_types = ["RES", "COM", "IND", "PUB", "road"]
2416
+ unique_types = set(
2417
+ self.fiat_model.exposure.exposure_db[
2418
+ _FIAT_COLUMNS.primary_object_type
2419
+ ].unique()
2420
+ )
2421
+ if unique_types.issubset(set(osm_types)):
2422
+ return "OSM"
2423
+ elif unique_types.issubset(set(nsi_types)):
2424
+ return "NSI"
2425
+ else:
2426
+ return None
2427
+
2402
2428
  def _get_fiat_gdf_full(self) -> gpd.GeoDataFrame:
2403
2429
  """
2404
2430
  Get the full GeoDataFrame of the Fiat model.