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.
- flood_adapt/__init__.py +1 -2
- flood_adapt/adapter/fiat_adapter.py +12 -7
- flood_adapt/adapter/sfincs_adapter.py +20 -23
- flood_adapt/config/fiat.py +1 -1
- flood_adapt/config/gui.py +185 -8
- flood_adapt/config/hazard.py +1 -1
- flood_adapt/database_builder/database_builder.py +155 -129
- flood_adapt/database_builder/metrics_utils.py +1834 -0
- flood_adapt/dbs_classes/database.py +4 -4
- flood_adapt/dbs_classes/dbs_static.py +2 -2
- flood_adapt/flood_adapt.py +65 -14
- flood_adapt/misc/utils.py +29 -10
- flood_adapt/objects/forcing/plotting.py +4 -4
- flood_adapt/objects/measures/measures.py +3 -1
- flood_adapt/workflows/benefit_runner.py +3 -2
- {flood_adapt-1.0.6.dist-info → flood_adapt-1.1.1.dist-info}/METADATA +13 -124
- {flood_adapt-1.0.6.dist-info → flood_adapt-1.1.1.dist-info}/RECORD +21 -41
- flood_adapt/database_builder/templates/infographics/OSM/config_charts.toml +0 -90
- flood_adapt/database_builder/templates/infographics/OSM/config_people.toml +0 -57
- flood_adapt/database_builder/templates/infographics/OSM/config_risk_charts.toml +0 -121
- flood_adapt/database_builder/templates/infographics/OSM/config_roads.toml +0 -65
- flood_adapt/database_builder/templates/infographics/US_NSI/config_charts.toml +0 -126
- flood_adapt/database_builder/templates/infographics/US_NSI/config_people.toml +0 -60
- flood_adapt/database_builder/templates/infographics/US_NSI/config_risk_charts.toml +0 -121
- flood_adapt/database_builder/templates/infographics/US_NSI/config_roads.toml +0 -65
- flood_adapt/database_builder/templates/infographics/US_NSI/styles.css +0 -45
- flood_adapt/database_builder/templates/infometrics/OSM/metrics_additional_risk_configs.toml +0 -4
- flood_adapt/database_builder/templates/infometrics/OSM/with_SVI/infographic_metrics_config.toml +0 -143
- flood_adapt/database_builder/templates/infometrics/OSM/with_SVI/infographic_metrics_config_risk.toml +0 -153
- flood_adapt/database_builder/templates/infometrics/OSM/without_SVI/infographic_metrics_config.toml +0 -127
- flood_adapt/database_builder/templates/infometrics/OSM/without_SVI/infographic_metrics_config_risk.toml +0 -57
- flood_adapt/database_builder/templates/infometrics/US_NSI/metrics_additional_risk_configs.toml +0 -4
- flood_adapt/database_builder/templates/infometrics/US_NSI/with_SVI/infographic_metrics_config.toml +0 -191
- flood_adapt/database_builder/templates/infometrics/US_NSI/with_SVI/infographic_metrics_config_risk.toml +0 -153
- flood_adapt/database_builder/templates/infometrics/US_NSI/without_SVI/infographic_metrics_config.toml +0 -178
- flood_adapt/database_builder/templates/infometrics/US_NSI/without_SVI/infographic_metrics_config_risk.toml +0 -57
- flood_adapt/database_builder/templates/infometrics/mandatory_metrics_config.toml +0 -9
- flood_adapt/database_builder/templates/infometrics/mandatory_metrics_config_risk.toml +0 -65
- /flood_adapt/database_builder/templates/infographics/{OSM/styles.css → styles.css} +0 -0
- {flood_adapt-1.0.6.dist-info → flood_adapt-1.1.1.dist-info}/LICENSE +0 -0
- {flood_adapt-1.0.6.dist-info → flood_adapt-1.1.1.dist-info}/WHEEL +0 -0
- {flood_adapt-1.0.6.dist-info → flood_adapt-1.1.1.dist-info}/top_level.txt +0 -0
flood_adapt/__init__.py
CHANGED
|
@@ -106,7 +106,7 @@ class FiatAdapter(IImpactAdapter):
|
|
|
106
106
|
self.config_base_path = config_base_path
|
|
107
107
|
self.exe_path = exe_path
|
|
108
108
|
self.delete_crashed_runs = delete_crashed_runs
|
|
109
|
-
self._model_root =
|
|
109
|
+
self._model_root = model_root.resolve().as_posix()
|
|
110
110
|
self.fiat_columns = _FIAT_COLUMNS
|
|
111
111
|
self.impact_columns = _IMPACT_COLUMNS # columns of FA impact output
|
|
112
112
|
|
|
@@ -135,7 +135,7 @@ class FiatAdapter(IImpactAdapter):
|
|
|
135
135
|
def read(self, path: Path) -> None:
|
|
136
136
|
"""Read the fiat model from the current model root."""
|
|
137
137
|
if Path(self.model.root).resolve() != Path(path).resolve():
|
|
138
|
-
self.model.set_root(root=
|
|
138
|
+
self.model.set_root(root=path.as_posix(), mode="r")
|
|
139
139
|
self.model.read()
|
|
140
140
|
|
|
141
141
|
def write(self, path_out: Union[str, os.PathLike], overwrite: bool = True) -> None:
|
|
@@ -148,7 +148,7 @@ class FiatAdapter(IImpactAdapter):
|
|
|
148
148
|
|
|
149
149
|
write_mode = "w+" if overwrite else "w"
|
|
150
150
|
with cd(path_out):
|
|
151
|
-
self.model.set_root(root=
|
|
151
|
+
self.model.set_root(root=path_out.as_posix(), mode=write_mode)
|
|
152
152
|
self.model.write()
|
|
153
153
|
|
|
154
154
|
def close_files(self):
|
|
@@ -717,8 +717,8 @@ class FiatAdapter(IImpactAdapter):
|
|
|
717
717
|
conversion_factor = wl_current_units.convert(self.model.exposure.unit)
|
|
718
718
|
|
|
719
719
|
self.model.setup_hazard(
|
|
720
|
-
map_fn=map_fn,
|
|
721
|
-
map_type=map_type,
|
|
720
|
+
map_fn=[Path(p).as_posix() for p in map_fn],
|
|
721
|
+
map_type=map_type.value,
|
|
722
722
|
rp=None,
|
|
723
723
|
crs=None, # change this in new version (maybe to str(floodmap.crs.split(':')[1]))
|
|
724
724
|
nodata=-999, # change this in new version
|
|
@@ -1160,7 +1160,7 @@ class FiatAdapter(IImpactAdapter):
|
|
|
1160
1160
|
object_dir=ObjectDir.measure,
|
|
1161
1161
|
obj_name=measure.name,
|
|
1162
1162
|
path=measure.polygon_file,
|
|
1163
|
-
)
|
|
1163
|
+
).as_posix()
|
|
1164
1164
|
else:
|
|
1165
1165
|
polygon_file = None
|
|
1166
1166
|
|
|
@@ -1171,7 +1171,7 @@ class FiatAdapter(IImpactAdapter):
|
|
|
1171
1171
|
non_building_names=self.config.non_building_names,
|
|
1172
1172
|
aggregation=measure.aggregation_area_type,
|
|
1173
1173
|
aggregation_area_name=measure.aggregation_area_name,
|
|
1174
|
-
polygon_file=
|
|
1174
|
+
polygon_file=polygon_file,
|
|
1175
1175
|
)
|
|
1176
1176
|
|
|
1177
1177
|
return ids
|
|
@@ -1365,6 +1365,11 @@ class FiatAdapter(IImpactAdapter):
|
|
|
1365
1365
|
True,
|
|
1366
1366
|
True,
|
|
1367
1367
|
]
|
|
1368
|
+
metrics_new.loc["Show In Metrics Map", ["EW", "EWEAD", "EWCEAD"]] = [
|
|
1369
|
+
False,
|
|
1370
|
+
True,
|
|
1371
|
+
False,
|
|
1372
|
+
]
|
|
1368
1373
|
metrics_new.loc["Long Name", ["EW", "EWEAD", "EWCEAD"]] = [
|
|
1369
1374
|
"Equity weight",
|
|
1370
1375
|
"Equity weighted expected annual damage",
|
|
@@ -121,7 +121,7 @@ class SfincsAdapter(IHazardAdapter):
|
|
|
121
121
|
self.settings = self.database.site.sfincs
|
|
122
122
|
self.units = self.database.site.gui.units
|
|
123
123
|
self._model = HydromtSfincsModel(
|
|
124
|
-
root=
|
|
124
|
+
root=model_root.resolve().as_posix(),
|
|
125
125
|
mode="r",
|
|
126
126
|
logger=self._setup_sfincs_logger(model_root),
|
|
127
127
|
)
|
|
@@ -130,7 +130,7 @@ class SfincsAdapter(IHazardAdapter):
|
|
|
130
130
|
def read(self, path: Path):
|
|
131
131
|
"""Read the sfincs model from the current model root."""
|
|
132
132
|
if Path(self._model.root).resolve() != Path(path).resolve():
|
|
133
|
-
self._model.set_root(root=
|
|
133
|
+
self._model.set_root(root=path.as_posix(), mode="r")
|
|
134
134
|
self._model.read()
|
|
135
135
|
|
|
136
136
|
def write(self, path_out: Union[str, os.PathLike], overwrite: bool = True):
|
|
@@ -147,7 +147,7 @@ class SfincsAdapter(IHazardAdapter):
|
|
|
147
147
|
|
|
148
148
|
write_mode = "w+" if overwrite else "w"
|
|
149
149
|
with cd(path_out):
|
|
150
|
-
self._model.set_root(root=
|
|
150
|
+
self._model.set_root(root=path_out.as_posix(), mode=write_mode)
|
|
151
151
|
self._model.write()
|
|
152
152
|
|
|
153
153
|
def close_files(self):
|
|
@@ -190,10 +190,16 @@ class SfincsAdapter(IHazardAdapter):
|
|
|
190
190
|
True if the model ran successfully, False otherwise.
|
|
191
191
|
|
|
192
192
|
"""
|
|
193
|
+
sfincs_bin = Settings().sfincs_bin_path
|
|
194
|
+
if not sfincs_bin or not sfincs_bin.exists():
|
|
195
|
+
raise FileNotFoundError(
|
|
196
|
+
f"SFINCS binary not found at {sfincs_bin}. Please check your settings."
|
|
197
|
+
)
|
|
198
|
+
|
|
193
199
|
with cd(path):
|
|
194
200
|
logger.info(f"Running SFINCS in {path}")
|
|
195
201
|
process = subprocess.run(
|
|
196
|
-
|
|
202
|
+
sfincs_bin.as_posix(),
|
|
197
203
|
stdout=subprocess.PIPE,
|
|
198
204
|
stderr=subprocess.PIPE,
|
|
199
205
|
text=True,
|
|
@@ -563,7 +569,7 @@ class SfincsAdapter(IHazardAdapter):
|
|
|
563
569
|
zsmax=floodmap_conversion * zsmax,
|
|
564
570
|
dep=dem_conversion * dem,
|
|
565
571
|
hmin=0.01,
|
|
566
|
-
floodmap_fn=
|
|
572
|
+
floodmap_fn=floodmap_fn.as_posix(),
|
|
567
573
|
)
|
|
568
574
|
|
|
569
575
|
def write_water_level_map(
|
|
@@ -803,7 +809,7 @@ class SfincsAdapter(IHazardAdapter):
|
|
|
803
809
|
zsmax=floodmap_conversion * zsmax,
|
|
804
810
|
dep=dem_conversion * dem,
|
|
805
811
|
hmin=0.01,
|
|
806
|
-
floodmap_fn=
|
|
812
|
+
floodmap_fn=floodmap_fn.as_posix(),
|
|
807
813
|
)
|
|
808
814
|
|
|
809
815
|
######################################
|
|
@@ -1304,14 +1310,10 @@ class SfincsAdapter(IHazardAdapter):
|
|
|
1304
1310
|
)
|
|
1305
1311
|
return
|
|
1306
1312
|
|
|
1307
|
-
model_rivers = self._read_river_locations()
|
|
1308
|
-
if model_rivers.empty:
|
|
1309
|
-
logger.warning(
|
|
1310
|
-
"Cannot add discharge forcing: No rivers defined in the sfincs model."
|
|
1311
|
-
)
|
|
1312
|
-
return
|
|
1313
1313
|
logger.info(f"Setting discharge forcing for river: {discharge.river.name}")
|
|
1314
|
+
|
|
1314
1315
|
time_frame = self.get_model_time()
|
|
1316
|
+
model_rivers = self._read_river_locations()
|
|
1315
1317
|
|
|
1316
1318
|
# Check that the river is defined in the model and that the coordinates match
|
|
1317
1319
|
river_loc = shapely.Point(
|
|
@@ -1630,7 +1632,7 @@ class SfincsAdapter(IHazardAdapter):
|
|
|
1630
1632
|
|
|
1631
1633
|
# Initialize the tropical cyclone
|
|
1632
1634
|
tc = TropicalCyclone()
|
|
1633
|
-
tc.read_track(filename=
|
|
1635
|
+
tc.read_track(filename=track_forcing.path.as_posix(), fmt="ddb_cyc")
|
|
1634
1636
|
|
|
1635
1637
|
# Alter the track of the tc if necessary
|
|
1636
1638
|
tc = self._translate_tc_track(
|
|
@@ -1709,22 +1711,17 @@ class SfincsAdapter(IHazardAdapter):
|
|
|
1709
1711
|
|
|
1710
1712
|
def _read_river_locations(self) -> gpd.GeoDataFrame:
|
|
1711
1713
|
path = self.get_model_root() / "sfincs.src"
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
lines = f.readlines()
|
|
1714
|
+
|
|
1715
|
+
with open(path) as f:
|
|
1716
|
+
lines = f.readlines()
|
|
1716
1717
|
coords = [(float(line.split()[0]), float(line.split()[1])) for line in lines]
|
|
1717
1718
|
points = [shapely.Point(coord) for coord in coords]
|
|
1718
1719
|
|
|
1719
1720
|
return gpd.GeoDataFrame({"geometry": points}, crs=self._model.crs)
|
|
1720
1721
|
|
|
1721
1722
|
def _read_waterlevel_boundary_locations(self) -> gpd.GeoDataFrame:
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
if path.exists():
|
|
1725
|
-
with open(path) as f:
|
|
1726
|
-
lines = f.readlines()
|
|
1727
|
-
|
|
1723
|
+
with open(self.get_model_root() / "sfincs.bnd") as f:
|
|
1724
|
+
lines = f.readlines()
|
|
1728
1725
|
coords = [(float(line.split()[0]), float(line.split()[1])) for line in lines]
|
|
1729
1726
|
points = [shapely.Point(coord) for coord in coords]
|
|
1730
1727
|
|
flood_adapt/config/fiat.py
CHANGED
flood_adapt/config/gui.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from enum import Enum
|
|
1
3
|
from pathlib import Path
|
|
2
|
-
from typing import Optional
|
|
4
|
+
from typing import Any, Literal, Optional, Union
|
|
3
5
|
|
|
4
6
|
import geopandas as gpd
|
|
5
7
|
import numpy as np
|
|
@@ -25,6 +27,7 @@ class Layer(BaseModel):
|
|
|
25
27
|
|
|
26
28
|
bins: list[float]
|
|
27
29
|
colors: list[str]
|
|
30
|
+
decimals: Optional[int] = 0
|
|
28
31
|
|
|
29
32
|
@model_validator(mode="after")
|
|
30
33
|
def check_bins_and_colors(self) -> "Layer":
|
|
@@ -42,20 +45,118 @@ class FloodMapLayer(Layer):
|
|
|
42
45
|
roads_min_zoom_level: int = 14
|
|
43
46
|
|
|
44
47
|
|
|
45
|
-
class AggregationDmgLayer(Layer):
|
|
46
|
-
damage_decimals: Optional[int] = 0
|
|
47
|
-
|
|
48
|
-
|
|
49
48
|
class FootprintsDmgLayer(Layer):
|
|
50
49
|
type: DamageType = DamageType.absolute
|
|
51
|
-
damage_decimals: Optional[int] = 0
|
|
52
|
-
buildings_min_zoom_level: int = 13
|
|
53
50
|
|
|
54
51
|
|
|
55
52
|
class BenefitsLayer(Layer):
|
|
56
53
|
threshold: Optional[float] = None
|
|
57
54
|
|
|
58
55
|
|
|
56
|
+
class LogicalOperator(str, Enum):
|
|
57
|
+
AND = "and"
|
|
58
|
+
OR = "or"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class FieldName(str, Enum):
|
|
62
|
+
"""Enum for valid field names with mapping to dictionary keys."""
|
|
63
|
+
|
|
64
|
+
NAME = "name"
|
|
65
|
+
LONG_NAME = "long_name"
|
|
66
|
+
DESCRIPTION = "description"
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def dict_key(self) -> str:
|
|
70
|
+
"""Get the actual dictionary key for this field name."""
|
|
71
|
+
mapping = {
|
|
72
|
+
"name": "name",
|
|
73
|
+
"long_name": "Long Name",
|
|
74
|
+
"description": "Description",
|
|
75
|
+
}
|
|
76
|
+
return mapping[self.value]
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class FilterCondition(BaseModel):
|
|
80
|
+
"""A single filter condition."""
|
|
81
|
+
|
|
82
|
+
field_name: FieldName
|
|
83
|
+
values: list[Any]
|
|
84
|
+
operator: LogicalOperator = (
|
|
85
|
+
LogicalOperator.OR
|
|
86
|
+
) # How to combine values within this condition
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class FilterGroup(BaseModel):
|
|
90
|
+
"""A group of filter conditions with logical operators."""
|
|
91
|
+
|
|
92
|
+
conditions: list[FilterCondition]
|
|
93
|
+
operator: LogicalOperator = (
|
|
94
|
+
LogicalOperator.OR
|
|
95
|
+
) # How to combine conditions within this group
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class MetricLayer(Layer):
|
|
99
|
+
type: str
|
|
100
|
+
# Simplified: just a single FilterGroup or FilterCondition
|
|
101
|
+
filters: Union[FilterGroup, FilterCondition] = Field(
|
|
102
|
+
default_factory=lambda: FilterGroup(conditions=[])
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
def matches(self, data_dict: dict) -> bool:
|
|
106
|
+
"""Check if the given data dictionary matches the filter criteria."""
|
|
107
|
+
if isinstance(self.filters, FilterCondition):
|
|
108
|
+
return self._evaluate_condition(self.filters, data_dict)
|
|
109
|
+
else: # FilterGroup
|
|
110
|
+
return self._evaluate_filter_group(self.filters, data_dict)
|
|
111
|
+
|
|
112
|
+
def _evaluate_filter_group(self, group: FilterGroup, data_dict: dict) -> bool:
|
|
113
|
+
"""Evaluate a single filter group."""
|
|
114
|
+
if not group.conditions:
|
|
115
|
+
return True
|
|
116
|
+
|
|
117
|
+
condition_results = []
|
|
118
|
+
for condition in group.conditions:
|
|
119
|
+
condition_results.append(self._evaluate_condition(condition, data_dict))
|
|
120
|
+
|
|
121
|
+
if group.operator == LogicalOperator.AND:
|
|
122
|
+
return all(condition_results)
|
|
123
|
+
else: # OR
|
|
124
|
+
return any(condition_results)
|
|
125
|
+
|
|
126
|
+
def _evaluate_condition(self, condition: FilterCondition, data_dict: dict) -> bool:
|
|
127
|
+
"""Evaluate a single condition."""
|
|
128
|
+
# Use the dict_key property to get the actual dictionary key
|
|
129
|
+
field_value = data_dict.get(condition.field_name.dict_key)
|
|
130
|
+
if field_value is None:
|
|
131
|
+
return False
|
|
132
|
+
|
|
133
|
+
value_matches = [value in field_value for value in condition.values] # noqa: PD011
|
|
134
|
+
|
|
135
|
+
if condition.operator == LogicalOperator.AND:
|
|
136
|
+
return all(value_matches)
|
|
137
|
+
else: # OR
|
|
138
|
+
return any(value_matches)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class AggregationDmgLayer(MetricLayer):
|
|
142
|
+
type: str = "damage"
|
|
143
|
+
filters: FilterGroup = Field(
|
|
144
|
+
default_factory=lambda: FilterGroup(
|
|
145
|
+
conditions=[
|
|
146
|
+
FilterCondition(
|
|
147
|
+
field_name=FieldName.NAME,
|
|
148
|
+
values=[
|
|
149
|
+
"TotalDamageEvent",
|
|
150
|
+
"ExpectedAnnualDamages",
|
|
151
|
+
"TotalDamageRP",
|
|
152
|
+
"EWEAD",
|
|
153
|
+
],
|
|
154
|
+
)
|
|
155
|
+
]
|
|
156
|
+
)
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
|
|
59
160
|
class OutputLayers(BaseModel):
|
|
60
161
|
"""The configuration of the mapbox layers in the gui.
|
|
61
162
|
|
|
@@ -75,9 +176,84 @@ class OutputLayers(BaseModel):
|
|
|
75
176
|
floodmap: FloodMapLayer
|
|
76
177
|
aggregation_dmg: AggregationDmgLayer
|
|
77
178
|
footprints_dmg: FootprintsDmgLayer
|
|
78
|
-
|
|
179
|
+
aggregated_metrics: list[MetricLayer] = Field(default_factory=list)
|
|
79
180
|
benefits: Optional[BenefitsLayer] = None
|
|
80
181
|
|
|
182
|
+
def get_aggr_metrics_layers(
|
|
183
|
+
self,
|
|
184
|
+
metrics: list[dict],
|
|
185
|
+
type: Literal["single_event", "risk"] = "single_event",
|
|
186
|
+
rp: Optional[int] = None,
|
|
187
|
+
equity: bool = False,
|
|
188
|
+
):
|
|
189
|
+
layer_types = [self.aggregation_dmg] + self.aggregated_metrics
|
|
190
|
+
filtered_input_metrics = self._filter_metrics(metrics, type, rp, equity)
|
|
191
|
+
return self._match_metrics_to_layers(filtered_input_metrics, layer_types)
|
|
192
|
+
|
|
193
|
+
def _should_skip_metric(
|
|
194
|
+
self, metric_name: str, rp: Optional[int], equity: bool
|
|
195
|
+
) -> bool:
|
|
196
|
+
rp_match = re.search(r"RP(\d+)", metric_name)
|
|
197
|
+
y_match = re.search(r"(\d+)Y", metric_name)
|
|
198
|
+
name_match = rp_match or y_match
|
|
199
|
+
|
|
200
|
+
if rp is None:
|
|
201
|
+
if name_match:
|
|
202
|
+
return True
|
|
203
|
+
if not equity and "EW" in metric_name:
|
|
204
|
+
return True
|
|
205
|
+
if equity and "EW" not in metric_name:
|
|
206
|
+
return True
|
|
207
|
+
return False
|
|
208
|
+
|
|
209
|
+
def _process_metric_name(
|
|
210
|
+
self, metric_name: str, rp: Optional[int]
|
|
211
|
+
) -> tuple[str, bool]:
|
|
212
|
+
rp_match = re.search(r"RP(\d+)", metric_name)
|
|
213
|
+
y_match = re.search(r"(\d+)Y", metric_name)
|
|
214
|
+
name_match = rp_match or y_match
|
|
215
|
+
if rp is not None:
|
|
216
|
+
if name_match:
|
|
217
|
+
extracted_rp = int(name_match.group(1))
|
|
218
|
+
name_check = extracted_rp == int(rp)
|
|
219
|
+
cleaned_name = re.sub(r"(RP\d+|\d+Y)", "", metric_name)
|
|
220
|
+
return cleaned_name.rstrip("_"), name_check
|
|
221
|
+
else:
|
|
222
|
+
return metric_name, False
|
|
223
|
+
return metric_name, True
|
|
224
|
+
|
|
225
|
+
def _filter_metrics(self, metrics, type, rp, equity):
|
|
226
|
+
filtered = []
|
|
227
|
+
for metric in metrics:
|
|
228
|
+
metric_name = metric.get("name", "")
|
|
229
|
+
metric["name_to_show"] = metric_name
|
|
230
|
+
if type == "risk":
|
|
231
|
+
if self._should_skip_metric(metric_name, rp, equity):
|
|
232
|
+
continue
|
|
233
|
+
metric["name_to_show"], name_check = self._process_metric_name(
|
|
234
|
+
metric_name, rp
|
|
235
|
+
)
|
|
236
|
+
if rp is not None and not name_check:
|
|
237
|
+
continue
|
|
238
|
+
filtered.append(metric)
|
|
239
|
+
return filtered
|
|
240
|
+
|
|
241
|
+
def _match_metrics_to_layers(self, metrics, layer_types):
|
|
242
|
+
filtered_metrics = []
|
|
243
|
+
for metric in metrics:
|
|
244
|
+
for layer in layer_types:
|
|
245
|
+
if layer.matches(metric):
|
|
246
|
+
filtered_metrics.append(
|
|
247
|
+
{
|
|
248
|
+
"metric": metric,
|
|
249
|
+
"bins": getattr(layer, "bins", None),
|
|
250
|
+
"colors": getattr(layer, "colors", None),
|
|
251
|
+
"decimals": getattr(layer, "decimals", None),
|
|
252
|
+
}
|
|
253
|
+
)
|
|
254
|
+
break
|
|
255
|
+
return filtered_metrics
|
|
256
|
+
|
|
81
257
|
|
|
82
258
|
class VisualizationLayer(Layer):
|
|
83
259
|
"""The configuration of a layer to visualize in the gui.
|
|
@@ -157,6 +333,7 @@ class VisualizationLayers(BaseModel):
|
|
|
157
333
|
The layers to visualize.
|
|
158
334
|
"""
|
|
159
335
|
|
|
336
|
+
buildings_min_zoom_level: int = 13
|
|
160
337
|
layers: list[VisualizationLayer] = Field(default_factory=list)
|
|
161
338
|
|
|
162
339
|
def add_layer(
|
flood_adapt/config/hazard.py
CHANGED
|
@@ -341,7 +341,7 @@ class SlrScenariosModel(BaseModel):
|
|
|
341
341
|
# write html to results folder
|
|
342
342
|
output_loc.parent.mkdir(parents=True, exist_ok=True)
|
|
343
343
|
fig.write_html(output_loc)
|
|
344
|
-
return
|
|
344
|
+
return output_loc.as_posix()
|
|
345
345
|
|
|
346
346
|
|
|
347
347
|
class FloodModel(BaseModel):
|