Spaces:
Runtime error
Runtime error
Commit
·
4408461
1
Parent(s):
768d678
Added two policies
Browse files- README.md +15 -3
- pyproject.toml +2 -1
- tomorrowcities/backend/engine.py +119 -108
- tomorrowcities/content/articles/metrics.md +42 -0
- tomorrowcities/content/articles/policies.md +22 -0
- tomorrowcities/content/articles/welcome.md +11 -0
- tomorrowcities/pages/engine.py +84 -40
README.md
CHANGED
|
@@ -1,4 +1,16 @@
|
|
| 1 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
-
##
|
| 4 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: App Engine
|
| 3 |
+
emoji: 🏆
|
| 4 |
+
colorFrom: purple
|
| 5 |
+
colorTo: blue
|
| 6 |
+
sdk: docker
|
| 7 |
+
pinned: false
|
| 8 |
+
---
|
| 9 |
|
| 10 |
+
## What is New?
|
| 11 |
+
|
| 12 |
+
### Excel support:
|
| 13 |
+
You can upload Excel files containing your tabular data such as individual, household, fragility or vulnerability data. However, processing excel files is very slower than processing JSON files so I definitely suggest working with JSON files. You can convert your Excel files via panda framework. The platform also does not try to convert the coordinates even if there is any in the Excel file because there is no way to know which columns represent the coordinates or coordinate reference systems without metadata. So Excel spreadsheets should be used to provide non-geo related tabular data. So please use them only for data not containing any geo-specific information.
|
| 14 |
+
|
| 15 |
+
### Dar Es Salaam Study
|
| 16 |
+
[Sample Dataset](https://drive.google.com/file/d/1BGPZQ2IKJHY9ExOCCHcNNrCTioYZ8D1y/view?usp=sharing) now contains some visioning scenario for Dar Es Salaam case.
|
pyproject.toml
CHANGED
|
@@ -17,7 +17,8 @@ dependencies = [
|
|
| 17 |
"psycopg2-binary",
|
| 18 |
"scipy",
|
| 19 |
"pandas",
|
| 20 |
-
"networkx"
|
|
|
|
| 21 |
]
|
| 22 |
|
| 23 |
[tool.hatch.version]
|
|
|
|
| 17 |
"psycopg2-binary",
|
| 18 |
"scipy",
|
| 19 |
"pandas",
|
| 20 |
+
"networkx",
|
| 21 |
+
"openpyxl"
|
| 22 |
]
|
| 23 |
|
| 24 |
[tool.hatch.version]
|
tomorrowcities/backend/engine.py
CHANGED
|
@@ -1,13 +1,7 @@
|
|
| 1 |
-
#%%
|
| 2 |
-
import warnings
|
| 3 |
-
import json
|
| 4 |
-
import sys
|
| 5 |
-
import argparse
|
| 6 |
-
import io
|
| 7 |
import os
|
|
|
|
| 8 |
import pandas as pd
|
| 9 |
-
import
|
| 10 |
-
import geopandas
|
| 11 |
import numpy as np
|
| 12 |
from scipy.stats import norm
|
| 13 |
from scipy.interpolate import interp1d
|
|
@@ -36,7 +30,7 @@ def compute_power_infra(nodes,edges,intensity,fragility):
|
|
| 36 |
for _, edge in edges.iterrows():
|
| 37 |
G_power.add_edge(*(edge.from_node, edge.to_node))
|
| 38 |
|
| 39 |
-
nodes =
|
| 40 |
how='left', rsuffix='intensity',distance_col='distance')
|
| 41 |
|
| 42 |
nodes = nodes.merge(eq_vuln, how='left',left_on='eq_vuln',right_on='vuln_string')
|
|
@@ -55,14 +49,14 @@ def compute_power_infra(nodes,edges,intensity,fragility):
|
|
| 55 |
for i in [1,2,3,4,5]:
|
| 56 |
nodes[f'ds_{i}'] = np.abs(nodes[f'prob_ds{i-1}'] - nodes[f'prob_ds{i}'])
|
| 57 |
df_ds = nodes[['ds_1','ds_2','ds_3','ds_4','ds_5']]
|
| 58 |
-
nodes['eq_ds'] = df_ds.idxmax(axis='columns').str.extract(r'ds_([0-9]+)').astype('int')
|
| 59 |
|
| 60 |
# Damage State Codes
|
| 61 |
-
DS_NO =
|
| 62 |
-
DS_SLIGHT =
|
| 63 |
-
DS_MODERATE =
|
| 64 |
-
DS_EXTENSIVE =
|
| 65 |
-
DS_COMPLETE =
|
| 66 |
|
| 67 |
# If a damage state is above this threshold (excluding),
|
| 68 |
# we consider the associated node as dead.
|
|
@@ -131,8 +125,7 @@ def compute_power_infra(nodes,edges,intensity,fragility):
|
|
| 131 |
|
| 132 |
return nodes['eq_ds'], nodes['is_damaged'], nodes['is_operational']
|
| 133 |
|
| 134 |
-
|
| 135 |
-
def compute(gdf_buildings, df_household, df_individual,gdf_intensity, df_hazard, hazard_type):
|
| 136 |
|
| 137 |
column_names = {'zoneID':'zoneid','bldID':'bldid','nHouse':'nhouse',
|
| 138 |
'specialFac':'specialfac','expStr':'expstr','repValue':'repvalue',
|
|
@@ -140,30 +133,28 @@ def compute(gdf_buildings, df_household, df_individual,gdf_intensity, df_hazard,
|
|
| 140 |
'CommFacID':'commfacid','indivId':'individ','eduAttStat':'eduattstat',
|
| 141 |
'indivFacID':'indivfacid','VALUE':'im'}
|
| 142 |
|
| 143 |
-
|
|
|
|
| 144 |
gdf_buildings = gdf_buildings.rename(columns=column_names)
|
| 145 |
df_household = df_household.rename(columns=column_names)
|
| 146 |
df_individual = df_individual.rename(columns=column_names)
|
| 147 |
gdf_intensity = gdf_intensity.rename(columns=column_names)
|
| 148 |
|
| 149 |
# Damage States
|
| 150 |
-
DS_NO =
|
| 151 |
-
DS_SLIGHT =
|
| 152 |
-
DS_MODERATE =
|
| 153 |
-
DS_EXTENSIZE =
|
| 154 |
-
DS_COLLAPSED =
|
| 155 |
|
| 156 |
# Hazard Types
|
| 157 |
HAZARD_EARTHQUAKE = "earthquake"
|
| 158 |
HAZARD_FLOOD = "flood"
|
| 159 |
HAZARD_DEBRIS = "debris"
|
| 160 |
|
| 161 |
-
policies = []
|
| 162 |
-
threshold = 1
|
| 163 |
threshold_flood = 0.2
|
| 164 |
threshold_flood_distance = 10
|
| 165 |
epsg = 3857
|
| 166 |
-
#epsg = 21037 # Arc 1960 / UTM zone 37S
|
| 167 |
|
| 168 |
# Replace strange TypeX LRS with RCi
|
| 169 |
print('deneme',df_hazard.columns)
|
|
@@ -172,42 +163,40 @@ def compute(gdf_buildings, df_household, df_individual,gdf_intensity, df_hazard,
|
|
| 172 |
number_of_unique_buildings = len(pd.unique(gdf_buildings['bldid']))
|
| 173 |
print('number of unique building', number_of_unique_buildings)
|
| 174 |
print('number of records in building layer ', len(gdf_buildings['bldid']))
|
| 175 |
-
|
| 176 |
# Convert both to the same target coordinate system
|
| 177 |
gdf_buildings = gdf_buildings.set_crs("EPSG:4326",allow_override=True)
|
| 178 |
gdf_intensity = gdf_intensity.set_crs("EPSG:4326",allow_override=True)
|
| 179 |
-
|
| 180 |
gdf_buildings = gdf_buildings.to_crs(f"EPSG:{epsg}")
|
| 181 |
gdf_intensity = gdf_intensity.to_crs(f"EPSG:{epsg}")
|
| 182 |
|
| 183 |
-
|
| 184 |
-
gdf_building_intensity = geopandas.sjoin_nearest(gdf_buildings,gdf_intensity,
|
| 185 |
how='left', rsuffix='intensity',distance_col='distance')
|
| 186 |
-
#%%
|
| 187 |
gdf_building_intensity = gdf_building_intensity.drop_duplicates(subset=['bldid'], keep='first')
|
| 188 |
-
|
|
|
|
|
|
|
| 189 |
# TODO: Check if the logic makes sense
|
| 190 |
if hazard_type == HAZARD_FLOOD:
|
| 191 |
away_from_flood = gdf_building_intensity['distance'] > threshold_flood_distance
|
| 192 |
print('threshold_flood_distance',threshold_flood_distance)
|
| 193 |
print('number of distant buildings', len(gdf_building_intensity.loc[away_from_flood, 'im']))
|
| 194 |
gdf_building_intensity.loc[away_from_flood, 'im'] = 0
|
| 195 |
-
# %%
|
| 196 |
gdf_building_intensity[['material','code_level','storeys','occupancy']] = \
|
| 197 |
gdf_building_intensity['expstr'].str.split('+',expand=True)
|
| 198 |
gdf_building_intensity['height'] = gdf_building_intensity['storeys'].str.extract(r'([0-9]+)s').astype('int')
|
| 199 |
-
# %%
|
| 200 |
lr = (gdf_building_intensity['height'] <= 4)
|
| 201 |
mr = (gdf_building_intensity['height'] >= 5) & (gdf_building_intensity['height'] <= 8)
|
| 202 |
hr = (gdf_building_intensity['height'] >= 9)
|
| 203 |
gdf_building_intensity.loc[lr, 'height_level'] = 'LR'
|
| 204 |
gdf_building_intensity.loc[mr, 'height_level'] = 'MR'
|
| 205 |
gdf_building_intensity.loc[hr, 'height_level'] = 'HR'
|
| 206 |
-
#
|
| 207 |
gdf_building_intensity['vulnstreq'] = \
|
| 208 |
gdf_building_intensity[['material','code_level','height_level']] \
|
| 209 |
.agg('+'.join,axis=1)
|
| 210 |
-
#
|
| 211 |
if hazard_type == HAZARD_EARTHQUAKE:
|
| 212 |
bld_eq = gdf_building_intensity.merge(df_hazard, left_on='vulnstreq',right_on='expstr', how='left')
|
| 213 |
nulls = bld_eq['muds1_g'].isna()
|
|
@@ -223,13 +212,18 @@ def compute(gdf_buildings, df_household, df_individual,gdf_intensity, df_hazard,
|
|
| 223 |
for i in [1,2,3,4,5]:
|
| 224 |
bld_eq[f'ds_{i}'] = np.abs(bld_eq[f'prob_ds{i-1}'] - bld_eq[f'prob_ds{i}'])
|
| 225 |
df_ds = bld_eq[['ds_1','ds_2','ds_3','ds_4','ds_5']]
|
| 226 |
-
bld_eq['eq_ds'] = df_ds.idxmax(axis='columns').str.extract(r'ds_([0-9]+)').astype('int')
|
| 227 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 228 |
# Create a simplified building-hazard relation
|
| 229 |
-
bld_hazard = bld_eq[['bldid','
|
| 230 |
bld_hazard = bld_hazard.rename(columns={'eq_ds':'ds'})
|
| 231 |
|
| 232 |
-
ds_str = {
|
| 233 |
|
| 234 |
elif hazard_type == HAZARD_FLOOD:
|
| 235 |
bld_flood = gdf_building_intensity.merge(df_hazard, on='expstr', how='left')
|
|
@@ -241,128 +235,146 @@ def compute(gdf_buildings, df_household, df_individual,gdf_intensity, df_hazard,
|
|
| 241 |
bld_flood['fl_prob'] = np.diag(flood_mapping(xnew))
|
| 242 |
bld_flood['fl_ds'] = 0
|
| 243 |
bld_flood.loc[bld_flood['fl_prob'] > threshold_flood,'fl_ds'] = 1
|
| 244 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 245 |
# Create a simplified building-hazard relation
|
| 246 |
-
bld_hazard = bld_flood[['bldid','
|
| 247 |
bld_hazard = bld_hazard.rename(columns={'fl_ds':'ds'})
|
| 248 |
|
| 249 |
ds_str = {0: 'No Damage',1:'Flooded'}
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 255 |
|
| 256 |
-
|
| 257 |
df_household_bld = df_household.merge(bld_hazard[['bldid','ds']], on='bldid', how='left',validate='many_to_one')
|
| 258 |
|
| 259 |
-
|
| 260 |
-
df_hospitals = df_household.merge(bld_hazard[['bldid',
|
| 261 |
how='left', left_on='commfacid', right_on='bldid', suffixes=['','_comm'],
|
| 262 |
validate='many_to_one')
|
| 263 |
|
| 264 |
-
|
| 265 |
-
df_individual_occupancy = df_individual.merge(bld_hazard[['bldid','occupancy','
|
| 266 |
how='inner',left_on='indivfacid',right_on='bldid',
|
| 267 |
suffixes=['_l','_r'],validate='many_to_one')
|
| 268 |
|
| 269 |
-
|
| 270 |
df_workers = df_individual_occupancy.query('occupancy in ["Com","ResCom","Ind"]')
|
| 271 |
|
| 272 |
-
|
| 273 |
df_students = df_individual_occupancy.query('occupancy in ["Edu"]')
|
| 274 |
|
| 275 |
-
|
| 276 |
-
df_indiv_hosp = df_individual.merge(df_hospitals[['hhid','ds'
|
| 277 |
-
|
| 278 |
-
#%%
|
| 279 |
|
| 280 |
# get the ds of household that individual lives in
|
| 281 |
df_indiv_household = df_individual[['hhid','individ']].merge(df_household_bld[['hhid','ds']])
|
| 282 |
|
|
|
|
| 283 |
df_displaced_indiv = df_indiv_hosp.rename(columns={'ds':'ds_hospital'})\
|
| 284 |
.merge(df_workers[['individ','ds']].rename(columns={'ds':'ds_workplace'}),on='individ', how='left')\
|
| 285 |
.merge(df_students[['individ','ds']].rename(columns={'ds':'ds_school'}), on='individ', how='left')\
|
| 286 |
-
.merge(df_indiv_household[['individ','ds']].rename(columns={'ds':'ds_household'}), on='individ',how='left')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 287 |
|
| 288 |
-
# %%
|
| 289 |
-
#%%
|
| 290 |
if hazard_type == HAZARD_EARTHQUAKE:
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
thresholds = {f'metric{id}':
|
| 294 |
-
|
| 295 |
-
#
|
| 296 |
-
#
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
thresholds['metric3'] = DS_SLIGHT
|
| 305 |
-
if 23 in policies and thresholds['metric2'] == DS_NO:
|
| 306 |
-
thresholds['metric2'] = DS_SLIGHT
|
| 307 |
-
|
| 308 |
-
# Policy-2: Knowledge sharing about DRR in public and private schools
|
| 309 |
-
# Changes: Damage state thresholds for “loss of school access”
|
| 310 |
-
# Increase thresholds loss of school access to beyond current scale. So that the impact will be downgraded to “0”.
|
| 311 |
-
if 22 in policies:
|
| 312 |
-
thresholds['metric2'] = DS_COLLAPSED
|
| 313 |
-
elif hazard_type == HAZARD_FLOOD:
|
| 314 |
-
# For flood, there are only two states: 0 or 1.
|
| 315 |
-
# So threshold is set to 0.
|
| 316 |
-
thresholds = {f'metric{id}': 0 for id in range(8)}
|
| 317 |
|
| 318 |
-
#%% metric 1 number of unemployed workers in each building
|
| 319 |
-
df_workers_per_building = df_workers[df_workers['ds'] > thresholds['metric1']].groupby('bldid',as_index=False).agg({'individ':'count'})
|
| 320 |
-
|
| 321 |
df_metric1 = bld_hazard.merge(df_workers_per_building,how='left',left_on='bldid',right_on = 'bldid')[['bldid','individ']]
|
| 322 |
df_metric1.rename(columns={'individ':'metric1'}, inplace=True)
|
| 323 |
df_metric1['metric1'] = df_metric1['metric1'].fillna(0).astype(int)
|
| 324 |
|
| 325 |
-
|
| 326 |
-
df_students_per_building = df_students[df_students['ds'] > thresholds['metric2']]
|
|
|
|
|
|
|
|
|
|
| 327 |
df_metric2 = bld_hazard.merge(df_students_per_building,how='left',left_on='bldid',right_on = 'bldid')[['bldid','individ']]
|
| 328 |
df_metric2.rename(columns={'individ':'metric2'}, inplace=True)
|
| 329 |
df_metric2['metric2'] = df_metric2['metric2'].fillna(0).astype(int)
|
| 330 |
|
| 331 |
-
|
| 332 |
-
df_hospitals_per_household = df_hospitals[df_hospitals['ds'] > thresholds['metric3']].groupby(
|
|
|
|
|
|
|
| 333 |
df_metric3 = bld_hazard.merge(df_hospitals_per_household,how='left',left_on='bldid',right_on='bldid')[['bldid','hhid']]
|
| 334 |
df_metric3.rename(columns={'hhid':'metric3'}, inplace=True)
|
| 335 |
df_metric3['metric3'] = df_metric3['metric3'].fillna(0).astype(int)
|
| 336 |
|
| 337 |
-
|
| 338 |
-
df_hospitals_per_individual = df_hospitals[df_hospitals['ds'] > thresholds['metric4']].groupby(
|
|
|
|
|
|
|
| 339 |
df_metric4 = bld_hazard.merge(df_hospitals_per_individual,how='left',left_on='bldid',right_on='bldid')[['bldid','nind']]
|
| 340 |
df_metric4.rename(columns={'nind':'metric4'}, inplace=True)
|
| 341 |
df_metric4['metric4'] = df_metric4['metric4'].fillna(0).astype(int)
|
| 342 |
|
| 343 |
-
|
| 344 |
-
df_homeless_households = df_household_bld[df_household_bld['ds'] > thresholds['metric5']].groupby(
|
|
|
|
|
|
|
| 345 |
df_metric5 = bld_hazard.merge(df_homeless_households,how='left',left_on='bldid',right_on='bldid')[['bldid','hhid']]
|
| 346 |
df_metric5.rename(columns={'hhid':'metric5'}, inplace=True)
|
| 347 |
df_metric5['metric5'] = df_metric5['metric5'].fillna(0).astype(int)
|
| 348 |
|
| 349 |
-
|
| 350 |
-
df_homeless_individuals = df_household_bld[df_household_bld['ds'] > thresholds['metric6']].groupby(
|
|
|
|
|
|
|
| 351 |
df_metric6 = bld_hazard.merge(df_homeless_individuals,how='left',left_on='bldid',right_on='bldid')[['bldid','nind']]
|
| 352 |
df_metric6.rename(columns={'nind':'metric6'}, inplace=True)
|
| 353 |
df_metric6['metric6'] = df_metric6['metric6'].fillna(0).astype(int)
|
| 354 |
|
| 355 |
-
|
| 356 |
# more info: an individual is displaced if at least of the conditions below hold
|
| 357 |
df_disp_per_bld = df_displaced_indiv[(df_displaced_indiv['ds_household'] > thresholds['metric6']) |
|
| 358 |
-
(df_displaced_indiv['ds_school'] > thresholds['
|
| 359 |
-
(df_displaced_indiv['ds_workplace'] > thresholds['
|
| 360 |
-
(df_displaced_indiv['ds_hospital'] > thresholds['
|
|
|
|
|
|
|
|
|
|
| 361 |
df_metric7 = bld_hazard.merge(df_disp_per_bld,how='left',left_on='bldid',right_on='bldid')[['bldid','individ']]
|
| 362 |
df_metric7.rename(columns={'individ':'metric7'}, inplace=True)
|
| 363 |
df_metric7['metric7'] = df_metric7['metric7'].fillna(0).astype(int)
|
| 364 |
|
| 365 |
-
|
|
|
|
| 366 |
df_metrics = {'metric1': df_metric1,
|
| 367 |
'metric2': df_metric2,
|
| 368 |
'metric3': df_metric3,
|
|
@@ -371,7 +383,7 @@ def compute(gdf_buildings, df_household, df_individual,gdf_intensity, df_hazard,
|
|
| 371 |
'metric6': df_metric6,
|
| 372 |
'metric7': df_metric7}
|
| 373 |
|
| 374 |
-
|
| 375 |
number_of_workers = len(df_workers)
|
| 376 |
print('number of workers', number_of_workers)
|
| 377 |
|
|
@@ -387,7 +399,7 @@ def compute(gdf_buildings, df_household, df_individual,gdf_intensity, df_hazard,
|
|
| 387 |
"metric2": {"desc": "Number of children with no access to education", "value": 0, "max_value": number_of_students},
|
| 388 |
"metric3": {"desc": "Number of households with no access to hospital", "value": 0, "max_value": number_of_households},
|
| 389 |
"metric4": {"desc": "Number of individuals with no access to hospital", "value": 0, "max_value": number_of_individuals},
|
| 390 |
-
"metric5": {"desc": "Number of
|
| 391 |
"metric6": {"desc": "Number of homeless individuals", "value": 0, "max_value": number_of_individuals},
|
| 392 |
"metric7": {"desc": "Population displacement", "value": 0, "max_value": number_of_individuals},}
|
| 393 |
metrics["metric1"]["value"] = int(df_metric1['metric1'].sum())
|
|
@@ -398,5 +410,4 @@ def compute(gdf_buildings, df_household, df_individual,gdf_intensity, df_hazard,
|
|
| 398 |
metrics["metric6"]["value"] = int(df_metric6['metric6'].sum())
|
| 399 |
metrics["metric7"]["value"] = int(df_metric7['metric7'].sum())
|
| 400 |
|
| 401 |
-
return metrics, df_metrics
|
| 402 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import os
|
| 2 |
+
os.environ['USE_PYGEOS'] = '0'
|
| 3 |
import pandas as pd
|
| 4 |
+
import geopandas as gpd
|
|
|
|
| 5 |
import numpy as np
|
| 6 |
from scipy.stats import norm
|
| 7 |
from scipy.interpolate import interp1d
|
|
|
|
| 30 |
for _, edge in edges.iterrows():
|
| 31 |
G_power.add_edge(*(edge.from_node, edge.to_node))
|
| 32 |
|
| 33 |
+
nodes = gpd.sjoin_nearest(nodes,intensity,
|
| 34 |
how='left', rsuffix='intensity',distance_col='distance')
|
| 35 |
|
| 36 |
nodes = nodes.merge(eq_vuln, how='left',left_on='eq_vuln',right_on='vuln_string')
|
|
|
|
| 49 |
for i in [1,2,3,4,5]:
|
| 50 |
nodes[f'ds_{i}'] = np.abs(nodes[f'prob_ds{i-1}'] - nodes[f'prob_ds{i}'])
|
| 51 |
df_ds = nodes[['ds_1','ds_2','ds_3','ds_4','ds_5']]
|
| 52 |
+
nodes['eq_ds'] = df_ds.idxmax(axis='columns').str.extract(r'ds_([0-9]+)').astype('int') - 1
|
| 53 |
|
| 54 |
# Damage State Codes
|
| 55 |
+
DS_NO = 0
|
| 56 |
+
DS_SLIGHT = 1
|
| 57 |
+
DS_MODERATE = 2
|
| 58 |
+
DS_EXTENSIVE = 3
|
| 59 |
+
DS_COMPLETE = 4
|
| 60 |
|
| 61 |
# If a damage state is above this threshold (excluding),
|
| 62 |
# we consider the associated node as dead.
|
|
|
|
| 125 |
|
| 126 |
return nodes['eq_ds'], nodes['is_damaged'], nodes['is_operational']
|
| 127 |
|
| 128 |
+
def compute(gdf_landuse, gdf_buildings, df_household, df_individual,gdf_intensity, df_hazard, hazard_type, policies=[]):
|
|
|
|
| 129 |
|
| 130 |
column_names = {'zoneID':'zoneid','bldID':'bldid','nHouse':'nhouse',
|
| 131 |
'specialFac':'specialfac','expStr':'expstr','repValue':'repvalue',
|
|
|
|
| 133 |
'CommFacID':'commfacid','indivId':'individ','eduAttStat':'eduattstat',
|
| 134 |
'indivFacID':'indivfacid','VALUE':'im'}
|
| 135 |
|
| 136 |
+
|
| 137 |
+
gdf_landuse = gdf_landuse.rename(columns=column_names)
|
| 138 |
gdf_buildings = gdf_buildings.rename(columns=column_names)
|
| 139 |
df_household = df_household.rename(columns=column_names)
|
| 140 |
df_individual = df_individual.rename(columns=column_names)
|
| 141 |
gdf_intensity = gdf_intensity.rename(columns=column_names)
|
| 142 |
|
| 143 |
# Damage States
|
| 144 |
+
DS_NO = 0
|
| 145 |
+
DS_SLIGHT = 1
|
| 146 |
+
DS_MODERATE = 2
|
| 147 |
+
DS_EXTENSIZE = 3
|
| 148 |
+
DS_COLLAPSED = 4
|
| 149 |
|
| 150 |
# Hazard Types
|
| 151 |
HAZARD_EARTHQUAKE = "earthquake"
|
| 152 |
HAZARD_FLOOD = "flood"
|
| 153 |
HAZARD_DEBRIS = "debris"
|
| 154 |
|
|
|
|
|
|
|
| 155 |
threshold_flood = 0.2
|
| 156 |
threshold_flood_distance = 10
|
| 157 |
epsg = 3857
|
|
|
|
| 158 |
|
| 159 |
# Replace strange TypeX LRS with RCi
|
| 160 |
print('deneme',df_hazard.columns)
|
|
|
|
| 163 |
number_of_unique_buildings = len(pd.unique(gdf_buildings['bldid']))
|
| 164 |
print('number of unique building', number_of_unique_buildings)
|
| 165 |
print('number of records in building layer ', len(gdf_buildings['bldid']))
|
| 166 |
+
|
| 167 |
# Convert both to the same target coordinate system
|
| 168 |
gdf_buildings = gdf_buildings.set_crs("EPSG:4326",allow_override=True)
|
| 169 |
gdf_intensity = gdf_intensity.set_crs("EPSG:4326",allow_override=True)
|
| 170 |
+
|
| 171 |
gdf_buildings = gdf_buildings.to_crs(f"EPSG:{epsg}")
|
| 172 |
gdf_intensity = gdf_intensity.to_crs(f"EPSG:{epsg}")
|
| 173 |
|
| 174 |
+
gdf_building_intensity = gpd.sjoin_nearest(gdf_buildings,gdf_intensity,
|
|
|
|
| 175 |
how='left', rsuffix='intensity',distance_col='distance')
|
|
|
|
| 176 |
gdf_building_intensity = gdf_building_intensity.drop_duplicates(subset=['bldid'], keep='first')
|
| 177 |
+
|
| 178 |
+
gdf_building_intensity = gdf_building_intensity.merge(gdf_landuse[['zoneid','avgincome']],on='zoneid',how='left')
|
| 179 |
+
|
| 180 |
# TODO: Check if the logic makes sense
|
| 181 |
if hazard_type == HAZARD_FLOOD:
|
| 182 |
away_from_flood = gdf_building_intensity['distance'] > threshold_flood_distance
|
| 183 |
print('threshold_flood_distance',threshold_flood_distance)
|
| 184 |
print('number of distant buildings', len(gdf_building_intensity.loc[away_from_flood, 'im']))
|
| 185 |
gdf_building_intensity.loc[away_from_flood, 'im'] = 0
|
|
|
|
| 186 |
gdf_building_intensity[['material','code_level','storeys','occupancy']] = \
|
| 187 |
gdf_building_intensity['expstr'].str.split('+',expand=True)
|
| 188 |
gdf_building_intensity['height'] = gdf_building_intensity['storeys'].str.extract(r'([0-9]+)s').astype('int')
|
|
|
|
| 189 |
lr = (gdf_building_intensity['height'] <= 4)
|
| 190 |
mr = (gdf_building_intensity['height'] >= 5) & (gdf_building_intensity['height'] <= 8)
|
| 191 |
hr = (gdf_building_intensity['height'] >= 9)
|
| 192 |
gdf_building_intensity.loc[lr, 'height_level'] = 'LR'
|
| 193 |
gdf_building_intensity.loc[mr, 'height_level'] = 'MR'
|
| 194 |
gdf_building_intensity.loc[hr, 'height_level'] = 'HR'
|
| 195 |
+
# Earthquake uses simplified taxonomy
|
| 196 |
gdf_building_intensity['vulnstreq'] = \
|
| 197 |
gdf_building_intensity[['material','code_level','height_level']] \
|
| 198 |
.agg('+'.join,axis=1)
|
| 199 |
+
#
|
| 200 |
if hazard_type == HAZARD_EARTHQUAKE:
|
| 201 |
bld_eq = gdf_building_intensity.merge(df_hazard, left_on='vulnstreq',right_on='expstr', how='left')
|
| 202 |
nulls = bld_eq['muds1_g'].isna()
|
|
|
|
| 212 |
for i in [1,2,3,4,5]:
|
| 213 |
bld_eq[f'ds_{i}'] = np.abs(bld_eq[f'prob_ds{i-1}'] - bld_eq[f'prob_ds{i}'])
|
| 214 |
df_ds = bld_eq[['ds_1','ds_2','ds_3','ds_4','ds_5']]
|
| 215 |
+
bld_eq['eq_ds'] = df_ds.idxmax(axis='columns').str.extract(r'ds_([0-9]+)').astype('int') - 1
|
| 216 |
|
| 217 |
+
if 1 in policies:
|
| 218 |
+
bld_eq.loc[bld_eq['occupancy'] == 'Res', 'eq_ds'] = 0
|
| 219 |
+
if 2 in policies:
|
| 220 |
+
bld_eq.loc[bld_eq['avgincome'] == 'lowIncomeA', 'eq_ds'] = 0
|
| 221 |
+
bld_eq.loc[bld_eq['avgincome'] == 'lowIncomeB', 'eq_ds'] = 0
|
| 222 |
# Create a simplified building-hazard relation
|
| 223 |
+
bld_hazard = bld_eq[['bldid','eq_ds']]
|
| 224 |
bld_hazard = bld_hazard.rename(columns={'eq_ds':'ds'})
|
| 225 |
|
| 226 |
+
ds_str = {0: 'No Damage',1:'Low',2:'Medium',3:'High',4:'Collapsed'}
|
| 227 |
|
| 228 |
elif hazard_type == HAZARD_FLOOD:
|
| 229 |
bld_flood = gdf_building_intensity.merge(df_hazard, on='expstr', how='left')
|
|
|
|
| 235 |
bld_flood['fl_prob'] = np.diag(flood_mapping(xnew))
|
| 236 |
bld_flood['fl_ds'] = 0
|
| 237 |
bld_flood.loc[bld_flood['fl_prob'] > threshold_flood,'fl_ds'] = 1
|
| 238 |
+
if 1 in policies:
|
| 239 |
+
bld_flood.loc[bld_flood['occupancy'] == 'Res', 'fl_ds'] = 0
|
| 240 |
+
if 2 in policies:
|
| 241 |
+
bld_flood.loc[bld_flood['avgincome'] == 'lowIncomeA', 'fl_ds'] = 0
|
| 242 |
+
bld_flood.loc[bld_flood['avgincome'] == 'lowIncomeB', 'fl_ds'] = 0
|
| 243 |
# Create a simplified building-hazard relation
|
| 244 |
+
bld_hazard = bld_flood[['bldid','fl_ds']]
|
| 245 |
bld_hazard = bld_hazard.rename(columns={'fl_ds':'ds'})
|
| 246 |
|
| 247 |
ds_str = {0: 'No Damage',1:'Flooded'}
|
| 248 |
+
|
| 249 |
+
return bld_hazard
|
| 250 |
+
|
| 251 |
+
|
| 252 |
+
def calculate_metrics(gdf_buildings, df_household, df_individual, hazard_type, policies=[]):
|
| 253 |
+
# only use necessary columns
|
| 254 |
+
bld_hazard = gdf_buildings[['bldid','ds','expstr']]
|
| 255 |
+
bld_hazard[['material','code_level','storeys','occupancy']] = \
|
| 256 |
+
bld_hazard['expstr'].str.split('+',expand=True).copy()
|
| 257 |
+
#bld_hazard['occupancy'] = bld_hazard['occupancy'].astype('category')
|
| 258 |
|
| 259 |
+
# Find the damage state of the building that the household is in
|
| 260 |
df_household_bld = df_household.merge(bld_hazard[['bldid','ds']], on='bldid', how='left',validate='many_to_one')
|
| 261 |
|
| 262 |
+
# Find the damage state of the hospital that the household is associated with
|
| 263 |
+
df_hospitals = df_household.merge(bld_hazard[['bldid', 'ds']],
|
| 264 |
how='left', left_on='commfacid', right_on='bldid', suffixes=['','_comm'],
|
| 265 |
validate='many_to_one')
|
| 266 |
|
| 267 |
+
# Find the occupancy of facility that the individual is associated
|
| 268 |
+
df_individual_occupancy = df_individual.merge(bld_hazard[['bldid','occupancy','ds']],
|
| 269 |
how='inner',left_on='indivfacid',right_on='bldid',
|
| 270 |
suffixes=['_l','_r'],validate='many_to_one')
|
| 271 |
|
| 272 |
+
# Filtering working places
|
| 273 |
df_workers = df_individual_occupancy.query('occupancy in ["Com","ResCom","Ind"]')
|
| 274 |
|
| 275 |
+
# Filtering schools
|
| 276 |
df_students = df_individual_occupancy.query('occupancy in ["Edu"]')
|
| 277 |
|
| 278 |
+
# connect individuals to damage state of associated hospitals
|
| 279 |
+
df_indiv_hosp = df_individual.merge(df_hospitals[['hhid','ds']],
|
| 280 |
+
how='left', on='hhid', validate='many_to_one')
|
|
|
|
| 281 |
|
| 282 |
# get the ds of household that individual lives in
|
| 283 |
df_indiv_household = df_individual[['hhid','individ']].merge(df_household_bld[['hhid','ds']])
|
| 284 |
|
| 285 |
+
# Collect all damage states in a single table
|
| 286 |
df_displaced_indiv = df_indiv_hosp.rename(columns={'ds':'ds_hospital'})\
|
| 287 |
.merge(df_workers[['individ','ds']].rename(columns={'ds':'ds_workplace'}),on='individ', how='left')\
|
| 288 |
.merge(df_students[['individ','ds']].rename(columns={'ds':'ds_school'}), on='individ', how='left')\
|
| 289 |
+
.merge(df_indiv_household[['individ','ds']].rename(columns={'ds':'ds_household'}), on='individ',how='left')\
|
| 290 |
+
.merge(df_household[['hhid','bldid']],on='hhid',how='left')
|
| 291 |
+
|
| 292 |
+
DS_NO = 0
|
| 293 |
+
DS_SLIGHT = 1
|
| 294 |
+
DS_MODERATE = 2
|
| 295 |
+
DS_EXTENSIZE = 3
|
| 296 |
+
DS_COLLAPSED = 4
|
| 297 |
+
|
| 298 |
+
# Hazard Types
|
| 299 |
+
HAZARD_EARTHQUAKE = "earthquake"
|
| 300 |
+
HAZARD_FLOOD = "flood"
|
| 301 |
+
HAZARD_DEBRIS = "debris"
|
| 302 |
|
|
|
|
|
|
|
| 303 |
if hazard_type == HAZARD_EARTHQUAKE:
|
| 304 |
+
# Effect of policies on thresholds
|
| 305 |
+
# First get the global threshold
|
| 306 |
+
thresholds = {f'metric{id}': DS_SLIGHT for id in range(8)}
|
| 307 |
+
else:
|
| 308 |
+
# Default thresholds for flood and debris
|
| 309 |
+
# For flood, there are only two states: 0 or 1.
|
| 310 |
+
# So threshold is set to 0.
|
| 311 |
+
thresholds = {f'metric{id}': DS_NO for id in range(8)}
|
| 312 |
+
|
| 313 |
+
# metric 1 number of unemployed workers in each building
|
| 314 |
+
df_workers_per_building = df_workers[df_workers['ds'] > thresholds['metric1']][['individ','hhid','ds']].merge(
|
| 315 |
+
df_household[['hhid','bldid']],on='hhid',how='left').groupby(
|
| 316 |
+
'bldid',as_index=False).agg({'individ':'count'})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 317 |
|
|
|
|
|
|
|
|
|
|
| 318 |
df_metric1 = bld_hazard.merge(df_workers_per_building,how='left',left_on='bldid',right_on = 'bldid')[['bldid','individ']]
|
| 319 |
df_metric1.rename(columns={'individ':'metric1'}, inplace=True)
|
| 320 |
df_metric1['metric1'] = df_metric1['metric1'].fillna(0).astype(int)
|
| 321 |
|
| 322 |
+
# metric 2 number of students in each building with no access to schools
|
| 323 |
+
df_students_per_building = df_students[df_students['ds'] > thresholds['metric2']][['individ','hhid','ds']].merge(
|
| 324 |
+
df_household[['hhid','bldid']],on='hhid',how='left').groupby(
|
| 325 |
+
'bldid',as_index=False).agg({'individ':'count'})
|
| 326 |
+
|
| 327 |
df_metric2 = bld_hazard.merge(df_students_per_building,how='left',left_on='bldid',right_on = 'bldid')[['bldid','individ']]
|
| 328 |
df_metric2.rename(columns={'individ':'metric2'}, inplace=True)
|
| 329 |
df_metric2['metric2'] = df_metric2['metric2'].fillna(0).astype(int)
|
| 330 |
|
| 331 |
+
# metric 3 number of households in each building with no access to hospitals
|
| 332 |
+
df_hospitals_per_household = df_hospitals[df_hospitals['ds'] > thresholds['metric3']].groupby(
|
| 333 |
+
'bldid',as_index=False).agg({'hhid':'count'})
|
| 334 |
+
|
| 335 |
df_metric3 = bld_hazard.merge(df_hospitals_per_household,how='left',left_on='bldid',right_on='bldid')[['bldid','hhid']]
|
| 336 |
df_metric3.rename(columns={'hhid':'metric3'}, inplace=True)
|
| 337 |
df_metric3['metric3'] = df_metric3['metric3'].fillna(0).astype(int)
|
| 338 |
|
| 339 |
+
# metric 4 number of individuals in each building with no access to hospitals
|
| 340 |
+
df_hospitals_per_individual = df_hospitals[df_hospitals['ds'] > thresholds['metric4']].groupby(
|
| 341 |
+
'bldid',as_index=False).agg({'nind':'sum'})
|
| 342 |
+
|
| 343 |
df_metric4 = bld_hazard.merge(df_hospitals_per_individual,how='left',left_on='bldid',right_on='bldid')[['bldid','nind']]
|
| 344 |
df_metric4.rename(columns={'nind':'metric4'}, inplace=True)
|
| 345 |
df_metric4['metric4'] = df_metric4['metric4'].fillna(0).astype(int)
|
| 346 |
|
| 347 |
+
# metric 5 number of damaged households in each building
|
| 348 |
+
df_homeless_households = df_household_bld[df_household_bld['ds'] > thresholds['metric5']].groupby(
|
| 349 |
+
'bldid',as_index=False).agg({'hhid':'count'})
|
| 350 |
+
|
| 351 |
df_metric5 = bld_hazard.merge(df_homeless_households,how='left',left_on='bldid',right_on='bldid')[['bldid','hhid']]
|
| 352 |
df_metric5.rename(columns={'hhid':'metric5'}, inplace=True)
|
| 353 |
df_metric5['metric5'] = df_metric5['metric5'].fillna(0).astype(int)
|
| 354 |
|
| 355 |
+
# metric 6 number of homeless individuals in each building
|
| 356 |
+
df_homeless_individuals = df_household_bld[df_household_bld['ds'] > thresholds['metric6']].groupby(
|
| 357 |
+
'bldid',as_index=False).agg({'nind':'sum'})
|
| 358 |
+
|
| 359 |
df_metric6 = bld_hazard.merge(df_homeless_individuals,how='left',left_on='bldid',right_on='bldid')[['bldid','nind']]
|
| 360 |
df_metric6.rename(columns={'nind':'metric6'}, inplace=True)
|
| 361 |
df_metric6['metric6'] = df_metric6['metric6'].fillna(0).astype(int)
|
| 362 |
|
| 363 |
+
# metric 7 the number of displaced individuals in each building
|
| 364 |
# more info: an individual is displaced if at least of the conditions below hold
|
| 365 |
df_disp_per_bld = df_displaced_indiv[(df_displaced_indiv['ds_household'] > thresholds['metric6']) |
|
| 366 |
+
(df_displaced_indiv['ds_school'] > thresholds['metric2']) |
|
| 367 |
+
(df_displaced_indiv['ds_workplace'] > thresholds['metric1']) |
|
| 368 |
+
(df_displaced_indiv['ds_hospital'] > thresholds['metric4'])]\
|
| 369 |
+
.groupby('bldid',as_index=False)\
|
| 370 |
+
.agg({'individ':'count'})
|
| 371 |
+
|
| 372 |
df_metric7 = bld_hazard.merge(df_disp_per_bld,how='left',left_on='bldid',right_on='bldid')[['bldid','individ']]
|
| 373 |
df_metric7.rename(columns={'individ':'metric7'}, inplace=True)
|
| 374 |
df_metric7['metric7'] = df_metric7['metric7'].fillna(0).astype(int)
|
| 375 |
|
| 376 |
+
|
| 377 |
+
|
| 378 |
df_metrics = {'metric1': df_metric1,
|
| 379 |
'metric2': df_metric2,
|
| 380 |
'metric3': df_metric3,
|
|
|
|
| 383 |
'metric6': df_metric6,
|
| 384 |
'metric7': df_metric7}
|
| 385 |
|
| 386 |
+
|
| 387 |
number_of_workers = len(df_workers)
|
| 388 |
print('number of workers', number_of_workers)
|
| 389 |
|
|
|
|
| 399 |
"metric2": {"desc": "Number of children with no access to education", "value": 0, "max_value": number_of_students},
|
| 400 |
"metric3": {"desc": "Number of households with no access to hospital", "value": 0, "max_value": number_of_households},
|
| 401 |
"metric4": {"desc": "Number of individuals with no access to hospital", "value": 0, "max_value": number_of_individuals},
|
| 402 |
+
"metric5": {"desc": "Number of households displaced", "value": 0, "max_value": number_of_households},
|
| 403 |
"metric6": {"desc": "Number of homeless individuals", "value": 0, "max_value": number_of_individuals},
|
| 404 |
"metric7": {"desc": "Population displacement", "value": 0, "max_value": number_of_individuals},}
|
| 405 |
metrics["metric1"]["value"] = int(df_metric1['metric1'].sum())
|
|
|
|
| 410 |
metrics["metric6"]["value"] = int(df_metric6['metric6'].sum())
|
| 411 |
metrics["metric7"]["value"] = int(df_metric7['metric7'].sum())
|
| 412 |
|
| 413 |
+
return metrics, df_metrics
|
|
|
tomorrowcities/content/articles/metrics.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
author: huseyin.kaya
|
| 3 |
+
title: Metrics
|
| 4 |
+
description: A brief introduction to metrics
|
| 5 |
+
image: https://images.unsplash.com/photo-1429041966141-44d228a42775?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=2500&q=80
|
| 6 |
+
thumbnail: https://images.unsplash.com/photo-1429041966141-44d228a42775?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=350&q=80
|
| 7 |
+
alt: "Metrics"
|
| 8 |
+
createdAt: 2023-10-10
|
| 9 |
+
duration: 6 min read
|
| 10 |
+
category:
|
| 11 |
+
- general
|
| 12 |
+
---
|
| 13 |
+
|
| 14 |
+
## Metrics
|
| 15 |
+
There are seven fundamental impact metrics displayed in the web application. For each them there is an associcated threhold but for the sake of simplicity, it is not explicitly stated.
|
| 16 |
+
|
| 17 |
+
### Metric 1: Number of workers unemployed
|
| 18 |
+
It denotes the number of invidivuals who last their jobs either due to a severe damage at the workplace
|
| 19 |
+
or lost access to a functional workplace. When the metrics is displayed on a building-level, it is the sum of
|
| 20 |
+
such individuals living in that building.
|
| 21 |
+
|
| 22 |
+
### Metric 2: Number of children with no access to education
|
| 23 |
+
Similar to the first metrics but individual here refers to a child who is associated with a school.
|
| 24 |
+
The metric becomes active if the school is damaged or not accessible.
|
| 25 |
+
|
| 26 |
+
### Metric 3: Number of households with no access to hospital
|
| 27 |
+
The number of households who lost its access to its associated hospital.
|
| 28 |
+
Either hospital is damaged or the path between the household and the hospital
|
| 29 |
+
is broken.
|
| 30 |
+
|
| 31 |
+
### Metric 4: Number of individuals with no access to hospital
|
| 32 |
+
It is derived from metric 3 by counting the individuals in the corresponding households.
|
| 33 |
+
|
| 34 |
+
### Metric 5: Number of households displaced
|
| 35 |
+
It is direct result of building damage state. If a building is damaged, then the households in it are also
|
| 36 |
+
damaged.
|
| 37 |
+
|
| 38 |
+
### Metric 6: Number of homeless individuals
|
| 39 |
+
It is derived from metric 5.
|
| 40 |
+
|
| 41 |
+
### Metric 7: Population displacement
|
| 42 |
+
An individual is assumed to be displaced if the associated household is damaged, or he/she lost access to workplace, schoold or hospital.
|
tomorrowcities/content/articles/policies.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
author: huseyin.kaya
|
| 3 |
+
title: Policies in a nutshell
|
| 4 |
+
description: A brief introduction to policies
|
| 5 |
+
image: https://images.unsplash.com/photo-1429041966141-44d228a42775?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=2500&q=80
|
| 6 |
+
thumbnail: https://images.unsplash.com/photo-1429041966141-44d228a42775?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=350&q=80
|
| 7 |
+
alt: "Policies"
|
| 8 |
+
createdAt: 2023-10-10
|
| 9 |
+
duration: 6 min read
|
| 10 |
+
category:
|
| 11 |
+
- general
|
| 12 |
+
---
|
| 13 |
+
|
| 14 |
+
### Policy 1: Land and tenure security program
|
| 15 |
+
When this policy applied, the code levels of the residential buildings are set to higher standards
|
| 16 |
+
which effectively reduces the damage states during hazard simulations. Since this policy only changes
|
| 17 |
+
the code levels, the computing engine is required to run again to see its effect.
|
| 18 |
+
|
| 19 |
+
### Policy 2: State-led upgrading/retrofitting of low-income/informal housing
|
| 20 |
+
When applied, this policy increase the code level of the buildings located in low income A and B zones.
|
| 21 |
+
This effectively reduces the damage states, and hence several impact metrics, starting from building-level
|
| 22 |
+
metrics.
|
tomorrowcities/content/articles/welcome.md
CHANGED
|
@@ -14,6 +14,17 @@ category:
|
|
| 14 |
## Tomorrow's Cities Decision Support Environment (TCDSE)
|
| 15 |
TCDSE is a web application designed to conduct computational tasks to generate information needed for decision mechanisms in designing future cities. The web application, which will be referred as TCDSE for short, contains a computational engine capable of executing several hazard scenarios on different exposure datasets and infrastructures.
|
| 16 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
## Features
|
| 18 |
General capabilities/features of the web application can be summarized as follows:
|
| 19 |
|
|
|
|
| 14 |
## Tomorrow's Cities Decision Support Environment (TCDSE)
|
| 15 |
TCDSE is a web application designed to conduct computational tasks to generate information needed for decision mechanisms in designing future cities. The web application, which will be referred as TCDSE for short, contains a computational engine capable of executing several hazard scenarios on different exposure datasets and infrastructures.
|
| 16 |
|
| 17 |
+
## What is New?
|
| 18 |
+
|
| 19 |
+
### Policy Support
|
| 20 |
+
Two new policies are added to the computing engine. More info is [here](/docs/policies)
|
| 21 |
+
|
| 22 |
+
### Excel support:
|
| 23 |
+
You can upload Excel files containing your tabular data such as individual, household, fragility or vulnerability data. However, processing excel files is very slower than processing JSON files so I definitely suggest working with JSON files. You can convert your Excel files via panda framework. The platform also does not try to convert the coordinates even if there is any in the Excel file because there is no way to know which columns represent the coordinates or coordinate reference systems without metadata. So Excel spreadsheets should be used to provide non-geo related tabular data. So please use them only for data not containing any geo-specific information.
|
| 24 |
+
|
| 25 |
+
### Dar Es Salaam Study
|
| 26 |
+
[Sample Dataset](https://drive.google.com/file/d/1BGPZQ2IKJHY9ExOCCHcNNrCTioYZ8D1y/view?usp=sharing) now contains some visioning scenario for Dar Es Salaam case.
|
| 27 |
+
|
| 28 |
## Features
|
| 29 |
General capabilities/features of the web application can be summarized as follows:
|
| 30 |
|
tomorrowcities/pages/engine.py
CHANGED
|
@@ -10,10 +10,10 @@ from typing import Tuple, Optional
|
|
| 10 |
import ipyleaflet
|
| 11 |
from ipyleaflet import AwesomeIcon, Marker
|
| 12 |
import numpy as np
|
| 13 |
-
import sys
|
| 14 |
-
|
| 15 |
-
from ..backend.engine import compute, compute_power_infra
|
| 16 |
|
|
|
|
| 17 |
|
| 18 |
layers = solara.reactive({
|
| 19 |
'layers' : {
|
|
@@ -23,6 +23,7 @@ layers = solara.reactive({
|
|
| 23 |
'force_render': solara.reactive(False),
|
| 24 |
'visible': solara.reactive(False),
|
| 25 |
'extra_cols': {'ds': 0, 'metric1': 0, 'metric2': 0, 'metric3': 0,'metric4': 0, 'metric5': 0,'metric6': 0,'metric7': 0},
|
|
|
|
| 26 |
'cols': set(['residents', 'fptarea', 'repvalue', 'nhouse', 'zoneid', 'expstr', 'bldid', 'geometry', 'specialfac'])},
|
| 27 |
'landuse': {
|
| 28 |
'df': solara.reactive(None),
|
|
@@ -30,6 +31,7 @@ layers = solara.reactive({
|
|
| 30 |
'force_render': solara.reactive(False),
|
| 31 |
'visible': solara.reactive(False),
|
| 32 |
'extra_cols': {},
|
|
|
|
| 33 |
'cols': set(['geometry', 'zoneid', 'luf', 'population', 'densitycap', 'floorarat', 'setback', 'avgincome'])},
|
| 34 |
'household': {
|
| 35 |
'df': solara.reactive(None),
|
|
@@ -37,6 +39,7 @@ layers = solara.reactive({
|
|
| 37 |
'force_render': solara.reactive(False),
|
| 38 |
'visible': solara.reactive(False),
|
| 39 |
'extra_cols': {},
|
|
|
|
| 40 |
'cols':set(['hhid', 'nind', 'income', 'bldid', 'commfacid'])},
|
| 41 |
'individual': {
|
| 42 |
'df': solara.reactive(None),
|
|
@@ -44,6 +47,7 @@ layers = solara.reactive({
|
|
| 44 |
'force_render': solara.reactive(False),
|
| 45 |
'visible': solara.reactive(False),
|
| 46 |
'extra_cols': {},
|
|
|
|
| 47 |
'cols': set(['individ', 'hhid', 'gender', 'age', 'eduattstat', 'head', 'indivfacid'])},
|
| 48 |
'intensity': {
|
| 49 |
'df': solara.reactive(None),
|
|
@@ -51,6 +55,7 @@ layers = solara.reactive({
|
|
| 51 |
'force_render': solara.reactive(False),
|
| 52 |
'visible': solara.reactive(False),
|
| 53 |
'extra_cols': {},
|
|
|
|
| 54 |
'cols': set(['geometry','im'])},
|
| 55 |
'fragility': {
|
| 56 |
'df': solara.reactive(None),
|
|
@@ -58,6 +63,7 @@ layers = solara.reactive({
|
|
| 58 |
'force_render': solara.reactive(False),
|
| 59 |
'visible': solara.reactive(False),
|
| 60 |
'extra_cols': {},
|
|
|
|
| 61 |
'cols': set(['expstr','muds1_g','muds2_g','muds3_g','muds4_g','sigmads1','sigmads2','sigmads3','sigmads4'])},
|
| 62 |
'vulnerability': {
|
| 63 |
'df': solara.reactive(None),
|
|
@@ -65,6 +71,7 @@ layers = solara.reactive({
|
|
| 65 |
'force_render': solara.reactive(False),
|
| 66 |
'visible': solara.reactive(False),
|
| 67 |
'extra_cols': {},
|
|
|
|
| 68 |
'cols': set(['expstr', 'hw0', 'hw0_5', 'hw1', 'hw1_5', 'hw2', 'hw3', 'hw4', 'hw5','hw6'])},
|
| 69 |
'power nodes': {
|
| 70 |
'df': solara.reactive(None),
|
|
@@ -72,6 +79,9 @@ layers = solara.reactive({
|
|
| 72 |
'force_render': solara.reactive(False),
|
| 73 |
'visible': solara.reactive(False),
|
| 74 |
'extra_cols': {'ds': 0, 'is_damaged': False, 'is_operational': True},
|
|
|
|
|
|
|
|
|
|
| 75 |
'cols': set(['geometry', 'fltytype', 'strctype', 'utilfcltyc', 'indpnode', 'guid',
|
| 76 |
'node_id', 'x_coord', 'y_coord', 'pwr_plant', 'serv_area', 'n_bldgs',
|
| 77 |
'income', 'eq_vuln'])},
|
|
@@ -81,6 +91,8 @@ layers = solara.reactive({
|
|
| 81 |
'force_render': solara.reactive(False),
|
| 82 |
'visible': solara.reactive(False),
|
| 83 |
'extra_cols': {},
|
|
|
|
|
|
|
| 84 |
'cols': set(['from_node', 'direction', 'pipetype', 'edge_id', 'guid', 'capacity',
|
| 85 |
'geometry', 'to_node', 'length'])},
|
| 86 |
'power fragility': {
|
|
@@ -89,6 +101,8 @@ layers = solara.reactive({
|
|
| 89 |
'force_render': solara.reactive(False),
|
| 90 |
'visible': solara.reactive(False),
|
| 91 |
'extra_cols': {},
|
|
|
|
|
|
|
| 92 |
'cols': set(['vuln_string', 'med_slight', 'med_moderate', 'med_extensive', 'med_complete',
|
| 93 |
'beta_slight', 'beta_moderate', 'beta_extensive', 'beta_complete', 'description'])}
|
| 94 |
},
|
|
@@ -96,12 +110,16 @@ layers = solara.reactive({
|
|
| 96 |
'selected_layer' : solara.reactive(None),
|
| 97 |
'render_count': solara.reactive(0),
|
| 98 |
'bounds': solara.reactive(None),
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
'metrics': {
|
| 100 |
"metric1": {"desc": "Number of workers unemployed", "value": 0, "max_value": 100},
|
| 101 |
"metric2": {"desc": "Number of children with no access to education", "value": 0, "max_value": 100},
|
| 102 |
"metric3": {"desc": "Number of households with no access to hospital", "value": 0, "max_value": 100},
|
| 103 |
"metric4": {"desc": "Number of individuals with no access to hospital", "value": 0, "max_value": 100},
|
| 104 |
-
"metric5": {"desc": "Number of
|
| 105 |
"metric6": {"desc": "Number of homeless individuals", "value": 0, "max_value": 100},
|
| 106 |
"metric7": {"desc": "Population displacement", "value": 0, "max_value":100},}})
|
| 107 |
|
|
@@ -200,21 +218,35 @@ def MetricWidget(name, description, value, max_value, render_count):
|
|
| 200 |
solara.FigureEcharts(option=options, attributes={ "style": "height: 100px; width: 100px" })
|
| 201 |
|
| 202 |
|
| 203 |
-
def import_data(
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
if
|
| 207 |
-
df =
|
| 208 |
else:
|
| 209 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 210 |
|
| 211 |
df.columns = df.columns.str.lower()
|
| 212 |
|
|
|
|
| 213 |
name = None
|
| 214 |
for layer_name, layer in layers.value['layers'].items():
|
| 215 |
if layer['cols'] == set(df.columns):
|
| 216 |
name = layer_name
|
| 217 |
break
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 218 |
|
| 219 |
# Inject columns
|
| 220 |
if name is not None:
|
|
@@ -232,7 +264,7 @@ def FileDropZone():
|
|
| 232 |
def load():
|
| 233 |
if fileinfo is not None:
|
| 234 |
print('processing file')
|
| 235 |
-
name, df = import_data(fileinfo
|
| 236 |
if name is not None and df is not None:
|
| 237 |
layers.value['layers'][name]['df'].set(df)
|
| 238 |
layers.value['selected_layer'].set(name)
|
|
@@ -278,7 +310,7 @@ def FileDropZone():
|
|
| 278 |
|
| 279 |
@solara.component
|
| 280 |
def LayerDisplayer():
|
| 281 |
-
|
| 282 |
nonempty_layers = {name: layer for name, layer in layers.value['layers'].items() if layer['df'].value is not None}
|
| 283 |
nonempty_layer_names = list(nonempty_layers.keys())
|
| 284 |
selected = layers.value['selected_layer'].value
|
|
@@ -290,9 +322,13 @@ def LayerDisplayer():
|
|
| 290 |
if selected is None and len(nonempty_layer_names) > 0:
|
| 291 |
set_selected(nonempty_layer_names[0])
|
| 292 |
if selected is not None:
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 296 |
|
| 297 |
@solara.component
|
| 298 |
def MetricPanel():
|
|
@@ -310,6 +346,8 @@ def MetricPanel():
|
|
| 310 |
filtered_metrics[name],
|
| 311 |
metric['max_value'],
|
| 312 |
layers.value['render_count'].value)
|
|
|
|
|
|
|
| 313 |
|
| 314 |
|
| 315 |
@solara.component
|
|
@@ -329,9 +367,6 @@ def MapViewer():
|
|
| 329 |
zoom, set_zoom = solara.use_state(default_zoom)
|
| 330 |
#center, set_center = solara.use_state(default_center)
|
| 331 |
|
| 332 |
-
def set_bounds(bounds):
|
| 333 |
-
layers.value['bounds'].set(bounds)
|
| 334 |
-
|
| 335 |
base_map = ipyleaflet.basemaps["Stamen"]["Watercolor"]
|
| 336 |
base_layer = ipyleaflet.TileLayer.element(url=base_map.build_url())
|
| 337 |
map_layers = [base_layer]
|
|
@@ -350,7 +385,7 @@ def MapViewer():
|
|
| 350 |
ipyleaflet.Map.element(
|
| 351 |
zoom=zoom,
|
| 352 |
on_zoom=set_zoom,
|
| 353 |
-
on_bounds=
|
| 354 |
center=layers.value['center'].value,
|
| 355 |
on_center=layers.value['center'].set,
|
| 356 |
scroll_wheel_zoom=True,
|
|
@@ -361,21 +396,11 @@ def MapViewer():
|
|
| 361 |
keyboard=True if random.random() > 0.5 else False,
|
| 362 |
layers=map_layers
|
| 363 |
)
|
| 364 |
-
|
| 365 |
-
|
| 366 |
-
@solara.component
|
| 367 |
-
def DataframeDisplayer(df, render_count, bounds):
|
| 368 |
-
df, set_df = solara.use_state_or_update(df)
|
| 369 |
-
if "geometry" in df.columns:
|
| 370 |
-
((ymin,xmin),(ymax,xmax)) = bounds
|
| 371 |
-
solara.DataFrame(df.cx[xmin:xmax,ymin:ymax].drop(columns='geometry'))
|
| 372 |
-
else:
|
| 373 |
-
solara.DataFrame(df)
|
| 374 |
-
|
| 375 |
@solara.component
|
| 376 |
def ExecutePanel():
|
| 377 |
-
infra, set_infra = solara.use_state(["
|
| 378 |
-
hazard, set_hazard = solara.use_state("
|
| 379 |
|
| 380 |
|
| 381 |
execute_counter, set_execute_counter = solara.use_state(0)
|
|
@@ -394,12 +419,12 @@ def ExecutePanel():
|
|
| 394 |
if "power" in infra:
|
| 395 |
missing += list(set(["power edges","power nodes","intensity","power fragility"]) - existing_layers)
|
| 396 |
if "building" in infra:
|
| 397 |
-
missing += list(set(["building","household","individual","intensity","fragility"]) - existing_layers)
|
| 398 |
elif hazard == "flood":
|
| 399 |
if "power" in infra:
|
| 400 |
missing += list(set(["power edges","power nodes","intensity","power vulnerability"]) - existing_layers)
|
| 401 |
if "building" in infra:
|
| 402 |
-
missing += list(set(["building","household","individual","intensity","vulnerability"]) - existing_layers)
|
| 403 |
|
| 404 |
if infra == []:
|
| 405 |
missing += ['You should select power and/or building']
|
|
@@ -429,6 +454,7 @@ def ExecutePanel():
|
|
| 429 |
return nodes
|
| 430 |
|
| 431 |
def execute_building():
|
|
|
|
| 432 |
buildings = layers.value['layers']['building']['df'].value
|
| 433 |
household = layers.value['layers']['household']['df'].value
|
| 434 |
individual = layers.value['layers']['individual']['df'].value
|
|
@@ -436,20 +462,27 @@ def ExecutePanel():
|
|
| 436 |
|
| 437 |
fragility = layers.value['layers']['fragility']['df'].value
|
| 438 |
vulnerability = layers.value['layers']['vulnerability']['df'].value
|
| 439 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 440 |
buildings,
|
| 441 |
household,
|
| 442 |
individual,
|
| 443 |
intensity,
|
| 444 |
fragility if hazard == "earthquake" else vulnerability,
|
| 445 |
-
hazard)
|
| 446 |
-
|
|
|
|
|
|
|
| 447 |
print(computed_metrics)
|
| 448 |
for metric in df_metrics.keys():
|
| 449 |
buildings[metric] = list(df_metrics[metric][metric])
|
| 450 |
layers.value['metrics'][metric]['value'] = computed_metrics[metric]['value']
|
| 451 |
layers.value['metrics'][metric]['max_value'] = computed_metrics[metric]['max_value']
|
| 452 |
-
|
| 453 |
return buildings
|
| 454 |
|
| 455 |
if execute_counter > 0 :
|
|
@@ -477,10 +510,12 @@ def ExecutePanel():
|
|
| 477 |
result = solara.use_thread(execute_engine, dependencies=[execute_counter])
|
| 478 |
|
| 479 |
with solara.Row(justify="center"):
|
| 480 |
-
solara.ToggleButtonsMultiple(value=infra, on_value=set_infra, values=["
|
| 481 |
with solara.Row(justify="center"):
|
| 482 |
solara.ToggleButtonsSingle(value=hazard, on_value=set_hazard, values=["earthquake","flood"])
|
| 483 |
|
|
|
|
|
|
|
| 484 |
solara.Button("Calculate", on_click=on_click, outlined=True,
|
| 485 |
disabled=execute_btn_disabled)
|
| 486 |
# The statements in this block are passed several times during thread execution
|
|
@@ -499,13 +534,22 @@ def ExecutePanel():
|
|
| 499 |
set_execute_btn_disabled(False)
|
| 500 |
solara.ProgressLinear(value=False)
|
| 501 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 502 |
|
| 503 |
@solara.component
|
| 504 |
def WebApp():
|
| 505 |
|
| 506 |
with solara.Columns([30,60]):
|
| 507 |
with solara.Column():
|
| 508 |
-
|
| 509 |
solara.Markdown('[Download Sample Dataset](https://drive.google.com/file/d/1BGPZQ2IKJHY9ExOCCHcNNrCTioYZ8D1y/view?usp=sharing)')
|
| 510 |
FileDropZone()
|
| 511 |
ExecutePanel()
|
|
|
|
| 10 |
import ipyleaflet
|
| 11 |
from ipyleaflet import AwesomeIcon, Marker
|
| 12 |
import numpy as np
|
| 13 |
+
import logging, sys
|
| 14 |
+
#logging.basicConfig(stream=sys.stderr, level=logging.INFO)
|
|
|
|
| 15 |
|
| 16 |
+
from ..backend.engine import compute, compute_power_infra, calculate_metrics
|
| 17 |
|
| 18 |
layers = solara.reactive({
|
| 19 |
'layers' : {
|
|
|
|
| 23 |
'force_render': solara.reactive(False),
|
| 24 |
'visible': solara.reactive(False),
|
| 25 |
'extra_cols': {'ds': 0, 'metric1': 0, 'metric2': 0, 'metric3': 0,'metric4': 0, 'metric5': 0,'metric6': 0,'metric7': 0},
|
| 26 |
+
'cols_required': set(['residents', 'fptarea', 'repvalue', 'nhouse', 'zoneid', 'expstr', 'bldid', 'geometry', 'specialfac']),
|
| 27 |
'cols': set(['residents', 'fptarea', 'repvalue', 'nhouse', 'zoneid', 'expstr', 'bldid', 'geometry', 'specialfac'])},
|
| 28 |
'landuse': {
|
| 29 |
'df': solara.reactive(None),
|
|
|
|
| 31 |
'force_render': solara.reactive(False),
|
| 32 |
'visible': solara.reactive(False),
|
| 33 |
'extra_cols': {},
|
| 34 |
+
'cols_required': set(['geometry', 'zoneid', 'luf', 'population', 'densitycap', 'avgincome']),
|
| 35 |
'cols': set(['geometry', 'zoneid', 'luf', 'population', 'densitycap', 'floorarat', 'setback', 'avgincome'])},
|
| 36 |
'household': {
|
| 37 |
'df': solara.reactive(None),
|
|
|
|
| 39 |
'force_render': solara.reactive(False),
|
| 40 |
'visible': solara.reactive(False),
|
| 41 |
'extra_cols': {},
|
| 42 |
+
'cols_required':set(['hhid', 'nind', 'income', 'bldid', 'commfacid']),
|
| 43 |
'cols':set(['hhid', 'nind', 'income', 'bldid', 'commfacid'])},
|
| 44 |
'individual': {
|
| 45 |
'df': solara.reactive(None),
|
|
|
|
| 47 |
'force_render': solara.reactive(False),
|
| 48 |
'visible': solara.reactive(False),
|
| 49 |
'extra_cols': {},
|
| 50 |
+
'cols_required': set(['individ', 'hhid', 'gender', 'age', 'eduattstat', 'head', 'indivfacid']),
|
| 51 |
'cols': set(['individ', 'hhid', 'gender', 'age', 'eduattstat', 'head', 'indivfacid'])},
|
| 52 |
'intensity': {
|
| 53 |
'df': solara.reactive(None),
|
|
|
|
| 55 |
'force_render': solara.reactive(False),
|
| 56 |
'visible': solara.reactive(False),
|
| 57 |
'extra_cols': {},
|
| 58 |
+
'cols_required': set(['geometry','im']),
|
| 59 |
'cols': set(['geometry','im'])},
|
| 60 |
'fragility': {
|
| 61 |
'df': solara.reactive(None),
|
|
|
|
| 63 |
'force_render': solara.reactive(False),
|
| 64 |
'visible': solara.reactive(False),
|
| 65 |
'extra_cols': {},
|
| 66 |
+
'cols_required': set(['expstr','muds1_g','muds2_g','muds3_g','muds4_g','sigmads1','sigmads2','sigmads3','sigmads4']),
|
| 67 |
'cols': set(['expstr','muds1_g','muds2_g','muds3_g','muds4_g','sigmads1','sigmads2','sigmads3','sigmads4'])},
|
| 68 |
'vulnerability': {
|
| 69 |
'df': solara.reactive(None),
|
|
|
|
| 71 |
'force_render': solara.reactive(False),
|
| 72 |
'visible': solara.reactive(False),
|
| 73 |
'extra_cols': {},
|
| 74 |
+
'cols_required': set(['expstr', 'hw0', 'hw0_5', 'hw1', 'hw1_5', 'hw2', 'hw3', 'hw4', 'hw5','hw6']),
|
| 75 |
'cols': set(['expstr', 'hw0', 'hw0_5', 'hw1', 'hw1_5', 'hw2', 'hw3', 'hw4', 'hw5','hw6'])},
|
| 76 |
'power nodes': {
|
| 77 |
'df': solara.reactive(None),
|
|
|
|
| 79 |
'force_render': solara.reactive(False),
|
| 80 |
'visible': solara.reactive(False),
|
| 81 |
'extra_cols': {'ds': 0, 'is_damaged': False, 'is_operational': True},
|
| 82 |
+
'cols_required': set(['geometry', 'fltytype', 'strctype', 'utilfcltyc', 'indpnode', 'guid',
|
| 83 |
+
'node_id', 'x_coord', 'y_coord', 'pwr_plant', 'serv_area', 'n_bldgs',
|
| 84 |
+
'income', 'eq_vuln']),
|
| 85 |
'cols': set(['geometry', 'fltytype', 'strctype', 'utilfcltyc', 'indpnode', 'guid',
|
| 86 |
'node_id', 'x_coord', 'y_coord', 'pwr_plant', 'serv_area', 'n_bldgs',
|
| 87 |
'income', 'eq_vuln'])},
|
|
|
|
| 91 |
'force_render': solara.reactive(False),
|
| 92 |
'visible': solara.reactive(False),
|
| 93 |
'extra_cols': {},
|
| 94 |
+
'cols_required': set(['from_node', 'direction', 'pipetype', 'edge_id', 'guid', 'capacity',
|
| 95 |
+
'geometry', 'to_node', 'length']),
|
| 96 |
'cols': set(['from_node', 'direction', 'pipetype', 'edge_id', 'guid', 'capacity',
|
| 97 |
'geometry', 'to_node', 'length'])},
|
| 98 |
'power fragility': {
|
|
|
|
| 101 |
'force_render': solara.reactive(False),
|
| 102 |
'visible': solara.reactive(False),
|
| 103 |
'extra_cols': {},
|
| 104 |
+
'cols_required': set(['vuln_string', 'med_slight', 'med_moderate', 'med_extensive', 'med_complete',
|
| 105 |
+
'beta_slight', 'beta_moderate', 'beta_extensive', 'beta_complete']),
|
| 106 |
'cols': set(['vuln_string', 'med_slight', 'med_moderate', 'med_extensive', 'med_complete',
|
| 107 |
'beta_slight', 'beta_moderate', 'beta_extensive', 'beta_complete', 'description'])}
|
| 108 |
},
|
|
|
|
| 110 |
'selected_layer' : solara.reactive(None),
|
| 111 |
'render_count': solara.reactive(0),
|
| 112 |
'bounds': solara.reactive(None),
|
| 113 |
+
'policies': {
|
| 114 |
+
'1': {'id':1, 'label': 'Policy 1', 'description': 'Land and tenure security program', 'applied': solara.reactive(False)},
|
| 115 |
+
'2': {'id':2, 'label': 'Policy 2', 'description': 'State-led upgrading/retrofitting of low-income/informal housing', 'applied': solara.reactive(False)},
|
| 116 |
+
},
|
| 117 |
'metrics': {
|
| 118 |
"metric1": {"desc": "Number of workers unemployed", "value": 0, "max_value": 100},
|
| 119 |
"metric2": {"desc": "Number of children with no access to education", "value": 0, "max_value": 100},
|
| 120 |
"metric3": {"desc": "Number of households with no access to hospital", "value": 0, "max_value": 100},
|
| 121 |
"metric4": {"desc": "Number of individuals with no access to hospital", "value": 0, "max_value": 100},
|
| 122 |
+
"metric5": {"desc": "Number of households displaced", "value": 0, "max_value": 100},
|
| 123 |
"metric6": {"desc": "Number of homeless individuals", "value": 0, "max_value": 100},
|
| 124 |
"metric7": {"desc": "Population displacement", "value": 0, "max_value":100},}})
|
| 125 |
|
|
|
|
| 218 |
solara.FigureEcharts(option=options, attributes={ "style": "height: 100px; width: 100px" })
|
| 219 |
|
| 220 |
|
| 221 |
+
def import_data(fileinfo: solara.components.file_drop.FileInfo):
|
| 222 |
+
data = fileinfo['data']
|
| 223 |
+
extension = fileinfo['name'].split('.')[-1]
|
| 224 |
+
if extension == 'xlsx':
|
| 225 |
+
df = pd.read_excel(data)
|
| 226 |
else:
|
| 227 |
+
json_string = data.decode('utf-8')
|
| 228 |
+
json_data = json.loads(json_string)
|
| 229 |
+
if "features" in json_data.keys():
|
| 230 |
+
df = gpd.GeoDataFrame.from_features(json_data['features'])
|
| 231 |
+
else:
|
| 232 |
+
df = pd.read_json(json_string)
|
| 233 |
|
| 234 |
df.columns = df.columns.str.lower()
|
| 235 |
|
| 236 |
+
# in the first pass, look for exact column match
|
| 237 |
name = None
|
| 238 |
for layer_name, layer in layers.value['layers'].items():
|
| 239 |
if layer['cols'] == set(df.columns):
|
| 240 |
name = layer_name
|
| 241 |
break
|
| 242 |
+
# if not, check only the required columns
|
| 243 |
+
if name is None:
|
| 244 |
+
for layer_name, layer in layers.value['layers'].items():
|
| 245 |
+
if layer['cols_required'].issubset(set(df.columns)):
|
| 246 |
+
name = layer_name
|
| 247 |
+
logging.debug('There are extra columns', set(df.columns) - layer['cols_required'])
|
| 248 |
+
break
|
| 249 |
+
|
| 250 |
|
| 251 |
# Inject columns
|
| 252 |
if name is not None:
|
|
|
|
| 264 |
def load():
|
| 265 |
if fileinfo is not None:
|
| 266 |
print('processing file')
|
| 267 |
+
name, df = import_data(fileinfo)
|
| 268 |
if name is not None and df is not None:
|
| 269 |
layers.value['layers'][name]['df'].set(df)
|
| 270 |
layers.value['selected_layer'].set(name)
|
|
|
|
| 310 |
|
| 311 |
@solara.component
|
| 312 |
def LayerDisplayer():
|
| 313 |
+
print(f'{layers.value["bounds"].value}')
|
| 314 |
nonempty_layers = {name: layer for name, layer in layers.value['layers'].items() if layer['df'].value is not None}
|
| 315 |
nonempty_layer_names = list(nonempty_layers.keys())
|
| 316 |
selected = layers.value['selected_layer'].value
|
|
|
|
| 322 |
if selected is None and len(nonempty_layer_names) > 0:
|
| 323 |
set_selected(nonempty_layer_names[0])
|
| 324 |
if selected is not None:
|
| 325 |
+
df = nonempty_layers[selected]['df'].value
|
| 326 |
+
if "geometry" in df.columns:
|
| 327 |
+
((ymin,xmin),(ymax,xmax)) = layers.value['bounds'].value
|
| 328 |
+
solara.DataFrame(df.cx[xmin:xmax,ymin:ymax].drop(columns='geometry'))
|
| 329 |
+
else:
|
| 330 |
+
solara.DataFrame(df)
|
| 331 |
+
|
| 332 |
|
| 333 |
@solara.component
|
| 334 |
def MetricPanel():
|
|
|
|
| 346 |
filtered_metrics[name],
|
| 347 |
metric['max_value'],
|
| 348 |
layers.value['render_count'].value)
|
| 349 |
+
with solara.Link("/docs/metrics"):
|
| 350 |
+
solara.Button(icon_name="mdi-help-circle-outline", icon=True)
|
| 351 |
|
| 352 |
|
| 353 |
@solara.component
|
|
|
|
| 367 |
zoom, set_zoom = solara.use_state(default_zoom)
|
| 368 |
#center, set_center = solara.use_state(default_center)
|
| 369 |
|
|
|
|
|
|
|
|
|
|
| 370 |
base_map = ipyleaflet.basemaps["Stamen"]["Watercolor"]
|
| 371 |
base_layer = ipyleaflet.TileLayer.element(url=base_map.build_url())
|
| 372 |
map_layers = [base_layer]
|
|
|
|
| 385 |
ipyleaflet.Map.element(
|
| 386 |
zoom=zoom,
|
| 387 |
on_zoom=set_zoom,
|
| 388 |
+
on_bounds=layers.value['bounds'].set,
|
| 389 |
center=layers.value['center'].value,
|
| 390 |
on_center=layers.value['center'].set,
|
| 391 |
scroll_wheel_zoom=True,
|
|
|
|
| 396 |
keyboard=True if random.random() > 0.5 else False,
|
| 397 |
layers=map_layers
|
| 398 |
)
|
| 399 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 400 |
@solara.component
|
| 401 |
def ExecutePanel():
|
| 402 |
+
infra, set_infra = solara.use_state(["building"])
|
| 403 |
+
hazard, set_hazard = solara.use_state("flood")
|
| 404 |
|
| 405 |
|
| 406 |
execute_counter, set_execute_counter = solara.use_state(0)
|
|
|
|
| 419 |
if "power" in infra:
|
| 420 |
missing += list(set(["power edges","power nodes","intensity","power fragility"]) - existing_layers)
|
| 421 |
if "building" in infra:
|
| 422 |
+
missing += list(set(["landuse","building","household","individual","intensity","fragility"]) - existing_layers)
|
| 423 |
elif hazard == "flood":
|
| 424 |
if "power" in infra:
|
| 425 |
missing += list(set(["power edges","power nodes","intensity","power vulnerability"]) - existing_layers)
|
| 426 |
if "building" in infra:
|
| 427 |
+
missing += list(set(["landuse","building","household","individual","intensity","vulnerability"]) - existing_layers)
|
| 428 |
|
| 429 |
if infra == []:
|
| 430 |
missing += ['You should select power and/or building']
|
|
|
|
| 454 |
return nodes
|
| 455 |
|
| 456 |
def execute_building():
|
| 457 |
+
landuse = layers.value['layers']['landuse']['df'].value
|
| 458 |
buildings = layers.value['layers']['building']['df'].value
|
| 459 |
household = layers.value['layers']['household']['df'].value
|
| 460 |
individual = layers.value['layers']['individual']['df'].value
|
|
|
|
| 462 |
|
| 463 |
fragility = layers.value['layers']['fragility']['df'].value
|
| 464 |
vulnerability = layers.value['layers']['vulnerability']['df'].value
|
| 465 |
+
|
| 466 |
+
policies = [p['id'] for id, p in layers.value['policies'].items() if p['applied'].value]
|
| 467 |
+
|
| 468 |
+
print('policies',policies)
|
| 469 |
+
df_bld_hazard = compute(
|
| 470 |
+
landuse,
|
| 471 |
buildings,
|
| 472 |
household,
|
| 473 |
individual,
|
| 474 |
intensity,
|
| 475 |
fragility if hazard == "earthquake" else vulnerability,
|
| 476 |
+
hazard,policies=policies)
|
| 477 |
+
buildings['ds'] = list(df_bld_hazard['ds'])
|
| 478 |
+
computed_metrics, df_metrics = calculate_metrics(buildings, household, individual, hazard, policies=policies)
|
| 479 |
+
|
| 480 |
print(computed_metrics)
|
| 481 |
for metric in df_metrics.keys():
|
| 482 |
buildings[metric] = list(df_metrics[metric][metric])
|
| 483 |
layers.value['metrics'][metric]['value'] = computed_metrics[metric]['value']
|
| 484 |
layers.value['metrics'][metric]['max_value'] = computed_metrics[metric]['max_value']
|
| 485 |
+
|
| 486 |
return buildings
|
| 487 |
|
| 488 |
if execute_counter > 0 :
|
|
|
|
| 510 |
result = solara.use_thread(execute_engine, dependencies=[execute_counter])
|
| 511 |
|
| 512 |
with solara.Row(justify="center"):
|
| 513 |
+
solara.ToggleButtonsMultiple(value=infra, on_value=set_infra, values=["building","power"])
|
| 514 |
with solara.Row(justify="center"):
|
| 515 |
solara.ToggleButtonsSingle(value=hazard, on_value=set_hazard, values=["earthquake","flood"])
|
| 516 |
|
| 517 |
+
PolicyPanel()
|
| 518 |
+
solara.ProgressLinear(value=False)
|
| 519 |
solara.Button("Calculate", on_click=on_click, outlined=True,
|
| 520 |
disabled=execute_btn_disabled)
|
| 521 |
# The statements in this block are passed several times during thread execution
|
|
|
|
| 534 |
set_execute_btn_disabled(False)
|
| 535 |
solara.ProgressLinear(value=False)
|
| 536 |
|
| 537 |
+
@solara.component
|
| 538 |
+
def PolicyPanel():
|
| 539 |
+
with solara.Row():
|
| 540 |
+
for policy_key, policy in layers.value['policies'].items():
|
| 541 |
+
with solara.Tooltip(tooltip=policy['description']):
|
| 542 |
+
solara.Checkbox(label=policy['label'],
|
| 543 |
+
value=policy['applied'])
|
| 544 |
+
with solara.Link("/docs/policies"):
|
| 545 |
+
solara.Button(icon_name="mdi-help-circle-outline", icon=True)
|
| 546 |
+
|
| 547 |
|
| 548 |
@solara.component
|
| 549 |
def WebApp():
|
| 550 |
|
| 551 |
with solara.Columns([30,60]):
|
| 552 |
with solara.Column():
|
|
|
|
| 553 |
solara.Markdown('[Download Sample Dataset](https://drive.google.com/file/d/1BGPZQ2IKJHY9ExOCCHcNNrCTioYZ8D1y/view?usp=sharing)')
|
| 554 |
FileDropZone()
|
| 555 |
ExecutePanel()
|