flood-adapt 0.3.12__py3-none-any.whl → 0.3.14__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 CHANGED
@@ -1,10 +1,9 @@
1
1
  # has to be here at the start to avoid circular imports
2
- __version__ = "0.3.12"
2
+ __version__ = "0.3.14"
3
3
 
4
- from flood_adapt import adapter, dbs_classes, objects
4
+ from flood_adapt import adapter, database_builder, dbs_classes, objects
5
5
  from flood_adapt.config.config import Settings
6
6
  from flood_adapt.config.site import Site
7
- from flood_adapt.database_builder.database_builder import DatabaseBuilder
8
7
  from flood_adapt.flood_adapt import FloodAdapt
9
8
  from flood_adapt.misc.exceptions import ComponentError, DatabaseError, FloodAdaptError
10
9
  from flood_adapt.misc.log import FloodAdaptLogging
@@ -19,10 +18,10 @@ __all__ = [
19
18
  "objects",
20
19
  "dbs_classes",
21
20
  "adapter",
21
+ "database_builder",
22
22
  "FloodAdaptError",
23
23
  "DatabaseError",
24
24
  "ComponentError",
25
- "DatabaseBuilder",
26
25
  ]
27
26
 
28
27
  FloodAdaptLogging() # Initialize logging once for the entire package
@@ -880,6 +880,16 @@ class FiatAdapter(IImpactAdapter):
880
880
  for aggr in self.config.aggregation
881
881
  ]
882
882
  new_dev_geom_name = Path(self.config.new_development_file_name).stem
883
+ # Ensure new_devs geom is in the correct CRS
884
+ new_dev_geom = gpd.read_file(area_path)
885
+ if new_dev_geom.crs != self.model.exposure.crs:
886
+ self.logger.warning(
887
+ f"New development area geometries are in {new_dev_geom.crs}, but the model is in {self.model.exposure.crs}. Reprojecting geometries."
888
+ )
889
+ new_dev_geom = new_dev_geom.to_crs(self.model.exposure.crs)
890
+ # Replace file with the reprojected one
891
+ os.remove(area_path)
892
+ new_dev_geom.to_file(area_path)
883
893
  # Use hydromt function
884
894
  self.model.exposure.setup_new_composite_areas(
885
895
  percent_growth=population_growth,
@@ -675,7 +675,10 @@ class SfincsAdapter(IHazardAdapter):
675
675
  )
676
676
 
677
677
  event = self.database.events.get(scenario.event)
678
- if self.settings.obs_point[ii].name == self.settings.tide_gauge.name:
678
+ if (
679
+ self.settings.tide_gauge is not None
680
+ and self.settings.obs_point[ii].name == self.settings.tide_gauge.name
681
+ ):
679
682
  self._add_tide_gauge_plot(fig, event, units=gui_units)
680
683
 
681
684
  # write html to results folder
@@ -0,0 +1,23 @@
1
+ from flood_adapt.database_builder.database_builder import (
2
+ Basins,
3
+ ConfigModel,
4
+ FootprintsOptions,
5
+ GuiConfigModel,
6
+ SpatialJoinModel,
7
+ SviConfigModel,
8
+ TideGaugeConfigModel,
9
+ UnitSystems,
10
+ create_database,
11
+ )
12
+
13
+ __all__ = [
14
+ "Basins",
15
+ "ConfigModel",
16
+ "FootprintsOptions",
17
+ "GuiConfigModel",
18
+ "SpatialJoinModel",
19
+ "SviConfigModel",
20
+ "TideGaugeConfigModel",
21
+ "UnitSystems",
22
+ "create_database",
23
+ ]
@@ -2,6 +2,7 @@ import datetime
2
2
  import logging
3
3
  import math
4
4
  import os
5
+ import re
5
6
  import shutil
6
7
  import time
7
8
  import warnings
@@ -108,19 +109,24 @@ def debug_timer(func):
108
109
 
109
110
 
110
111
  def path_check(str_path: str, config_path: Optional[Path] = None) -> str:
111
- """
112
- Check if the given path is absolute and return the absolute path.
112
+ """Check if the given path is absolute and return the absolute path.
113
113
 
114
- Args:
115
- path (str): The path to be checked.
114
+ Parameters
115
+ ----------
116
+ str_path : str
117
+ The path to be checked.
118
+ config_path : Optional[Path], default None
119
+ The base path to resolve relative paths.
116
120
 
117
121
  Returns
118
122
  -------
119
- str: The absolute path.
123
+ str
124
+ The absolute path as a string.
120
125
 
121
126
  Raises
122
127
  ------
123
- ValueError: If the path is not absolute and no config_path is provided.
128
+ ValueError
129
+ If the path is not absolute and no config_path is provided.
124
130
  """
125
131
  path = Path(str_path)
126
132
  if not path.is_absolute():
@@ -132,14 +138,16 @@ def path_check(str_path: str, config_path: Optional[Path] = None) -> str:
132
138
 
133
139
 
134
140
  class SpatialJoinModel(BaseModel):
135
- """
136
- Represents a spatial join model.
141
+ """Represents a spatial join model.
137
142
 
138
143
  Attributes
139
144
  ----------
140
- name (Optional[str]): The name of the model (optional).
141
- file (str): The file associated with the model.
142
- field_name (str): The field name used for the spatial join.
145
+ name : Optional[str], default None
146
+ The name of the model.
147
+ file : str
148
+ The file associated with the model.
149
+ field_name : str
150
+ The field name used for the spatial join.
143
151
  """
144
152
 
145
153
  name: Optional[str] = None
@@ -148,14 +156,14 @@ class SpatialJoinModel(BaseModel):
148
156
 
149
157
 
150
158
  class UnitSystems(str, Enum):
151
- """The `UnitSystems` class is an enumeration that represents the accepted values for the `metric_system` field.
152
-
153
- It provides two options: `imperial` and `metric`.
159
+ """Enumeration for accepted values for the unit_system field.
154
160
 
155
161
  Attributes
156
162
  ----------
157
- imperial (str): Represents the imperial unit system.
158
- metric (str): Represents the metric unit system.
163
+ imperial : str
164
+ Represents the imperial unit system.
165
+ metric : str
166
+ Represents the metric unit system.
159
167
  """
160
168
 
161
169
  imperial = "imperial"
@@ -163,24 +171,36 @@ class UnitSystems(str, Enum):
163
171
 
164
172
 
165
173
  class FootprintsOptions(str, Enum):
174
+ """Enumeration for accepted values for the building_footprints field.
175
+
176
+ Attributes
177
+ ----------
178
+ OSM : str
179
+ Use OpenStreetMap for building footprints.
180
+ """
181
+
166
182
  OSM = "OSM"
167
183
 
168
184
 
169
185
  class Basins(str, Enum):
170
- """
171
- Enumeration class representing different basins.
172
-
173
- Each basin is represented by a string value.
186
+ """Enumeration class representing different basins.
174
187
 
175
188
  Attributes
176
189
  ----------
177
- NA (str): North Atlantic
178
- SA (str): South Atlantic
179
- EP (str): Eastern North Pacific (which includes the Central Pacific region)
180
- WP (str): Western North Pacific
181
- SP (str): South Pacific
182
- SI (str): South Indian
183
- NI (str): North Indian
190
+ NA : str
191
+ North Atlantic
192
+ SA : str
193
+ South Atlantic
194
+ EP : str
195
+ Eastern North Pacific (which includes the Central Pacific region)
196
+ WP : str
197
+ Western North Pacific
198
+ SP : str
199
+ South Pacific
200
+ SI : str
201
+ South Indian
202
+ NI : str
203
+ North Indian
184
204
  """
185
205
 
186
206
  NA = "NA"
@@ -193,15 +213,18 @@ class Basins(str, Enum):
193
213
 
194
214
 
195
215
  class GuiConfigModel(BaseModel):
196
- """
197
- Represents a GUI model for for FloodAdapt.
216
+ """Represents a GUI model for FloodAdapt.
198
217
 
199
218
  Attributes
200
219
  ----------
201
- max_flood_depth (float): The last visualization bin will be ">value".
202
- max_aggr_dmg (float): The last visualization bin will be ">value".
203
- max_footprint_dmg (float): The last visualization bin will be ">value".
204
- max_benefits (float): The last visualization bin will be ">value".
220
+ max_flood_depth : float
221
+ The last visualization bin will be ">value".
222
+ max_aggr_dmg : float
223
+ The last visualization bin will be ">value".
224
+ max_footprint_dmg : float
225
+ The last visualization bin will be ">value".
226
+ max_benefits : float
227
+ The last visualization bin will be ">value".
205
228
  """
206
229
 
207
230
  max_flood_depth: float
@@ -211,32 +234,38 @@ class GuiConfigModel(BaseModel):
211
234
 
212
235
 
213
236
  class SviConfigModel(SpatialJoinModel):
214
- """
215
- Represents a model for the Social Vulnerability Index (SVI).
237
+ """Represents a model for the Social Vulnerability Index (SVI).
216
238
 
217
239
  Attributes
218
240
  ----------
219
- threshold (float): The threshold value for the SVI model to specify vulnerability.
241
+ threshold : float
242
+ The threshold value for the SVI model to specify vulnerability.
220
243
  """
221
244
 
222
245
  threshold: float
223
246
 
224
247
 
225
- class Point(BaseModel):
226
- lat: float
227
- lon: float
228
-
229
-
230
248
  class TideGaugeConfigModel(BaseModel):
231
- """
232
- Represents a tide gauge model.
249
+ """Represents a tide gauge model.
233
250
 
234
251
  Attributes
235
252
  ----------
236
- source (str): The source of the tide gauge data.
237
- file (Optional[str]): The file associated with the tide gauge data (default: None).
238
- max_distance (Optional[float]): The maximum distance (default: None).
239
- ref (str): The reference name. Should be defined in the water level references.
253
+ source : TideGaugeSource
254
+ The source of the tide gauge data.
255
+ description : str, default ""
256
+ Description of the tide gauge.
257
+ ref : Optional[str], default None
258
+ The reference name. Should be defined in the water level references.
259
+ id : Optional[int], default None
260
+ The station ID.
261
+ lon : Optional[float], default None
262
+ Longitude of the tide gauge.
263
+ lat : Optional[float], default None
264
+ Latitude of the tide gauge.
265
+ file : Optional[str], default None
266
+ The file associated with the tide gauge data.
267
+ max_distance : Optional[us.UnitfulLength], default None
268
+ The maximum distance.
240
269
  """
241
270
 
242
271
  source: TideGaugeSource
@@ -249,21 +278,8 @@ class TideGaugeConfigModel(BaseModel):
249
278
  max_distance: Optional[us.UnitfulLength] = None
250
279
 
251
280
 
252
- class SviModel(SpatialJoinModel):
253
- """
254
- Represents a model for the Social Vulnerability Index (SVI).
255
-
256
- Attributes
257
- ----------
258
- threshold (float): The threshold value for the SVI model to specify vulnerability.
259
- """
260
-
261
- threshold: float
262
-
263
-
264
281
  class ConfigModel(BaseModel):
265
- """
266
- Represents the configuration model for FloodAdapt.
282
+ """Represents the configuration model for FloodAdapt.
267
283
 
268
284
  Attributes
269
285
  ----------
@@ -285,8 +301,8 @@ class ConfigModel(BaseModel):
285
301
  The list of aggregation area models.
286
302
  building_footprints : Optional[SpatialJoinModel | FootprintsOptions], default FootprintsOptions.OSM
287
303
  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.
304
+ fiat_buildings_name : str | list[str], default "buildings"
305
+ The name(s) of the buildings geometry in the FIAT model.
290
306
  fiat_roads_name : Optional[str], default "roads"
291
307
  The name of the roads geometry in the FIAT model.
292
308
  bfe : Optional[SpatialJoinModel], default None
@@ -343,21 +359,14 @@ class ConfigModel(BaseModel):
343
359
  fiat_roads_name: Optional[str] = "roads"
344
360
  bfe: Optional[SpatialJoinModel] = None
345
361
  svi: Optional[SviConfigModel] = None
346
- road_width: Optional[float] = 5
362
+ road_width: us.UnitfulLength = us.UnitfulLength(
363
+ value=5.0, units=us.UnitTypesLength.meters
364
+ )
347
365
  return_periods: list[int] = Field(default_factory=list)
348
366
  floodmap_type: Optional[FloodmapType] = None
349
367
 
350
368
  # SFINCS
351
- references: WaterlevelReferenceModel = WaterlevelReferenceModel(
352
- reference="MSL",
353
- datums=[
354
- DatumModel(
355
- name="MSL",
356
- height=us.UnitfulLength(value=0.0, units=us.UnitTypesLength.meters),
357
- ),
358
- ],
359
- )
360
-
369
+ references: Optional[WaterlevelReferenceModel] = None
361
370
  sfincs_overland: FloodModel
362
371
  sfincs_offshore: Optional[FloodModel] = None
363
372
  dem: Optional[DemModel] = None
@@ -373,17 +382,18 @@ class ConfigModel(BaseModel):
373
382
  probabilistic_set: Optional[str] = None
374
383
 
375
384
  @staticmethod
376
- def read(toml_path: Path) -> "ConfigModel":
385
+ def read(toml_path: Union[str, Path]) -> "ConfigModel":
377
386
  """
378
387
  Read a configuration file and returns the validated attributes.
379
388
 
380
389
  Args:
381
- config (str): The path to the configuration file.
390
+ toml_path (str | Path): The path to the configuration file.
382
391
 
383
392
  Returns
384
393
  -------
385
394
  ConfigModel: The validated attributes from the configuration file.
386
395
  """
396
+ toml_path = Path(toml_path)
387
397
  with open(toml_path, mode="rb") as fp:
388
398
  toml = tomli.load(fp)
389
399
  config = ConfigModel.model_validate(toml)
@@ -428,7 +438,7 @@ class DatabaseBuilder:
428
438
  _aggregation_areas: Optional[list] = None
429
439
  _probabilistic_set_name: Optional[str] = None
430
440
 
431
- def __init__(self, config: ConfigModel, overwrite: bool = True):
441
+ def __init__(self, config: ConfigModel):
432
442
  self.config = config
433
443
 
434
444
  # Set database root
@@ -442,9 +452,6 @@ class DatabaseBuilder:
442
452
  # Read info that needs to be used to create other models
443
453
  self.unit_system = self.create_default_units()
444
454
 
445
- # Read info that needs to be updated with other model info
446
- self.water_level_references = self.config.references
447
-
448
455
  @property
449
456
  def static_path(self) -> Path:
450
457
  return self.root / "static"
@@ -495,6 +502,9 @@ class DatabaseBuilder:
495
502
  self.read_template_sfincs_overland_model()
496
503
  self.read_template_sfincs_offshore_model()
497
504
 
505
+ # Copy standard static files
506
+ self.add_static_files()
507
+
498
508
  @debug_timer
499
509
  def set_standard_objects(self):
500
510
  # Define name and create object
@@ -787,13 +797,12 @@ class DatabaseBuilder:
787
797
  self.fiat_model.exposure.exposure_db = exposure
788
798
 
789
799
  def read_damage_unit(self) -> str:
790
- if self.fiat_model.exposure.damage_unit is not None:
791
- return self.fiat_model.exposure.damage_unit
792
- else:
800
+ if self.fiat_model.exposure.damage_unit is None:
793
801
  logger.warning(
794
802
  "Delft-FIAT model was missing damage units so '$' was assumed."
795
803
  )
796
- return "$"
804
+ self.fiat_model.exposure.damage_unit = "$"
805
+ return self.fiat_model.exposure.damage_unit
797
806
 
798
807
  @debug_timer
799
808
  def read_floodmap_type(self) -> FloodmapType:
@@ -833,9 +842,8 @@ class DatabaseBuilder:
833
842
  # TODO should this should be performed through hydromt-FIAT?
834
843
  if not isinstance(roads.geometry.iloc[0], Polygon):
835
844
  roads = roads.to_crs(roads.estimate_utm_crs())
836
- roads.geometry = roads.geometry.buffer(
837
- self.config.road_width / 2, cap_style=2
838
- )
845
+ road_width = self.config.road_width.convert(us.UnitTypesLength.meters)
846
+ roads.geometry = roads.geometry.buffer(road_width / 2, cap_style=2)
839
847
  roads = roads.to_crs(self.fiat_model.exposure.crs)
840
848
  self.fiat_model.exposure.exposure_geoms[self._get_fiat_road_index()] = roads
841
849
  logger.info(
@@ -1028,9 +1036,10 @@ class DatabaseBuilder:
1028
1036
  aggr_name = Path(aggr.file).stem
1029
1037
  # If aggregation area already in FIAT model raise Error
1030
1038
  if aggr_name in [aggr.name for aggr in aggregation_areas]:
1031
- raise ValueError(
1032
- f"Aggregation area '{aggr_name}' already exists in the FIAT model."
1039
+ logger.warning(
1040
+ f"Aggregation area '{aggr_name}' already exists in the FIAT model. The input aggregation area will be ignored."
1033
1041
  )
1042
+ continue
1034
1043
  # Do spatial join of FIAT objects and aggregation areas
1035
1044
  exposure_csv = self.fiat_model.exposure.exposure_db
1036
1045
  gdf = self._get_fiat_gdf_full()
@@ -1039,6 +1048,7 @@ class DatabaseBuilder:
1039
1048
  layer=str(self._check_exists_and_absolute(aggr.file)),
1040
1049
  field_name=aggr.field_name,
1041
1050
  rename=_FIAT_COLUMNS.aggregation_label.format(name=aggr_name),
1051
+ filter=True,
1042
1052
  )
1043
1053
  aggr_path = Path(self.fiat_model.root).joinpath(
1044
1054
  "exposure", "aggregation_areas", f"{Path(aggr.file).stem}.gpkg"
@@ -1072,6 +1082,9 @@ class DatabaseBuilder:
1072
1082
  ),
1073
1083
  )
1074
1084
  )
1085
+ logger.info(
1086
+ f"Aggregation areas: {aggr_name} provided in the config are going to be used."
1087
+ )
1075
1088
 
1076
1089
  # No config provided, no aggr areas in the model -> try to use the region file as a mock aggregation area
1077
1090
  if (
@@ -1183,7 +1196,8 @@ class DatabaseBuilder:
1183
1196
  def create_sfincs_config(self) -> SfincsModel:
1184
1197
  # call these functions before others to make sure water level references are updated
1185
1198
  config = self.create_sfincs_model_config()
1186
- tide_gauge = self.create_tide_gauge()
1199
+ self.water_level_references = self.create_water_level_references()
1200
+ self.tide_gauge = self.create_tide_gauge()
1187
1201
 
1188
1202
  sfincs = SfincsModel(
1189
1203
  config=config,
@@ -1192,13 +1206,42 @@ class DatabaseBuilder:
1192
1206
  dem=self.create_dem_model(),
1193
1207
  scs=self.create_scs_model(),
1194
1208
  cyclone_track_database=self.create_cyclone_track_database(),
1195
- tide_gauge=tide_gauge,
1209
+ tide_gauge=self.tide_gauge,
1196
1210
  river=self.create_rivers(),
1197
1211
  obs_point=self.create_observation_points(),
1198
1212
  )
1199
1213
 
1200
1214
  return sfincs
1201
1215
 
1216
+ @debug_timer
1217
+ def create_water_level_references(self) -> WaterlevelReferenceModel:
1218
+ sfincs_ref = self.config.sfincs_overland.reference
1219
+ if self.config.references is None:
1220
+ logger.warning(
1221
+ f"No water level references provided in the config file. Using reference provided for overland SFINCS model '{sfincs_ref}' as the main reference."
1222
+ )
1223
+ refs = WaterlevelReferenceModel(
1224
+ reference=sfincs_ref,
1225
+ datums=[
1226
+ DatumModel(
1227
+ name=sfincs_ref,
1228
+ height=us.UnitfulLength(
1229
+ value=0.0, units=self.unit_system.default_length_units
1230
+ ),
1231
+ )
1232
+ ],
1233
+ )
1234
+ else:
1235
+ # Check if sfincs_ref is in the references
1236
+ if sfincs_ref not in [ref.name for ref in self.config.references.datums]:
1237
+ raise ValueError(
1238
+ f"Reference '{sfincs_ref}' not found in the provided references."
1239
+ )
1240
+ else:
1241
+ refs = self.config.references
1242
+
1243
+ return refs
1244
+
1202
1245
  @debug_timer
1203
1246
  def create_cyclone_track_database(self) -> Optional[CycloneTrackDatabaseModel]:
1204
1247
  if not self.config.cyclones or not self.config.sfincs_offshore:
@@ -1219,7 +1262,9 @@ class DatabaseBuilder:
1219
1262
  try:
1220
1263
  urlretrieve(url, fn)
1221
1264
  except Exception:
1222
- raise RuntimeError(f"Could not retrieve cyclone track database from {url}")
1265
+ logger.warning(f"Could not retrieve cyclone track database from {url}")
1266
+ logger.warning("No cyclones will be available in the database.")
1267
+ return None
1223
1268
 
1224
1269
  return CycloneTrackDatabaseModel(file=name)
1225
1270
 
@@ -1315,10 +1360,43 @@ class DatabaseBuilder:
1315
1360
  @debug_timer
1316
1361
  def create_observation_points(self) -> Union[list[ObsPointModel], None]:
1317
1362
  if self.config.obs_point is None:
1318
- return None
1363
+ obs_points = []
1364
+ else:
1365
+ logger.info("Observation points were provided in the config file.")
1366
+ obs_points = self.config.obs_point
1367
+ if self.tide_gauge is not None:
1368
+ # Check if the tide gauge point is within the SFINCS region
1369
+ region = self.sfincs_overland_model.region
1370
+ point = gpd.GeoSeries(
1371
+ [gpd.points_from_xy([self.tide_gauge.lon], [self.tide_gauge.lat])[0]],
1372
+ crs=4326,
1373
+ )
1374
+ region_4326 = region.to_crs(4326)
1375
+ if not point.within(region_4326.unary_union).item():
1376
+ logger.warning(
1377
+ "The tide gauge location is outside the SFINCS region and will not be added as an observation point."
1378
+ )
1379
+ else:
1380
+ logger.info(
1381
+ "A tide gauge has been setup in the database. It will be used as an observation point as well."
1382
+ )
1383
+ obs_points.append(
1384
+ ObsPointModel(
1385
+ name=self.tide_gauge.name,
1386
+ description="Tide gauge observation point",
1387
+ ID=self.tide_gauge.ID,
1388
+ lon=self.tide_gauge.lon,
1389
+ lat=self.tide_gauge.lat,
1390
+ )
1391
+ )
1319
1392
 
1320
- logger.info("Observation points were provided in the config file.")
1321
- return self.config.obs_point
1393
+ if not obs_points:
1394
+ logger.warning(
1395
+ "No observation points were provided in the config file or created from the tide gauge. No observation points will be available in FloodAdapt."
1396
+ )
1397
+ return None
1398
+ else:
1399
+ return obs_points
1322
1400
 
1323
1401
  @debug_timer
1324
1402
  def create_rivers(self) -> list[RiverModel]:
@@ -1370,9 +1448,6 @@ class DatabaseBuilder:
1370
1448
  logger.warning(
1371
1449
  "Tide gauge information not provided. Historical events will not have an option to use gauged data in FloodAdapt!"
1372
1450
  )
1373
- logger.warning(
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."
1375
- )
1376
1451
  return None
1377
1452
 
1378
1453
  if self.config.tide_gauge.source == TideGaugeSource.file:
@@ -1382,9 +1457,16 @@ class DatabaseBuilder:
1382
1457
  )
1383
1458
  if self.config.tide_gauge.ref is None:
1384
1459
  logger.warning(
1385
- "Tide gauge reference not provided. MSL is assumed as the reference of the water levels in the file."
1460
+ f"Tide gauge reference not provided. '{self.water_level_references.reference}' is assumed as the reference of the water levels in the file."
1386
1461
  )
1387
- self.config.tide_gauge.ref = "MSL"
1462
+ self.config.tide_gauge.ref = self.water_level_references.reference
1463
+ else:
1464
+ if self.config.tide_gauge.ref not in [
1465
+ datum.name for datum in self.water_level_references.datums
1466
+ ]:
1467
+ raise ValueError(
1468
+ f"Provided tide gauge reference '{self.config.tide_gauge.ref}' not found in the water level references!"
1469
+ )
1388
1470
 
1389
1471
  tide_gauge_file = self._check_exists_and_absolute(
1390
1472
  self.config.tide_gauge.file
@@ -1416,10 +1498,6 @@ class DatabaseBuilder:
1416
1498
  else:
1417
1499
  ref = "MLLW" # If reference is not provided use MLLW
1418
1500
 
1419
- self.water_level_references.reference = (
1420
- ref # update the water level reference
1421
- )
1422
-
1423
1501
  if self.config.tide_gauge.id is None:
1424
1502
  station_id = self._get_closest_station()
1425
1503
  logger.info(
@@ -1432,6 +1510,80 @@ class DatabaseBuilder:
1432
1510
  )
1433
1511
  station = self._get_station_metadata(station_id=station_id, ref=ref)
1434
1512
  if station is not None:
1513
+ # First create water level references based on station
1514
+ # Get datums
1515
+ datums = []
1516
+ # Get local datum
1517
+ datums.append(
1518
+ DatumModel(
1519
+ name=station["datum_name"],
1520
+ height=us.UnitfulLength(
1521
+ value=station["datum"], units=station["units"]
1522
+ ).transform(self.unit_system.default_length_units),
1523
+ )
1524
+ )
1525
+ # Get MSL
1526
+ datums.append(
1527
+ DatumModel(
1528
+ name="MSL",
1529
+ height=us.UnitfulLength(
1530
+ value=station["msl"], units=station["units"]
1531
+ ).transform(self.unit_system.default_length_units),
1532
+ )
1533
+ )
1534
+ # Get extras
1535
+ for name in ["MLLW", "MHHW"]:
1536
+ height = us.UnitfulLength(
1537
+ value=station[name.lower()], units=station["units"]
1538
+ ).transform(self.unit_system.default_length_units)
1539
+
1540
+ wl_info = DatumModel(
1541
+ name=name,
1542
+ height=height,
1543
+ )
1544
+ datums.append(wl_info)
1545
+
1546
+ station_refs = WaterlevelReferenceModel(reference=ref, datums=datums)
1547
+
1548
+ # Check if we can translate the rest of the datums
1549
+ if self.water_level_references.reference != station_refs.reference:
1550
+ for dat in self.water_level_references.datums:
1551
+ if dat.name not in [
1552
+ datum.name for datum in station_refs.datums
1553
+ ]:
1554
+ # If datum is not in the datums, try to convert it
1555
+ h1 = dat.height
1556
+ ref1 = self.water_level_references.reference
1557
+ h2 = h1 + station_refs.get_datum(ref1).height
1558
+ # Replace the datum in self.water_level_references.datums
1559
+ dat.height = h2
1560
+ logger.warning(
1561
+ f"Datum '{dat.name}' converted to reference '{ref1}' with new height {h2}."
1562
+ )
1563
+
1564
+ # Check if datums already exist in the water level references and replace
1565
+ for datum in datums:
1566
+ existing_datum = next(
1567
+ (
1568
+ dat
1569
+ for dat in self.water_level_references.datums
1570
+ if dat.name == datum.name
1571
+ ),
1572
+ None,
1573
+ )
1574
+ if existing_datum:
1575
+ self.water_level_references.datums.remove(existing_datum)
1576
+ logger.warning(
1577
+ f"Datum '{datum.name}' already exists in config reference. Replacing it based on NOAA station data."
1578
+ )
1579
+ self.water_level_references.datums.append(datum)
1580
+
1581
+ # Update reference datum
1582
+ self.water_level_references.reference = (
1583
+ ref # update the water level reference
1584
+ )
1585
+ logger.warning(f"Main water level reference set to '{ref}'.")
1586
+
1435
1587
  # Add tide_gauge information in site toml
1436
1588
  tide_gauge = TideGauge(
1437
1589
  name=station["name"],
@@ -1444,43 +1596,6 @@ class DatabaseBuilder:
1444
1596
  units=us.UnitTypesLength.meters, # the api always asks for SI units right now
1445
1597
  )
1446
1598
 
1447
- local_datum = DatumModel(
1448
- name=station["datum_name"],
1449
- height=us.UnitfulLength(
1450
- value=station["datum"], units=station["units"]
1451
- ).transform(self.unit_system.default_length_units),
1452
- )
1453
- self.water_level_references.datums.append(local_datum)
1454
-
1455
- msl = DatumModel(
1456
- name="MSL",
1457
- height=us.UnitfulLength(
1458
- value=station["msl"], units=station["units"]
1459
- ).transform(self.unit_system.default_length_units),
1460
- )
1461
- # Check if MSL is already there and if yes replace it
1462
- existing_msl = next(
1463
- (
1464
- datum
1465
- for datum in self.water_level_references.datums
1466
- if datum.name == "MSL"
1467
- ),
1468
- None,
1469
- )
1470
- if existing_msl:
1471
- self.water_level_references.datums.remove(existing_msl)
1472
- self.water_level_references.datums.append(msl)
1473
-
1474
- for name in ["MLLW", "MHHW"]:
1475
- height = us.UnitfulLength(
1476
- value=station[name.lower()], units=station["units"]
1477
- ).transform(self.unit_system.default_length_units)
1478
-
1479
- wl_info = DatumModel(
1480
- name=name,
1481
- height=height,
1482
- )
1483
- self.water_level_references.datums.append(wl_info)
1484
1599
  return tide_gauge
1485
1600
  else:
1486
1601
  logger.warning(
@@ -1843,6 +1958,33 @@ class DatabaseBuilder:
1843
1958
  path_1 = self.static_path / folder
1844
1959
  shutil.copytree(path_0, path_1)
1845
1960
 
1961
+ # Check table values
1962
+ green_infra_path = (
1963
+ self.static_path / "green_infra_table" / "green_infra_lookup_table.csv"
1964
+ )
1965
+ df = pd.read_csv(green_infra_path)
1966
+ # Convert "Infiltration depth (feet)" to the database unit system and rename column
1967
+ # Find the column that has "Infiltration depth" in its name
1968
+ infiltration_col = next(
1969
+ (col for col in df.columns if "Infiltration depth" in col), None
1970
+ )
1971
+ # Try to infer the units from the column name, e.g., "Infiltration depth (feet)"
1972
+ match = re.search(r"\((.*?)\)", infiltration_col)
1973
+ current_units = match.group(1).strip()
1974
+
1975
+ # Determine target units and column name
1976
+ if self.unit_system.default_length_units != current_units:
1977
+ target_units = self.unit_system.default_length_units
1978
+ new_col = f"Infiltration depth ({target_units.value})"
1979
+ conversion_factor = us.UnitfulLength(
1980
+ value=1.0, units=current_units
1981
+ ).convert(target_units)
1982
+
1983
+ df[new_col] = (df[infiltration_col] * conversion_factor).round(2)
1984
+ df = df.drop(columns=[infiltration_col])
1985
+ # Save the updated table
1986
+ df.to_csv(green_infra_path, index=False)
1987
+
1846
1988
  @debug_timer
1847
1989
  def add_probabilistic_set(self):
1848
1990
  # Copy prob set if given
@@ -1993,7 +2135,8 @@ class DatabaseBuilder:
1993
2135
 
1994
2136
  Parameters
1995
2137
  ----------
1996
- None
2138
+ clip_footprints : bool, default True
2139
+ Whether to clip the building footprints to the hazard area.
1997
2140
 
1998
2141
  Returns
1999
2142
  -------
@@ -2159,6 +2302,7 @@ class DatabaseBuilder:
2159
2302
  Find the closest tide gauge station to the SFINCS domain and retrieves its metadata.
2160
2303
 
2161
2304
  Args:
2305
+ station_id (str): The ID of the tide gauge station.
2162
2306
  ref (str, optional): The reference level for water level measurements. Defaults to "MLLW".
2163
2307
 
2164
2308
  Returns
@@ -2205,7 +2349,7 @@ class DatabaseBuilder:
2205
2349
  )
2206
2350
 
2207
2351
  logger.info(
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}."
2352
+ f"The station metadata will be used to fill in the water_level attribute in the site.toml. The reference level will be '{ref}'."
2209
2353
  )
2210
2354
 
2211
2355
  return meta
@@ -2260,6 +2404,22 @@ class DatabaseBuilder:
2260
2404
  return gdf
2261
2405
 
2262
2406
 
2407
+ def create_database(config: Union[str, Path, ConfigModel], overwrite=False) -> None:
2408
+ """Create a new database from a configuration file or ConfigModel.
2409
+
2410
+ Parameters
2411
+ ----------
2412
+ config : str, Path, or ConfigModel
2413
+ The path to the configuration file (as a string or Path) or a ConfigModel instance.
2414
+ overwrite : bool, default False
2415
+ Whether to overwrite the existing database if it exists.
2416
+ """
2417
+ if isinstance(config, (str, Path)):
2418
+ config = ConfigModel.read(config)
2419
+
2420
+ DatabaseBuilder(config=config).build(overwrite)
2421
+
2422
+
2263
2423
  def main():
2264
2424
  while True:
2265
2425
  config_path = Path(
@@ -84,7 +84,7 @@
84
84
  [Other.Info]
85
85
  text="""Inundation depth thresholds:<br>
86
86
  Minor: 0.25 m<br>
87
- Major: 2.0 m<br>
88
- Destroyed: >2.0 m"""
87
+ Major: 1.5 m<br>
88
+ Destroyed: >1.5 m"""
89
89
  image="https://openclipart.org/image/800px/302413"
90
90
  scale=0.1
@@ -54,7 +54,7 @@
54
54
  [Other.Subtitle]
55
55
  font = 25
56
56
  [Other.Y_axis_title]
57
- text = "Kilometers of interruption"
57
+ text = "Kilometers"
58
58
  [Other.Info]
59
59
  text="""Thresholds:<br>
60
60
  Pedestrians: 0.05 m<br>
@@ -54,7 +54,7 @@
54
54
  [Other.Subtitle]
55
55
  font = 25
56
56
  [Other.Y_axis_title]
57
- text = "Miles of interruption"
57
+ text = "Miles"
58
58
  [Other.Info]
59
59
  text="""Thresholds:<br>
60
60
  Pedestrians: 0.3 feet<br>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: flood-adapt
3
- Version: 0.3.12
3
+ Version: 0.3.14
4
4
  Summary: A software package support system which can be used to assess the benefits and costs of flood resilience measures
5
5
  Author-email: Gundula Winter <Gundula.Winter@deltares.nl>, Panos Athanasiou <Panos.Athanasiou@deltares.nl>, Frederique de Groen <Frederique.deGroen@deltares.nl>, Tim de Wilde <Tim.deWilde@deltares.nl>, Julian Hofer <Julian.Hofer@deltares.nl>, Daley Adrichem <Daley.Adrichem@deltares.nl>, Luuk Blom <Luuk.Blom@deltares.nl>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -1,9 +1,8 @@
1
- flood_adapt/__init__.py,sha256=TXChGK-h0gjHlNBp_CxcwBXWia75E2dUO20jRwiKdEo,875
2
- flood_adapt/database_builder.py,sha256=E0JPsPuq2PHhCzH0cp7kDEkRYG3XQBjlCaEoK0GGttc,436
1
+ flood_adapt/__init__.py,sha256=q8w6NmihIIoZF7Zr8qUZzX7ZOBNFAFb5GGLsJIUDyEw,819
3
2
  flood_adapt/flood_adapt.py,sha256=szovzFyApBeT-XPMZ-GNsWH-ap63ejouP7hZZjadMVU,38364
4
3
  flood_adapt/adapter/__init__.py,sha256=UuiuVz5vKDoXnvBUMJ7Yybk0pjZkYLREgTA4G1Pzj8c,258
5
- flood_adapt/adapter/fiat_adapter.py,sha256=QVGjVX1BOhpxVvKEsJ5p3cn5Ssj0M_GjQZXAoaiGKzY,59596
6
- flood_adapt/adapter/sfincs_adapter.py,sha256=n3Agc7Tx809KRvbJJ1nllwvLl9gYNVjbqWraEHCvTSw,79152
4
+ flood_adapt/adapter/fiat_adapter.py,sha256=UsCY2zUM_ihRb-606ZbUlgWCJOKaxgcm3Q3FbhbYQXQ,60149
5
+ flood_adapt/adapter/sfincs_adapter.py,sha256=4Je6xd_6CWTQpqqvC8kB6QdCOiv0AD4WY5eFbCgJz74,79244
7
6
  flood_adapt/adapter/sfincs_offshore.py,sha256=QWJBD1ReQuVrQn3enyQeKR-YTHTQmGxzrLOjnOJkths,7486
8
7
  flood_adapt/adapter/interface/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
8
  flood_adapt/adapter/interface/hazard_adapter.py,sha256=S2NIUAMSRgxC_E-tZRJ2qIP06U1zEVdn-MnvMTrn86s,2828
@@ -16,8 +15,8 @@ flood_adapt/config/fiat.py,sha256=2w3StKeTg6-PHAz6fG8_br4QhzxWgMgbg-YQ4MVuhCk,62
16
15
  flood_adapt/config/gui.py,sha256=vCmmfApHBN7_XAK1Cu7qh0C2WURWsyC3fhL-6Ri2-Lk,10524
17
16
  flood_adapt/config/sfincs.py,sha256=oj-YtjOM58UpcoBlqlMHhEBSHP-C_yyuefxNoewv9OU,16876
18
17
  flood_adapt/config/site.py,sha256=VR90jCHWcxgoQJptNyXy7LseGjXUDRtdOjNGCddFVzI,4328
19
- flood_adapt/database_builder/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- flood_adapt/database_builder/database_builder.py,sha256=Ny60W5U_nVQlcBaaW73DlElBEnSP5EhmuOKmWruwjqs,91623
18
+ flood_adapt/database_builder/__init__.py,sha256=YsI5bGcAKYmsmb5W-spp91hzsKSTRtkXBLNRxLOWml4,474
19
+ flood_adapt/database_builder/database_builder.py,sha256=IbrahJVk8HoVZm9gdr0VdQxvnIWsPZeeJu9suQVfxyc,98880
21
20
  flood_adapt/database_builder/templates/default_units/imperial.toml,sha256=zIjPlxIa2kWLUjSYisd8UolXGo5iKdFoDDz_JkKBXTM,295
22
21
  flood_adapt/database_builder/templates/default_units/metric.toml,sha256=tc0XMKs7xGL9noB9lAb0gyQfjYxzokgHa3NqpccxWl0,302
23
22
  flood_adapt/database_builder/templates/green_infra_table/green_infra_lookup_table.csv,sha256=ooQzNGQwAMSAphy5W2ZyR5boQlcwvPv9ToJx1MlZhVE,466
@@ -39,15 +38,15 @@ flood_adapt/database_builder/templates/icons/white_down_48x48.png,sha256=ZxXz4WK
39
38
  flood_adapt/database_builder/templates/icons/white_left_48x48.png,sha256=mLjrGhsMo79EyaeEK9Ykmym-knv2LHk92JTMWaAfaw0,738
40
39
  flood_adapt/database_builder/templates/icons/white_right_48x48.png,sha256=dxYJT-zTp8KXLcjRkd4toZjkPrFCjDwBbSmqPspw9Qk,753
41
40
  flood_adapt/database_builder/templates/icons/white_up_48x48.png,sha256=eahYlRiknCFlIfA26BJQl__o74cqwuKTRRZ35SgCXcY,733
42
- flood_adapt/database_builder/templates/infographics/OSM/config_charts.toml,sha256=m6O7MXGSmA-F82nSzD2O4EMGSrmAf8Y8S0IDOIVFmiM,2608
41
+ flood_adapt/database_builder/templates/infographics/OSM/config_charts.toml,sha256=bHvgceR1Gb05jIv7Rk2NFNWowtFn4ztVTgF4z_OOYOk,2608
43
42
  flood_adapt/database_builder/templates/infographics/OSM/config_people.toml,sha256=k-0q5reU-0LA0QMlT5Mat8mldH9t37fMSt2pUZ0P0do,1754
44
43
  flood_adapt/database_builder/templates/infographics/OSM/config_risk_charts.toml,sha256=kwBVwsSY-D3ADuJdLxFQlSLuhxguxdNXk-dn-Pt_ud8,3804
45
- flood_adapt/database_builder/templates/infographics/OSM/config_roads.toml,sha256=pcdHavl1gGFY5AEU_907tmyUDtZaHDu_Uf3cEvCpnKA,1890
44
+ flood_adapt/database_builder/templates/infographics/OSM/config_roads.toml,sha256=VFlCpP2KzGlfBPoblNdL6YlJEmccUvIlUgLaOtwxwrc,1874
46
45
  flood_adapt/database_builder/templates/infographics/OSM/styles.css,sha256=yV8U2Z1DwuyfaAL13iQHjEJcPiDHXCUzGt2REXlSKeg,762
47
46
  flood_adapt/database_builder/templates/infographics/US_NSI/config_charts.toml,sha256=7mRXeh7-pvfsHp-gFdubQXR33nqaSyzKuJVc1WILdJU,3762
48
47
  flood_adapt/database_builder/templates/infographics/US_NSI/config_people.toml,sha256=9VJ0rzIj4YZIYWATQZYmbO6KLBB-C9i7skBOvE0M3oA,1892
49
48
  flood_adapt/database_builder/templates/infographics/US_NSI/config_risk_charts.toml,sha256=kwBVwsSY-D3ADuJdLxFQlSLuhxguxdNXk-dn-Pt_ud8,3804
50
- flood_adapt/database_builder/templates/infographics/US_NSI/config_roads.toml,sha256=8xabCxMA9-NksDJOAQOyFLK1Y5Wwg-h0mGuF6oDBvK8,1889
49
+ flood_adapt/database_builder/templates/infographics/US_NSI/config_roads.toml,sha256=T1QqW95D0MTeWMLDV85WdodXrIf0T7uIRZA9FU0l_is,1873
51
50
  flood_adapt/database_builder/templates/infographics/US_NSI/styles.css,sha256=yV8U2Z1DwuyfaAL13iQHjEJcPiDHXCUzGt2REXlSKeg,762
52
51
  flood_adapt/database_builder/templates/infographics/images/ambulance.png,sha256=dw5vcU_kRePrBA3Zl4_aQFz1Rc93x26y8PelY2MJ_Lo,7757
53
52
  flood_adapt/database_builder/templates/infographics/images/car.png,sha256=PFED6xR31G2P15ZGM14CyJeca7zfUlwQe2348bpPCxM,6600
@@ -133,8 +132,8 @@ flood_adapt/workflows/benefit_runner.py,sha256=VtYt0sHFymNyErpzOtuN55cKJGVm5hT2a
133
132
  flood_adapt/workflows/floodmap.py,sha256=8PMd9PBep4nkrOtavW_UXeLU15J7AZMk1QEonQxVNk0,3017
134
133
  flood_adapt/workflows/impacts_integrator.py,sha256=zPJrb3Fm6gXPCmlA3vMh-2nmIwH_DWGrJGS1BUDqHLU,2495
135
134
  flood_adapt/workflows/scenario_runner.py,sha256=o6x_p7oF0apmo6Zq-_4uBXVeL6saRJZlIX6lTCPN2Rc,2984
136
- flood_adapt-0.3.12.dist-info/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
137
- flood_adapt-0.3.12.dist-info/METADATA,sha256=7pl6lx3fcbpZLShAHYkWqIJgDMccSjhEyhODvG-11aA,52578
138
- flood_adapt-0.3.12.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
139
- flood_adapt-0.3.12.dist-info/top_level.txt,sha256=JvzMi6cTcQPEThCfpgMEeVny3ghI1urSH0CCgVIqSzw,12
140
- flood_adapt-0.3.12.dist-info/RECORD,,
135
+ flood_adapt-0.3.14.dist-info/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
136
+ flood_adapt-0.3.14.dist-info/METADATA,sha256=yUXia0KxJTxDdnw2ZLeqhRs_Hwm1NWcx5RQANUTs1TM,52578
137
+ flood_adapt-0.3.14.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
138
+ flood_adapt-0.3.14.dist-info/top_level.txt,sha256=JvzMi6cTcQPEThCfpgMEeVny3ghI1urSH0CCgVIqSzw,12
139
+ flood_adapt-0.3.14.dist-info/RECORD,,
@@ -1,16 +0,0 @@
1
- from pathlib import Path
2
-
3
- from flood_adapt.database_builder.database_builder import ConfigModel, DatabaseBuilder
4
-
5
-
6
- def create_database(config_path: Path) -> None:
7
- """Create a new database from a configuration file or dictionary.
8
-
9
- Parameters
10
- ----------
11
- config : Path
12
- The path to the configuration file
13
- """
14
- config = ConfigModel.read(config_path)
15
-
16
- DatabaseBuilder(config=config).build()