flood-adapt 0.3.9__py3-none-any.whl → 0.3.10__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 +26 -22
- flood_adapt/adapter/__init__.py +9 -9
- flood_adapt/adapter/fiat_adapter.py +1541 -1541
- flood_adapt/adapter/interface/hazard_adapter.py +70 -70
- flood_adapt/adapter/interface/impact_adapter.py +36 -36
- flood_adapt/adapter/interface/model_adapter.py +89 -89
- flood_adapt/adapter/interface/offshore.py +19 -19
- flood_adapt/adapter/sfincs_adapter.py +1848 -1848
- flood_adapt/adapter/sfincs_offshore.py +193 -193
- flood_adapt/config/config.py +248 -248
- flood_adapt/config/fiat.py +219 -219
- flood_adapt/config/gui.py +331 -331
- flood_adapt/config/sfincs.py +481 -336
- flood_adapt/config/site.py +129 -129
- flood_adapt/database_builder/database_builder.py +2210 -2210
- flood_adapt/database_builder/templates/default_units/imperial.toml +9 -9
- flood_adapt/database_builder/templates/default_units/metric.toml +9 -9
- flood_adapt/database_builder/templates/green_infra_table/green_infra_lookup_table.csv +10 -10
- flood_adapt/database_builder/templates/infographics/OSM/config_charts.toml +90 -90
- flood_adapt/database_builder/templates/infographics/OSM/config_people.toml +57 -57
- flood_adapt/database_builder/templates/infographics/OSM/config_risk_charts.toml +121 -121
- flood_adapt/database_builder/templates/infographics/OSM/config_roads.toml +65 -65
- flood_adapt/database_builder/templates/infographics/OSM/styles.css +45 -45
- flood_adapt/database_builder/templates/infographics/US_NSI/config_charts.toml +126 -126
- flood_adapt/database_builder/templates/infographics/US_NSI/config_people.toml +60 -60
- flood_adapt/database_builder/templates/infographics/US_NSI/config_risk_charts.toml +121 -121
- flood_adapt/database_builder/templates/infographics/US_NSI/config_roads.toml +65 -65
- flood_adapt/database_builder/templates/infographics/US_NSI/styles.css +45 -45
- flood_adapt/database_builder/templates/infometrics/OSM/metrics_additional_risk_configs.toml +4 -4
- flood_adapt/database_builder/templates/infometrics/OSM/with_SVI/infographic_metrics_config.toml +143 -143
- flood_adapt/database_builder/templates/infometrics/OSM/with_SVI/infographic_metrics_config_risk.toml +153 -153
- flood_adapt/database_builder/templates/infometrics/OSM/without_SVI/infographic_metrics_config.toml +127 -127
- flood_adapt/database_builder/templates/infometrics/OSM/without_SVI/infographic_metrics_config_risk.toml +57 -57
- flood_adapt/database_builder/templates/infometrics/US_NSI/metrics_additional_risk_configs.toml +4 -4
- flood_adapt/database_builder/templates/infometrics/US_NSI/with_SVI/infographic_metrics_config.toml +191 -191
- flood_adapt/database_builder/templates/infometrics/US_NSI/with_SVI/infographic_metrics_config_risk.toml +153 -153
- flood_adapt/database_builder/templates/infometrics/US_NSI/without_SVI/infographic_metrics_config.toml +178 -178
- flood_adapt/database_builder/templates/infometrics/US_NSI/without_SVI/infographic_metrics_config_risk.toml +57 -57
- flood_adapt/database_builder/templates/infometrics/mandatory_metrics_config.toml +9 -9
- flood_adapt/database_builder/templates/infometrics/mandatory_metrics_config_risk.toml +65 -65
- flood_adapt/database_builder/templates/output_layers/bin_colors.toml +5 -5
- flood_adapt/database_builder.py +16 -16
- flood_adapt/dbs_classes/__init__.py +21 -21
- flood_adapt/dbs_classes/database.py +495 -684
- flood_adapt/dbs_classes/dbs_benefit.py +77 -76
- flood_adapt/dbs_classes/dbs_event.py +61 -59
- flood_adapt/dbs_classes/dbs_measure.py +112 -111
- flood_adapt/dbs_classes/dbs_projection.py +34 -34
- flood_adapt/dbs_classes/dbs_scenario.py +137 -137
- flood_adapt/dbs_classes/dbs_static.py +274 -273
- flood_adapt/dbs_classes/dbs_strategy.py +130 -129
- flood_adapt/dbs_classes/dbs_template.py +279 -278
- flood_adapt/dbs_classes/interface/database.py +107 -139
- flood_adapt/dbs_classes/interface/element.py +121 -121
- flood_adapt/dbs_classes/interface/static.py +47 -47
- flood_adapt/flood_adapt.py +1207 -1178
- flood_adapt/misc/database_user.py +16 -16
- flood_adapt/misc/exceptions.py +22 -0
- flood_adapt/misc/log.py +183 -183
- flood_adapt/misc/path_builder.py +54 -54
- flood_adapt/misc/utils.py +185 -185
- flood_adapt/objects/__init__.py +82 -82
- flood_adapt/objects/benefits/benefits.py +61 -61
- flood_adapt/objects/events/event_factory.py +135 -135
- flood_adapt/objects/events/event_set.py +88 -84
- flood_adapt/objects/events/events.py +234 -234
- flood_adapt/objects/events/historical.py +58 -58
- flood_adapt/objects/events/hurricane.py +68 -67
- flood_adapt/objects/events/synthetic.py +46 -50
- flood_adapt/objects/forcing/__init__.py +92 -92
- flood_adapt/objects/forcing/csv.py +68 -68
- flood_adapt/objects/forcing/discharge.py +66 -66
- flood_adapt/objects/forcing/forcing.py +150 -150
- flood_adapt/objects/forcing/forcing_factory.py +182 -182
- flood_adapt/objects/forcing/meteo_handler.py +93 -93
- flood_adapt/objects/forcing/netcdf.py +40 -40
- flood_adapt/objects/forcing/plotting.py +453 -429
- flood_adapt/objects/forcing/rainfall.py +98 -98
- flood_adapt/objects/forcing/tide_gauge.py +191 -191
- flood_adapt/objects/forcing/time_frame.py +90 -90
- flood_adapt/objects/forcing/timeseries.py +564 -564
- flood_adapt/objects/forcing/unit_system.py +580 -580
- flood_adapt/objects/forcing/waterlevels.py +108 -108
- flood_adapt/objects/forcing/wind.py +124 -124
- flood_adapt/objects/measures/measure_factory.py +92 -92
- flood_adapt/objects/measures/measures.py +529 -529
- flood_adapt/objects/object_model.py +74 -68
- flood_adapt/objects/projections/projections.py +103 -103
- flood_adapt/objects/scenarios/scenarios.py +22 -22
- flood_adapt/objects/strategies/strategies.py +89 -89
- flood_adapt/workflows/benefit_runner.py +579 -554
- flood_adapt/workflows/floodmap.py +85 -85
- flood_adapt/workflows/impacts_integrator.py +85 -85
- flood_adapt/workflows/scenario_runner.py +70 -70
- {flood_adapt-0.3.9.dist-info → flood_adapt-0.3.10.dist-info}/LICENSE +674 -674
- {flood_adapt-0.3.9.dist-info → flood_adapt-0.3.10.dist-info}/METADATA +866 -865
- flood_adapt-0.3.10.dist-info/RECORD +140 -0
- flood_adapt-0.3.9.dist-info/RECORD +0 -139
- {flood_adapt-0.3.9.dist-info → flood_adapt-0.3.10.dist-info}/WHEEL +0 -0
- {flood_adapt-0.3.9.dist-info → flood_adapt-0.3.10.dist-info}/top_level.txt +0 -0
flood_adapt/config/gui.py
CHANGED
|
@@ -1,331 +1,331 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
from typing import Optional
|
|
3
|
-
|
|
4
|
-
import geopandas as gpd
|
|
5
|
-
import numpy as np
|
|
6
|
-
import pandas as pd
|
|
7
|
-
import tomli
|
|
8
|
-
from pydantic import BaseModel, Field, model_validator
|
|
9
|
-
|
|
10
|
-
from flood_adapt.config.fiat import DamageType
|
|
11
|
-
from flood_adapt.objects.forcing import unit_system as us
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class Layer(BaseModel):
|
|
15
|
-
"""
|
|
16
|
-
Base class for layers in the GUI.
|
|
17
|
-
|
|
18
|
-
Attributes
|
|
19
|
-
----------
|
|
20
|
-
bins : list[float]
|
|
21
|
-
The bins for the layer.
|
|
22
|
-
colors : list[str]
|
|
23
|
-
The colors for the layer.
|
|
24
|
-
"""
|
|
25
|
-
|
|
26
|
-
bins: list[float]
|
|
27
|
-
colors: list[str]
|
|
28
|
-
|
|
29
|
-
@model_validator(mode="after")
|
|
30
|
-
def check_bins_and_colors(self) -> "Layer":
|
|
31
|
-
"""Check that the bins and colors have the same length."""
|
|
32
|
-
if (len(self.bins) + 1) != len(self.colors):
|
|
33
|
-
raise ValueError(
|
|
34
|
-
f"Number of bins ({len(self.bins)}) must be one less than number of colors ({len(self.colors)})"
|
|
35
|
-
)
|
|
36
|
-
return self
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
class FloodMapLayer(Layer):
|
|
40
|
-
zbmax: float
|
|
41
|
-
depth_min: float
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
class AggregationDmgLayer(Layer):
|
|
45
|
-
damage_decimals: Optional[int] = 0
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
class FootprintsDmgLayer(Layer):
|
|
49
|
-
type: DamageType = DamageType.absolute
|
|
50
|
-
damage_decimals: Optional[int] = 0
|
|
51
|
-
buildings_min_zoom_level: int = 13
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
class BenefitsLayer(Layer):
|
|
55
|
-
threshold: Optional[float] = None
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
class OutputLayers(BaseModel):
|
|
59
|
-
"""The configuration of the mapbox layers in the gui.
|
|
60
|
-
|
|
61
|
-
Attributes
|
|
62
|
-
----------
|
|
63
|
-
floodmap : FloodMapLayer
|
|
64
|
-
The configuration of the floodmap layer.
|
|
65
|
-
aggregation_dmg : AggregationDmgLayer
|
|
66
|
-
The configuration of the aggregation damage layer.
|
|
67
|
-
footprints_dmg : FootprintsDmgLayer
|
|
68
|
-
The configuration of the footprints damage layer.
|
|
69
|
-
|
|
70
|
-
benefits : BenefitsLayer
|
|
71
|
-
The configuration of the benefits layer.
|
|
72
|
-
"""
|
|
73
|
-
|
|
74
|
-
floodmap: FloodMapLayer
|
|
75
|
-
aggregation_dmg: AggregationDmgLayer
|
|
76
|
-
footprints_dmg: FootprintsDmgLayer
|
|
77
|
-
|
|
78
|
-
benefits: Optional[BenefitsLayer] = None
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
class VisualizationLayer(Layer):
|
|
82
|
-
"""The configuration of a layer to visualize in the gui.
|
|
83
|
-
|
|
84
|
-
name : str
|
|
85
|
-
The name of the layer to visualize.
|
|
86
|
-
long_name : str
|
|
87
|
-
The long name of the layer to visualize.
|
|
88
|
-
path : str
|
|
89
|
-
The path to the layer data to visualize.
|
|
90
|
-
field_name : str
|
|
91
|
-
The field names of the layer to visualize.
|
|
92
|
-
decimals : Optional[int]
|
|
93
|
-
The number of decimals to use for the layer to visualize. default is None.
|
|
94
|
-
"""
|
|
95
|
-
|
|
96
|
-
name: str
|
|
97
|
-
long_name: str
|
|
98
|
-
path: str
|
|
99
|
-
field_name: str
|
|
100
|
-
decimals: Optional[int] = None
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
_DEFAULT_BIN_NR = 4
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
def interpolate_hex_colors(
|
|
107
|
-
start_hex="#FFFFFF", end_hex="#860000", number_bins=_DEFAULT_BIN_NR
|
|
108
|
-
):
|
|
109
|
-
"""
|
|
110
|
-
Interpolate between two hex colors and returns a list of number_bins hex color codes.
|
|
111
|
-
|
|
112
|
-
Parameters
|
|
113
|
-
----------
|
|
114
|
-
start_hex : str
|
|
115
|
-
Starting color in hex format (e.g., "#FFFFFF").
|
|
116
|
-
end_hex : str
|
|
117
|
-
Ending color in hex format (e.g., "#000000").
|
|
118
|
-
number_bins : int
|
|
119
|
-
Number of colors to generate between the start and end colors.
|
|
120
|
-
|
|
121
|
-
Returns
|
|
122
|
-
-------
|
|
123
|
-
list[str]
|
|
124
|
-
List of hex color codes interpolated between the start and end colors.
|
|
125
|
-
"""
|
|
126
|
-
|
|
127
|
-
def hex_to_rgb(hex_color):
|
|
128
|
-
hex_color = hex_color.lstrip("#")
|
|
129
|
-
return tuple(int(hex_color[i : i + 2], 16) for i in (0, 2, 4))
|
|
130
|
-
|
|
131
|
-
def rgb_to_hex(rgb_color):
|
|
132
|
-
return "#{:02X}{:02X}{:02X}".format(*rgb_color)
|
|
133
|
-
|
|
134
|
-
start_rgb = hex_to_rgb(start_hex)
|
|
135
|
-
end_rgb = hex_to_rgb(end_hex)
|
|
136
|
-
|
|
137
|
-
interpolated_colors = []
|
|
138
|
-
for i in range(number_bins):
|
|
139
|
-
ratio = i / (number_bins - 1) if number_bins > 1 else 0
|
|
140
|
-
interpolated_rgb = tuple(
|
|
141
|
-
int(start + (end - start) * ratio) for start, end in zip(start_rgb, end_rgb)
|
|
142
|
-
)
|
|
143
|
-
interpolated_colors.append(rgb_to_hex(interpolated_rgb))
|
|
144
|
-
|
|
145
|
-
return interpolated_colors
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
class VisualizationLayers(BaseModel):
|
|
149
|
-
"""The configuration of the layers you might want to visualize in the gui.
|
|
150
|
-
|
|
151
|
-
Attributes
|
|
152
|
-
----------
|
|
153
|
-
default : Layer
|
|
154
|
-
The default layer settings the visualization layers.
|
|
155
|
-
layers : list[VisualizationLayer]
|
|
156
|
-
The layers to visualize.
|
|
157
|
-
"""
|
|
158
|
-
|
|
159
|
-
layers: list[VisualizationLayer] = Field(default_factory=list)
|
|
160
|
-
|
|
161
|
-
def add_layer(
|
|
162
|
-
self,
|
|
163
|
-
name: str,
|
|
164
|
-
long_name: str,
|
|
165
|
-
path: str,
|
|
166
|
-
field_name: str,
|
|
167
|
-
database_path: Path,
|
|
168
|
-
decimals: Optional[int] = None,
|
|
169
|
-
bins: Optional[list[float]] = None,
|
|
170
|
-
colors: Optional[list[str]] = None,
|
|
171
|
-
) -> None:
|
|
172
|
-
if not Path(path).is_absolute():
|
|
173
|
-
raise ValueError(f"Path {path} must be absolute.")
|
|
174
|
-
|
|
175
|
-
data = gpd.read_file(path)
|
|
176
|
-
if field_name not in data.columns:
|
|
177
|
-
raise ValueError(
|
|
178
|
-
f"Field name {field_name} not found in data. Available fields: {data.columns.tolist()}"
|
|
179
|
-
)
|
|
180
|
-
|
|
181
|
-
if bins is None:
|
|
182
|
-
_, _bins = pd.qcut(
|
|
183
|
-
data[field_name], _DEFAULT_BIN_NR, retbins=True, duplicates="drop"
|
|
184
|
-
)
|
|
185
|
-
bins = _bins.tolist()[1:-1]
|
|
186
|
-
|
|
187
|
-
if decimals is None:
|
|
188
|
-
non_zero_bins = [abs(b) for b in bins if b != 0]
|
|
189
|
-
min_non_zero = min(non_zero_bins) if non_zero_bins else 1
|
|
190
|
-
decimals = max(int(-np.floor(np.log10(min_non_zero))), 0)
|
|
191
|
-
|
|
192
|
-
if colors is None:
|
|
193
|
-
nr_bins = len(bins) + 1
|
|
194
|
-
colors = interpolate_hex_colors(number_bins=nr_bins)
|
|
195
|
-
|
|
196
|
-
relative_path = Path(path).relative_to(database_path / "static")
|
|
197
|
-
self.layers.append(
|
|
198
|
-
VisualizationLayer(
|
|
199
|
-
bins=bins,
|
|
200
|
-
colors=colors,
|
|
201
|
-
name=name,
|
|
202
|
-
long_name=long_name,
|
|
203
|
-
path=relative_path.as_posix(),
|
|
204
|
-
field_name=field_name,
|
|
205
|
-
decimals=decimals,
|
|
206
|
-
)
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
class GuiUnitModel(BaseModel):
|
|
211
|
-
"""The unit system used in the GUI.
|
|
212
|
-
|
|
213
|
-
Attributes
|
|
214
|
-
----------
|
|
215
|
-
default_length_units : us.UnitTypesLength
|
|
216
|
-
The length units used in the GUI.
|
|
217
|
-
default_distance_units : us.UnitTypesLength
|
|
218
|
-
The distance units used in the GUI.
|
|
219
|
-
default_area_units : us.UnitTypesArea
|
|
220
|
-
The area units used in the GUI.
|
|
221
|
-
default_volume_units : us.UnitTypesVolume
|
|
222
|
-
The volume units used in the GUI.
|
|
223
|
-
default_velocity_units : us.UnitTypesVelocity
|
|
224
|
-
The velocity units used in the GUI.
|
|
225
|
-
default_direction_units : us.UnitTypesDirection
|
|
226
|
-
The direction units used in the GUI.
|
|
227
|
-
default_discharge_units : us.UnitTypesDischarge
|
|
228
|
-
The discharge units used in the GUI.
|
|
229
|
-
default_intensity_units : us.UnitTypesIntensity
|
|
230
|
-
The intensity units used in the GUI.
|
|
231
|
-
default_cumulative_units : us.UnitTypesLength
|
|
232
|
-
The cumulative units used in the GUI.
|
|
233
|
-
"""
|
|
234
|
-
|
|
235
|
-
default_length_units: us.UnitTypesLength
|
|
236
|
-
default_distance_units: us.UnitTypesLength
|
|
237
|
-
default_area_units: us.UnitTypesArea
|
|
238
|
-
default_volume_units: us.UnitTypesVolume
|
|
239
|
-
default_velocity_units: us.UnitTypesVelocity
|
|
240
|
-
default_direction_units: us.UnitTypesDirection
|
|
241
|
-
default_discharge_units: us.UnitTypesDischarge
|
|
242
|
-
default_intensity_units: us.UnitTypesIntensity
|
|
243
|
-
default_cumulative_units: us.UnitTypesLength
|
|
244
|
-
|
|
245
|
-
@staticmethod
|
|
246
|
-
def imperial() -> "GuiUnitModel":
|
|
247
|
-
return GuiUnitModel(
|
|
248
|
-
default_length_units=us.UnitTypesLength.feet,
|
|
249
|
-
default_distance_units=us.UnitTypesLength.miles,
|
|
250
|
-
default_area_units=us.UnitTypesArea.sf,
|
|
251
|
-
default_volume_units=us.UnitTypesVolume.cf,
|
|
252
|
-
default_velocity_units=us.UnitTypesVelocity.mph,
|
|
253
|
-
default_direction_units=us.UnitTypesDirection.degrees,
|
|
254
|
-
default_discharge_units=us.UnitTypesDischarge.cfs,
|
|
255
|
-
default_intensity_units=us.UnitTypesIntensity.inch_hr,
|
|
256
|
-
default_cumulative_units=us.UnitTypesLength.inch,
|
|
257
|
-
)
|
|
258
|
-
|
|
259
|
-
@staticmethod
|
|
260
|
-
def metric() -> "GuiUnitModel":
|
|
261
|
-
return GuiUnitModel(
|
|
262
|
-
default_length_units=us.UnitTypesLength.meters,
|
|
263
|
-
default_distance_units=us.UnitTypesLength.meters,
|
|
264
|
-
default_area_units=us.UnitTypesArea.m2,
|
|
265
|
-
default_volume_units=us.UnitTypesVolume.m3,
|
|
266
|
-
default_velocity_units=us.UnitTypesVelocity.mps,
|
|
267
|
-
default_direction_units=us.UnitTypesDirection.degrees,
|
|
268
|
-
default_discharge_units=us.UnitTypesDischarge.cms,
|
|
269
|
-
default_intensity_units=us.UnitTypesIntensity.mm_hr,
|
|
270
|
-
default_cumulative_units=us.UnitTypesLength.millimeters,
|
|
271
|
-
)
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
class SyntheticTideModel(BaseModel):
|
|
275
|
-
"""Configuration for the synthetic tide.
|
|
276
|
-
|
|
277
|
-
Parameters
|
|
278
|
-
----------
|
|
279
|
-
harmonic_amplitude : us.UnitfulLength
|
|
280
|
-
The amplitude of the tide harmonic relative to the datum.
|
|
281
|
-
datum : str
|
|
282
|
-
The datum to which the harmonic amplitude is relative.
|
|
283
|
-
Should be a datum defined in `site.sfincs.waterlevels.datums`
|
|
284
|
-
"""
|
|
285
|
-
|
|
286
|
-
harmonic_amplitude: us.UnitfulLength
|
|
287
|
-
datum: str
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
class PlottingModel(BaseModel):
|
|
291
|
-
"""
|
|
292
|
-
The configuration of the plotting in the gui.
|
|
293
|
-
|
|
294
|
-
Attributes
|
|
295
|
-
----------
|
|
296
|
-
excluded_datums : list[str]
|
|
297
|
-
A list of datums that will be excluded from the forcing plot in event windows.
|
|
298
|
-
synthetic_tide : SyntheticTideModel
|
|
299
|
-
The configuration of the synthetic tide.
|
|
300
|
-
"""
|
|
301
|
-
|
|
302
|
-
synthetic_tide: SyntheticTideModel
|
|
303
|
-
excluded_datums: list[str] = Field(default_factory=list)
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
class GuiModel(BaseModel):
|
|
307
|
-
"""The accepted input for the variable gui in Site.
|
|
308
|
-
|
|
309
|
-
Attributes
|
|
310
|
-
----------
|
|
311
|
-
units : GuiUnitModel
|
|
312
|
-
The unit system used in the GUI.
|
|
313
|
-
output_layers : OutputLayers
|
|
314
|
-
The configuration of the mapbox layers in the GUI.
|
|
315
|
-
visualization_layers : VisualizationLayers
|
|
316
|
-
The configuration of the visualization layers in the GUI.
|
|
317
|
-
plotting : PlottingModel
|
|
318
|
-
The configuration for creating hazard forcing plots.
|
|
319
|
-
"""
|
|
320
|
-
|
|
321
|
-
units: GuiUnitModel
|
|
322
|
-
output_layers: OutputLayers
|
|
323
|
-
visualization_layers: VisualizationLayers
|
|
324
|
-
plotting: PlottingModel
|
|
325
|
-
|
|
326
|
-
@staticmethod
|
|
327
|
-
def read_toml(path: Path) -> "GuiModel":
|
|
328
|
-
with open(path, mode="rb") as fp:
|
|
329
|
-
toml_contents = tomli.load(fp)
|
|
330
|
-
|
|
331
|
-
return GuiModel(**toml_contents)
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
import geopandas as gpd
|
|
5
|
+
import numpy as np
|
|
6
|
+
import pandas as pd
|
|
7
|
+
import tomli
|
|
8
|
+
from pydantic import BaseModel, Field, model_validator
|
|
9
|
+
|
|
10
|
+
from flood_adapt.config.fiat import DamageType
|
|
11
|
+
from flood_adapt.objects.forcing import unit_system as us
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Layer(BaseModel):
|
|
15
|
+
"""
|
|
16
|
+
Base class for layers in the GUI.
|
|
17
|
+
|
|
18
|
+
Attributes
|
|
19
|
+
----------
|
|
20
|
+
bins : list[float]
|
|
21
|
+
The bins for the layer.
|
|
22
|
+
colors : list[str]
|
|
23
|
+
The colors for the layer.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
bins: list[float]
|
|
27
|
+
colors: list[str]
|
|
28
|
+
|
|
29
|
+
@model_validator(mode="after")
|
|
30
|
+
def check_bins_and_colors(self) -> "Layer":
|
|
31
|
+
"""Check that the bins and colors have the same length."""
|
|
32
|
+
if (len(self.bins) + 1) != len(self.colors):
|
|
33
|
+
raise ValueError(
|
|
34
|
+
f"Number of bins ({len(self.bins)}) must be one less than number of colors ({len(self.colors)})"
|
|
35
|
+
)
|
|
36
|
+
return self
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class FloodMapLayer(Layer):
|
|
40
|
+
zbmax: float
|
|
41
|
+
depth_min: float
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class AggregationDmgLayer(Layer):
|
|
45
|
+
damage_decimals: Optional[int] = 0
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class FootprintsDmgLayer(Layer):
|
|
49
|
+
type: DamageType = DamageType.absolute
|
|
50
|
+
damage_decimals: Optional[int] = 0
|
|
51
|
+
buildings_min_zoom_level: int = 13
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class BenefitsLayer(Layer):
|
|
55
|
+
threshold: Optional[float] = None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class OutputLayers(BaseModel):
|
|
59
|
+
"""The configuration of the mapbox layers in the gui.
|
|
60
|
+
|
|
61
|
+
Attributes
|
|
62
|
+
----------
|
|
63
|
+
floodmap : FloodMapLayer
|
|
64
|
+
The configuration of the floodmap layer.
|
|
65
|
+
aggregation_dmg : AggregationDmgLayer
|
|
66
|
+
The configuration of the aggregation damage layer.
|
|
67
|
+
footprints_dmg : FootprintsDmgLayer
|
|
68
|
+
The configuration of the footprints damage layer.
|
|
69
|
+
|
|
70
|
+
benefits : BenefitsLayer
|
|
71
|
+
The configuration of the benefits layer.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
floodmap: FloodMapLayer
|
|
75
|
+
aggregation_dmg: AggregationDmgLayer
|
|
76
|
+
footprints_dmg: FootprintsDmgLayer
|
|
77
|
+
|
|
78
|
+
benefits: Optional[BenefitsLayer] = None
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class VisualizationLayer(Layer):
|
|
82
|
+
"""The configuration of a layer to visualize in the gui.
|
|
83
|
+
|
|
84
|
+
name : str
|
|
85
|
+
The name of the layer to visualize.
|
|
86
|
+
long_name : str
|
|
87
|
+
The long name of the layer to visualize.
|
|
88
|
+
path : str
|
|
89
|
+
The path to the layer data to visualize.
|
|
90
|
+
field_name : str
|
|
91
|
+
The field names of the layer to visualize.
|
|
92
|
+
decimals : Optional[int]
|
|
93
|
+
The number of decimals to use for the layer to visualize. default is None.
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
name: str
|
|
97
|
+
long_name: str
|
|
98
|
+
path: str
|
|
99
|
+
field_name: str
|
|
100
|
+
decimals: Optional[int] = None
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
_DEFAULT_BIN_NR = 4
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def interpolate_hex_colors(
|
|
107
|
+
start_hex="#FFFFFF", end_hex="#860000", number_bins=_DEFAULT_BIN_NR
|
|
108
|
+
):
|
|
109
|
+
"""
|
|
110
|
+
Interpolate between two hex colors and returns a list of number_bins hex color codes.
|
|
111
|
+
|
|
112
|
+
Parameters
|
|
113
|
+
----------
|
|
114
|
+
start_hex : str
|
|
115
|
+
Starting color in hex format (e.g., "#FFFFFF").
|
|
116
|
+
end_hex : str
|
|
117
|
+
Ending color in hex format (e.g., "#000000").
|
|
118
|
+
number_bins : int
|
|
119
|
+
Number of colors to generate between the start and end colors.
|
|
120
|
+
|
|
121
|
+
Returns
|
|
122
|
+
-------
|
|
123
|
+
list[str]
|
|
124
|
+
List of hex color codes interpolated between the start and end colors.
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
def hex_to_rgb(hex_color):
|
|
128
|
+
hex_color = hex_color.lstrip("#")
|
|
129
|
+
return tuple(int(hex_color[i : i + 2], 16) for i in (0, 2, 4))
|
|
130
|
+
|
|
131
|
+
def rgb_to_hex(rgb_color):
|
|
132
|
+
return "#{:02X}{:02X}{:02X}".format(*rgb_color)
|
|
133
|
+
|
|
134
|
+
start_rgb = hex_to_rgb(start_hex)
|
|
135
|
+
end_rgb = hex_to_rgb(end_hex)
|
|
136
|
+
|
|
137
|
+
interpolated_colors = []
|
|
138
|
+
for i in range(number_bins):
|
|
139
|
+
ratio = i / (number_bins - 1) if number_bins > 1 else 0
|
|
140
|
+
interpolated_rgb = tuple(
|
|
141
|
+
int(start + (end - start) * ratio) for start, end in zip(start_rgb, end_rgb)
|
|
142
|
+
)
|
|
143
|
+
interpolated_colors.append(rgb_to_hex(interpolated_rgb))
|
|
144
|
+
|
|
145
|
+
return interpolated_colors
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class VisualizationLayers(BaseModel):
|
|
149
|
+
"""The configuration of the layers you might want to visualize in the gui.
|
|
150
|
+
|
|
151
|
+
Attributes
|
|
152
|
+
----------
|
|
153
|
+
default : Layer
|
|
154
|
+
The default layer settings the visualization layers.
|
|
155
|
+
layers : list[VisualizationLayer]
|
|
156
|
+
The layers to visualize.
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
layers: list[VisualizationLayer] = Field(default_factory=list)
|
|
160
|
+
|
|
161
|
+
def add_layer(
|
|
162
|
+
self,
|
|
163
|
+
name: str,
|
|
164
|
+
long_name: str,
|
|
165
|
+
path: str,
|
|
166
|
+
field_name: str,
|
|
167
|
+
database_path: Path,
|
|
168
|
+
decimals: Optional[int] = None,
|
|
169
|
+
bins: Optional[list[float]] = None,
|
|
170
|
+
colors: Optional[list[str]] = None,
|
|
171
|
+
) -> None:
|
|
172
|
+
if not Path(path).is_absolute():
|
|
173
|
+
raise ValueError(f"Path {path} must be absolute.")
|
|
174
|
+
|
|
175
|
+
data = gpd.read_file(path)
|
|
176
|
+
if field_name not in data.columns:
|
|
177
|
+
raise ValueError(
|
|
178
|
+
f"Field name {field_name} not found in data. Available fields: {data.columns.tolist()}"
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
if bins is None:
|
|
182
|
+
_, _bins = pd.qcut(
|
|
183
|
+
data[field_name], _DEFAULT_BIN_NR, retbins=True, duplicates="drop"
|
|
184
|
+
)
|
|
185
|
+
bins = _bins.tolist()[1:-1]
|
|
186
|
+
|
|
187
|
+
if decimals is None:
|
|
188
|
+
non_zero_bins = [abs(b) for b in bins if b != 0]
|
|
189
|
+
min_non_zero = min(non_zero_bins) if non_zero_bins else 1
|
|
190
|
+
decimals = max(int(-np.floor(np.log10(min_non_zero))), 0)
|
|
191
|
+
|
|
192
|
+
if colors is None:
|
|
193
|
+
nr_bins = len(bins) + 1
|
|
194
|
+
colors = interpolate_hex_colors(number_bins=nr_bins)
|
|
195
|
+
|
|
196
|
+
relative_path = Path(path).relative_to(database_path / "static")
|
|
197
|
+
self.layers.append(
|
|
198
|
+
VisualizationLayer(
|
|
199
|
+
bins=bins,
|
|
200
|
+
colors=colors,
|
|
201
|
+
name=name,
|
|
202
|
+
long_name=long_name,
|
|
203
|
+
path=relative_path.as_posix(),
|
|
204
|
+
field_name=field_name,
|
|
205
|
+
decimals=decimals,
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
class GuiUnitModel(BaseModel):
|
|
211
|
+
"""The unit system used in the GUI.
|
|
212
|
+
|
|
213
|
+
Attributes
|
|
214
|
+
----------
|
|
215
|
+
default_length_units : us.UnitTypesLength
|
|
216
|
+
The length units used in the GUI.
|
|
217
|
+
default_distance_units : us.UnitTypesLength
|
|
218
|
+
The distance units used in the GUI.
|
|
219
|
+
default_area_units : us.UnitTypesArea
|
|
220
|
+
The area units used in the GUI.
|
|
221
|
+
default_volume_units : us.UnitTypesVolume
|
|
222
|
+
The volume units used in the GUI.
|
|
223
|
+
default_velocity_units : us.UnitTypesVelocity
|
|
224
|
+
The velocity units used in the GUI.
|
|
225
|
+
default_direction_units : us.UnitTypesDirection
|
|
226
|
+
The direction units used in the GUI.
|
|
227
|
+
default_discharge_units : us.UnitTypesDischarge
|
|
228
|
+
The discharge units used in the GUI.
|
|
229
|
+
default_intensity_units : us.UnitTypesIntensity
|
|
230
|
+
The intensity units used in the GUI.
|
|
231
|
+
default_cumulative_units : us.UnitTypesLength
|
|
232
|
+
The cumulative units used in the GUI.
|
|
233
|
+
"""
|
|
234
|
+
|
|
235
|
+
default_length_units: us.UnitTypesLength
|
|
236
|
+
default_distance_units: us.UnitTypesLength
|
|
237
|
+
default_area_units: us.UnitTypesArea
|
|
238
|
+
default_volume_units: us.UnitTypesVolume
|
|
239
|
+
default_velocity_units: us.UnitTypesVelocity
|
|
240
|
+
default_direction_units: us.UnitTypesDirection
|
|
241
|
+
default_discharge_units: us.UnitTypesDischarge
|
|
242
|
+
default_intensity_units: us.UnitTypesIntensity
|
|
243
|
+
default_cumulative_units: us.UnitTypesLength
|
|
244
|
+
|
|
245
|
+
@staticmethod
|
|
246
|
+
def imperial() -> "GuiUnitModel":
|
|
247
|
+
return GuiUnitModel(
|
|
248
|
+
default_length_units=us.UnitTypesLength.feet,
|
|
249
|
+
default_distance_units=us.UnitTypesLength.miles,
|
|
250
|
+
default_area_units=us.UnitTypesArea.sf,
|
|
251
|
+
default_volume_units=us.UnitTypesVolume.cf,
|
|
252
|
+
default_velocity_units=us.UnitTypesVelocity.mph,
|
|
253
|
+
default_direction_units=us.UnitTypesDirection.degrees,
|
|
254
|
+
default_discharge_units=us.UnitTypesDischarge.cfs,
|
|
255
|
+
default_intensity_units=us.UnitTypesIntensity.inch_hr,
|
|
256
|
+
default_cumulative_units=us.UnitTypesLength.inch,
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
@staticmethod
|
|
260
|
+
def metric() -> "GuiUnitModel":
|
|
261
|
+
return GuiUnitModel(
|
|
262
|
+
default_length_units=us.UnitTypesLength.meters,
|
|
263
|
+
default_distance_units=us.UnitTypesLength.meters,
|
|
264
|
+
default_area_units=us.UnitTypesArea.m2,
|
|
265
|
+
default_volume_units=us.UnitTypesVolume.m3,
|
|
266
|
+
default_velocity_units=us.UnitTypesVelocity.mps,
|
|
267
|
+
default_direction_units=us.UnitTypesDirection.degrees,
|
|
268
|
+
default_discharge_units=us.UnitTypesDischarge.cms,
|
|
269
|
+
default_intensity_units=us.UnitTypesIntensity.mm_hr,
|
|
270
|
+
default_cumulative_units=us.UnitTypesLength.millimeters,
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
class SyntheticTideModel(BaseModel):
|
|
275
|
+
"""Configuration for the synthetic tide.
|
|
276
|
+
|
|
277
|
+
Parameters
|
|
278
|
+
----------
|
|
279
|
+
harmonic_amplitude : us.UnitfulLength
|
|
280
|
+
The amplitude of the tide harmonic relative to the datum.
|
|
281
|
+
datum : str
|
|
282
|
+
The datum to which the harmonic amplitude is relative.
|
|
283
|
+
Should be a datum defined in `site.sfincs.waterlevels.datums`
|
|
284
|
+
"""
|
|
285
|
+
|
|
286
|
+
harmonic_amplitude: us.UnitfulLength
|
|
287
|
+
datum: str
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
class PlottingModel(BaseModel):
|
|
291
|
+
"""
|
|
292
|
+
The configuration of the plotting in the gui.
|
|
293
|
+
|
|
294
|
+
Attributes
|
|
295
|
+
----------
|
|
296
|
+
excluded_datums : list[str]
|
|
297
|
+
A list of datums that will be excluded from the forcing plot in event windows.
|
|
298
|
+
synthetic_tide : SyntheticTideModel
|
|
299
|
+
The configuration of the synthetic tide.
|
|
300
|
+
"""
|
|
301
|
+
|
|
302
|
+
synthetic_tide: SyntheticTideModel
|
|
303
|
+
excluded_datums: list[str] = Field(default_factory=list)
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
class GuiModel(BaseModel):
|
|
307
|
+
"""The accepted input for the variable gui in Site.
|
|
308
|
+
|
|
309
|
+
Attributes
|
|
310
|
+
----------
|
|
311
|
+
units : GuiUnitModel
|
|
312
|
+
The unit system used in the GUI.
|
|
313
|
+
output_layers : OutputLayers
|
|
314
|
+
The configuration of the mapbox layers in the GUI.
|
|
315
|
+
visualization_layers : VisualizationLayers
|
|
316
|
+
The configuration of the visualization layers in the GUI.
|
|
317
|
+
plotting : PlottingModel
|
|
318
|
+
The configuration for creating hazard forcing plots.
|
|
319
|
+
"""
|
|
320
|
+
|
|
321
|
+
units: GuiUnitModel
|
|
322
|
+
output_layers: OutputLayers
|
|
323
|
+
visualization_layers: VisualizationLayers
|
|
324
|
+
plotting: PlottingModel
|
|
325
|
+
|
|
326
|
+
@staticmethod
|
|
327
|
+
def read_toml(path: Path) -> "GuiModel":
|
|
328
|
+
with open(path, mode="rb") as fp:
|
|
329
|
+
toml_contents = tomli.load(fp)
|
|
330
|
+
|
|
331
|
+
return GuiModel(**toml_contents)
|