Spaces:
Runtime error
Runtime error
Commit
·
0b69fd8
1
Parent(s):
0922c28
Added support for reading GEM XML format
Browse files- pyproject.toml +3 -1
- requirements.txt +2 -1
- tomorrowcities/components/article.py +2 -0
- tomorrowcities/content/articles/{data_formats.md → data.md} +57 -12
- tomorrowcities/content/articles/flood.md +6 -4
- tomorrowcities/content/articles/metrics.md +5 -3
- tomorrowcities/content/articles/policies.md +6 -4
- tomorrowcities/content/articles/power.md +5 -3
- tomorrowcities/content/articles/welcome.md +5 -2
- tomorrowcities/data.py +5 -1
- tomorrowcities/pages/docs.py +1 -0
- tomorrowcities/pages/engine.py +254 -90
pyproject.toml
CHANGED
|
@@ -19,7 +19,9 @@ dependencies = [
|
|
| 19 |
"pandas",
|
| 20 |
"networkx",
|
| 21 |
"openpyxl",
|
| 22 |
-
"rasterio"
|
|
|
|
|
|
|
| 23 |
]
|
| 24 |
|
| 25 |
[tool.hatch.version]
|
|
|
|
| 19 |
"pandas",
|
| 20 |
"networkx",
|
| 21 |
"openpyxl",
|
| 22 |
+
"rasterio",
|
| 23 |
+
"xml"
|
| 24 |
+
|
| 25 |
]
|
| 26 |
|
| 27 |
[tool.hatch.version]
|
requirements.txt
CHANGED
|
@@ -7,4 +7,5 @@ matplotlib
|
|
| 7 |
psycopg2-binary
|
| 8 |
scipy
|
| 9 |
pandas
|
| 10 |
-
rasterio
|
|
|
|
|
|
| 7 |
psycopg2-binary
|
| 8 |
scipy
|
| 9 |
pandas
|
| 10 |
+
rasterio
|
| 11 |
+
xml
|
tomorrowcities/components/article.py
CHANGED
|
@@ -7,6 +7,8 @@ from ..data import articles
|
|
| 7 |
def ArticleCard(name):
|
| 8 |
article = articles[name]
|
| 9 |
with rv.Card(max_width="400px") as main:
|
|
|
|
|
|
|
| 10 |
rv.CardTitle(children=[article.title])
|
| 11 |
with rv.CardText():
|
| 12 |
solara.Markdown(article.description)
|
|
|
|
| 7 |
def ArticleCard(name):
|
| 8 |
article = articles[name]
|
| 9 |
with rv.Card(max_width="400px") as main:
|
| 10 |
+
with solara.Link(f"/docs/{name}"):
|
| 11 |
+
rv.Img(height="250", src=article.image_url)
|
| 12 |
rv.CardTitle(children=[article.title])
|
| 13 |
with rv.CardText():
|
| 14 |
solara.Markdown(article.description)
|
tomorrowcities/content/articles/{data_formats.md → data.md}
RENAMED
|
@@ -2,6 +2,8 @@
|
|
| 2 |
author: huseyin.kaya
|
| 3 |
title: Data Formats
|
| 4 |
description: Description of the data formats used in web application
|
|
|
|
|
|
|
| 5 |
alt: "Data Formats"
|
| 6 |
createdAt: 2023-10-10
|
| 7 |
duration: 6 min read
|
|
@@ -9,13 +11,34 @@ category:
|
|
| 9 |
- general
|
| 10 |
---
|
| 11 |
|
|
|
|
|
|
|
| 12 |
## Data Structures
|
| 13 |
-
Tomorrow's Cities Decision Support Environment (TCDSE) is capable of conducting different hazard scenarios on different infrastructures, hence it needs to use several data input/output formats.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
|
| 15 |
-
|
| 16 |
-
|
| 17 |
|
| 18 |
-
*
|
| 19 |
* building
|
| 20 |
* household
|
| 21 |
* individual
|
|
@@ -25,14 +48,6 @@ In TCDSE, we generally use tabular data where each row corresponds to a unique o
|
|
| 25 |
* power nodes
|
| 26 |
* power edges
|
| 27 |
|
| 28 |
-
**Storage Format:** The tabular data can be stored in different formats such as Comma-Separeted Values or spreadsheets. If the data does not contain geographic coordinates or the coordinates are defined with longitude and latitude pairs, spreadsheet formats
|
| 29 |
-
|
| 30 |
-
In this way, building data can be joined with other type of data that we will mention in the coming section via relational databases.
|
| 31 |
-
|
| 32 |
-
### Format
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
## Layers
|
| 36 |
### Buildings
|
| 37 |
Buildings are the core component of visioning scenarios. The features of the building with some example data are shown below:
|
| 38 |
|
|
@@ -54,3 +69,33 @@ Whether it is flood, debris or earthquake, every hazard map should contain at le
|
|
| 54 |
information so that the engine could map the coordinates to a common CRS to conduct calculations.
|
| 55 |
|
| 56 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
author: huseyin.kaya
|
| 3 |
title: Data Formats
|
| 4 |
description: Description of the data formats used in web application
|
| 5 |
+
image: https://github.com/TomorrowsCities/tomorrowcities/blob/main/tomorrowcities/content/images/data.png?raw=true
|
| 6 |
+
thumbnail: https://github.com/TomorrowsCities/tomorrowcities/blob/main/tomorrowcities/content/images/data.png?raw=true
|
| 7 |
alt: "Data Formats"
|
| 8 |
createdAt: 2023-10-10
|
| 9 |
duration: 6 min read
|
|
|
|
| 11 |
- general
|
| 12 |
---
|
| 13 |
|
| 14 |
+
[TOC]
|
| 15 |
+
|
| 16 |
## Data Structures
|
| 17 |
+
Tomorrow's Cities Decision Support Environment (TCDSE) is capable of conducting different hazard scenarios on different infrastructures, hence it needs to use several layers with different data input/output formats.
|
| 18 |
+
|
| 19 |
+
## Storage Formats
|
| 20 |
+
**Tabular**
|
| 21 |
+
If the data does not contain geo-spatial information, then spreadsheet formats such as
|
| 22 |
+
Comma-Separated Values (*.csv) or Microsoft Excel (*.xlsx) can be used to store the data.
|
| 23 |
+
|
| 24 |
+
**GeoGSON**
|
| 25 |
+
The primary choice of the webapp to store geo-spatial information is GeoJSON for couple of reasons.
|
| 26 |
+
First, unlike SHP format, it doesn't require auxillary files so it is easy to transfer or load
|
| 27 |
+
a GeoJSON file. Secondly, it is supported by QGIS and geopandas Python libraries. Another advantage is
|
| 28 |
+
that every GeoJSON is a text file and you can always look at the content by any text editor.
|
| 29 |
+
|
| 30 |
+
**GeoTIFF**
|
| 31 |
+
When the intensity measures are given as a mesh of rectangular grid, then the number of points to store
|
| 32 |
+
can become very large. In those cases, the size of the GeoJSON files will be very large because GeoJSON is
|
| 33 |
+
a text file and there will be redundant information. It is possible to compress a GeoJSON but a better
|
| 34 |
+
approach is to use GeoTIFF files. GeoTIFF is useful because it is a raster file and moreover geo-spatial
|
| 35 |
+
context is already built-in in the format. Additionally GeoTIFF files are lot smaller than GeoJSON files.
|
| 36 |
+
Please note that GeoTIFF is only advised to represent intensity measures on a mesh grid.
|
| 37 |
|
| 38 |
+
## Layers
|
| 39 |
+
The layers supported by tomorrowcities are listed below. In this section, we will cover all of them and provide information so that the users are able to generate data compatible to web application.
|
| 40 |
|
| 41 |
+
* land use
|
| 42 |
* building
|
| 43 |
* household
|
| 44 |
* individual
|
|
|
|
| 48 |
* power nodes
|
| 49 |
* power edges
|
| 50 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
### Buildings
|
| 52 |
Buildings are the core component of visioning scenarios. The features of the building with some example data are shown below:
|
| 53 |
|
|
|
|
| 69 |
information so that the engine could map the coordinates to a common CRS to conduct calculations.
|
| 70 |
|
| 71 |
|
| 72 |
+
### Vulnerability
|
| 73 |
+
The engine has a built-in support for [Global Vulnerability Model (GVM)](https://github.com/gem/global_vulnerability_model) used in [Global Earthquake Model (GEM)](https://github.com/gem).
|
| 74 |
+
GVM uses an XML format in which one can define several vulnerability functons. There are almost a thousand XML files in [GVM repository](https://github.com/gem/global_vulnerability_model) precomputed for hundred cities from all continents. When a user uploads an XML file conforming to GVM, the engine can parse, display and use it in the calculations.
|
| 75 |
+
|
| 76 |
+
The GVM's XML format is very simple. For demonstration purposes, we provide a short but structurally-complete one below. Please note that the values do not represent a real vulnerability function:
|
| 77 |
+
|
| 78 |
+
~~~xml
|
| 79 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
| 80 |
+
<nrml xmlns="http://openquake.org/xmlns/nrml/0.5">
|
| 81 |
+
<vulnerabilityModel id="vulnerability_model"
|
| 82 |
+
assetCategory="buildings"
|
| 83 |
+
lossCategory="structural">
|
| 84 |
+
<description>Example vulnerability model</description>
|
| 85 |
+
|
| 86 |
+
<vulnerabilityFunction id="CR/LDUAL+CDL+DUM/H12/COM" dist="BT">
|
| 87 |
+
<imls imt="SA(1.0)">0.05 0.1 1.2 2.3 5.2 6.6 8.38 15</imls>
|
| 88 |
+
<meanLRs>1e-08 0.0001 0.002 0.06 0.14 0.19 0.6 0.99</meanLRs>
|
| 89 |
+
<covLRs>1e-08 1e-08 1e-08 1e-08 0.62 0.53 1e-08 1e-08 </covLRs>
|
| 90 |
+
</vulnerabilityFunction>
|
| 91 |
+
|
| 92 |
+
<vulnerabilityFunction id="MUR+CLBRS/LWAL+DNO/H1/RES" dist="BT">
|
| 93 |
+
<imls imt="SA(1.0)">0.04 0.2 1.2 2.3 5.2 6.4 9.38 15</imls>
|
| 94 |
+
<meanLRs>1e-08 0.001 0.003 0.06 0.15 0.18 0.7 0.99</meanLRs>
|
| 95 |
+
<covLRs>1e-08 1e-08 1e-08 1e-08 0.2 0.6 1e-08 1e-08 </covLRs>
|
| 96 |
+
</vulnerabilityFunction>
|
| 97 |
+
|
| 98 |
+
</vulnerabilityModel>
|
| 99 |
+
</nrml>
|
| 100 |
+
~~~
|
| 101 |
+
|
tomorrowcities/content/articles/flood.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
| 1 |
---
|
| 2 |
author: huseyin.kaya
|
| 3 |
-
title: Flood
|
| 4 |
-
description:
|
| 5 |
-
image: https://
|
| 6 |
-
thumbnail: https://
|
| 7 |
alt: "Flood vulnerability analysis"
|
| 8 |
createdAt: 2023-10-10
|
| 9 |
duration: 6 min read
|
|
@@ -11,6 +11,8 @@ category:
|
|
| 11 |
- general
|
| 12 |
---
|
| 13 |
|
|
|
|
|
|
|
| 14 |
The core component used in flood vulnerability analysis is to generate a mapping between water levels and relative damage on the structure. The shape of the curve depends on the typology of the structure, material type, height, code levels and many other features.
|
| 15 |
|
| 16 |
The Joint Research Centre (JRC) led by the European Commission provides technical reports and guidelines to generate damage curves [1]. It is also always possible to make minor modification for special cases such as single storey adobe buildings in Africa [2].
|
|
|
|
| 1 |
---
|
| 2 |
author: huseyin.kaya
|
| 3 |
+
title: Flood
|
| 4 |
+
description: How to define flood vulnerabiities and conduct damage assessment
|
| 5 |
+
image: https://github.com/TomorrowsCities/tomorrowcities/blob/main/tomorrowcities/content/images/flood.png?raw=true
|
| 6 |
+
thumbnail: https://github.com/TomorrowsCities/tomorrowcities/blob/main/tomorrowcities/content/images/flood.png?raw=true
|
| 7 |
alt: "Flood vulnerability analysis"
|
| 8 |
createdAt: 2023-10-10
|
| 9 |
duration: 6 min read
|
|
|
|
| 11 |
- general
|
| 12 |
---
|
| 13 |
|
| 14 |
+
[TOC]
|
| 15 |
+
|
| 16 |
The core component used in flood vulnerability analysis is to generate a mapping between water levels and relative damage on the structure. The shape of the curve depends on the typology of the structure, material type, height, code levels and many other features.
|
| 17 |
|
| 18 |
The Joint Research Centre (JRC) led by the European Commission provides technical reports and guidelines to generate damage curves [1]. It is also always possible to make minor modification for special cases such as single storey adobe buildings in Africa [2].
|
tomorrowcities/content/articles/metrics.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
| 1 |
---
|
| 2 |
author: huseyin.kaya
|
| 3 |
title: Metrics
|
| 4 |
-
description: A brief introduction to metrics
|
| 5 |
-
image: https://
|
| 6 |
-
thumbnail: https://
|
| 7 |
alt: "Metrics"
|
| 8 |
createdAt: 2023-10-10
|
| 9 |
duration: 6 min read
|
|
@@ -11,6 +11,8 @@ 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 |
|
|
|
|
| 1 |
---
|
| 2 |
author: huseyin.kaya
|
| 3 |
title: Metrics
|
| 4 |
+
description: A brief introduction to impact metrics and implementation strategies
|
| 5 |
+
image: https://github.com/TomorrowsCities/tomorrowcities/blob/main/tomorrowcities/content/images/metrics.jpg?raw=true
|
| 6 |
+
thumbnail: https://github.com/TomorrowsCities/tomorrowcities/blob/main/tomorrowcities/content/images/metrics.jpg?raw=true
|
| 7 |
alt: "Metrics"
|
| 8 |
createdAt: 2023-10-10
|
| 9 |
duration: 6 min read
|
|
|
|
| 11 |
- general
|
| 12 |
---
|
| 13 |
|
| 14 |
+
[TOC]
|
| 15 |
+
|
| 16 |
## Metrics
|
| 17 |
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.
|
| 18 |
|
tomorrowcities/content/articles/policies.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
| 1 |
---
|
| 2 |
author: huseyin.kaya
|
| 3 |
-
title: Policies
|
| 4 |
-
description:
|
| 5 |
-
image: https://
|
| 6 |
-
thumbnail: https://
|
| 7 |
alt: "Policies"
|
| 8 |
createdAt: 2023-10-10
|
| 9 |
duration: 6 min read
|
|
@@ -11,6 +11,8 @@ 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
|
|
|
|
| 1 |
---
|
| 2 |
author: huseyin.kaya
|
| 3 |
+
title: Policies
|
| 4 |
+
description: How to describe policies and their effect on impact metrics
|
| 5 |
+
image: https://github.com/TomorrowsCities/tomorrowcities/blob/main/tomorrowcities/content/images/policies.png?raw=true
|
| 6 |
+
thumbnail: https://github.com/TomorrowsCities/tomorrowcities/blob/main/tomorrowcities/content/images/policies.png?raw=true
|
| 7 |
alt: "Policies"
|
| 8 |
createdAt: 2023-10-10
|
| 9 |
duration: 6 min read
|
|
|
|
| 11 |
- general
|
| 12 |
---
|
| 13 |
|
| 14 |
+
[TOC]
|
| 15 |
+
|
| 16 |
### Policy 1: Land and tenure security program
|
| 17 |
When this policy applied, the code levels of the residential buildings are set to higher standards
|
| 18 |
which effectively reduces the damage states during hazard simulations. Since this policy only changes
|
tomorrowcities/content/articles/power.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
| 2 |
author: huseyin.kaya
|
| 3 |
title: Power Network Analysis
|
| 4 |
description: How to conduct power network analysis in Tomorrowville
|
| 5 |
-
image: https://
|
| 6 |
-
thumbnail: https://
|
| 7 |
alt: "Power Network Analysis"
|
| 8 |
createdAt: 2023-10-10
|
| 9 |
duration: 6 min read
|
|
@@ -11,4 +11,6 @@ category:
|
|
| 11 |
- general
|
| 12 |
---
|
| 13 |
|
| 14 |
-
|
|
|
|
|
|
|
|
|
| 2 |
author: huseyin.kaya
|
| 3 |
title: Power Network Analysis
|
| 4 |
description: How to conduct power network analysis in Tomorrowville
|
| 5 |
+
image: https://github.com/TomorrowsCities/tomorrowcities/blob/main/tomorrowcities/content/images/power.png?raw=true
|
| 6 |
+
thumbnail: https://github.com/TomorrowsCities/tomorrowcities/blob/main/tomorrowcities/content/images/power.png?raw=true
|
| 7 |
alt: "Power Network Analysis"
|
| 8 |
createdAt: 2023-10-10
|
| 9 |
duration: 6 min read
|
|
|
|
| 11 |
- general
|
| 12 |
---
|
| 13 |
|
| 14 |
+
[TOC]
|
| 15 |
+
|
| 16 |
+
## Power Infrastructure Analysis
|
tomorrowcities/content/articles/welcome.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
| 2 |
author: huseyin.kaya
|
| 3 |
title: Welcome!
|
| 4 |
description: A Brief Introduction to Tomorrow's Cities Decision Support Environment (TCDSE)
|
| 5 |
-
image: https://
|
| 6 |
-
thumbnail: https://
|
| 7 |
alt: "Welcome!"
|
| 8 |
createdAt: 2023-10-10
|
| 9 |
duration: 6 min read
|
|
@@ -11,10 +11,13 @@ category:
|
|
| 11 |
- general
|
| 12 |
---
|
| 13 |
|
|
|
|
|
|
|
| 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 |
* basemap is changed to ESri.WorldImagery to see the landscapes especially rivers.
|
| 19 |
* utilities page is added.
|
| 20 |
* Excel to GeoJSON converted is added to utilities page.
|
|
|
|
| 2 |
author: huseyin.kaya
|
| 3 |
title: Welcome!
|
| 4 |
description: A Brief Introduction to Tomorrow's Cities Decision Support Environment (TCDSE)
|
| 5 |
+
image: https://github.com/TomorrowsCities/tomorrowcities/blob/main/tomorrowcities/content/images/welcome.jpg?raw=true
|
| 6 |
+
thumbnail: https://github.com/TomorrowsCities/tomorrowcities/blob/main/tomorrowcities/content/images/welcome.jpg?raw=true
|
| 7 |
alt: "Welcome!"
|
| 8 |
createdAt: 2023-10-10
|
| 9 |
duration: 6 min read
|
|
|
|
| 11 |
- general
|
| 12 |
---
|
| 13 |
|
| 14 |
+
[TOC]
|
| 15 |
+
|
| 16 |
## Tomorrow's Cities Decision Support Environment (TCDSE)
|
| 17 |
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.
|
| 18 |
|
| 19 |
## What is New?
|
| 20 |
+
* **New** The engine now can parse, display vulnerability curves located on [Global Vulnerability Model Reposity](https://github.com/gem/global_vulnerability_model) maintained by [Global Earthquake Model Foundation)](https://www.globalquakemodel.org/gem). To see the new fatures, download one of the XML files in [Global Vulnerability Model Reposity](https://github.com/gem/global_vulnerability_model) and drag and drop to [Engine](/engine). The engine will read all the vulnerability functions defined in the XML file and display them.
|
| 21 |
* basemap is changed to ESri.WorldImagery to see the landscapes especially rivers.
|
| 22 |
* utilities page is added.
|
| 23 |
* Excel to GeoJSON converted is added to utilities page.
|
tomorrowcities/data.py
CHANGED
|
@@ -14,6 +14,7 @@ class Article:
|
|
| 14 |
markdown: str
|
| 15 |
title: str
|
| 16 |
description: str
|
|
|
|
| 17 |
|
| 18 |
|
| 19 |
articles: Dict[str, Article] = {}
|
|
@@ -26,5 +27,8 @@ for file in (HERE.parent / "content/articles").glob("*.md"):
|
|
| 26 |
yamltext = "\n".join(lines[frontmatter_start + 1 : frontmatter_end - 2])
|
| 27 |
metadata = yaml.safe_load(yamltext)
|
| 28 |
markdown = "\n".join(lines[frontmatter_end + 1 :])
|
| 29 |
-
articles[file.stem] = Article(markdown=markdown,
|
|
|
|
|
|
|
|
|
|
| 30 |
|
|
|
|
| 14 |
markdown: str
|
| 15 |
title: str
|
| 16 |
description: str
|
| 17 |
+
image_url: str
|
| 18 |
|
| 19 |
|
| 20 |
articles: Dict[str, Article] = {}
|
|
|
|
| 27 |
yamltext = "\n".join(lines[frontmatter_start + 1 : frontmatter_end - 2])
|
| 28 |
metadata = yaml.safe_load(yamltext)
|
| 29 |
markdown = "\n".join(lines[frontmatter_end + 1 :])
|
| 30 |
+
articles[file.stem] = Article(markdown=markdown,
|
| 31 |
+
title=metadata["title"],
|
| 32 |
+
description=metadata["description"],
|
| 33 |
+
image_url=metadata["image"])
|
| 34 |
|
tomorrowcities/pages/docs.py
CHANGED
|
@@ -12,6 +12,7 @@ def Page(name: Optional[str] = None, page: int = 0, page_size=100):
|
|
| 12 |
with solara.Column() as main:
|
| 13 |
solara.Title("TCDSE» Documentation")
|
| 14 |
Overview()
|
|
|
|
| 15 |
return main
|
| 16 |
if name not in data.articles:
|
| 17 |
return solara.Error(f"No such article: {name!r}")
|
|
|
|
| 12 |
with solara.Column() as main:
|
| 13 |
solara.Title("TCDSE» Documentation")
|
| 14 |
Overview()
|
| 15 |
+
solara.Text('Images by rawpixel.com')
|
| 16 |
return main
|
| 17 |
if name not in data.articles:
|
| 18 |
return solara.Error(f"No such article: {name!r}")
|
tomorrowcities/pages/engine.py
CHANGED
|
@@ -13,6 +13,7 @@ import numpy as np
|
|
| 13 |
import rasterio
|
| 14 |
from rasterio.warp import calculate_default_transform, reproject, Resampling
|
| 15 |
import io
|
|
|
|
| 16 |
import logging, sys
|
| 17 |
#logging.basicConfig(stream=sys.stderr, level=logging.INFO)
|
| 18 |
|
|
@@ -24,107 +25,118 @@ layers = solara.reactive({
|
|
| 24 |
'building': {
|
| 25 |
'render_order': 50,
|
| 26 |
'map_info_tooltip': 'Number of buildings',
|
| 27 |
-
'
|
| 28 |
'map_layer': solara.reactive(None),
|
| 29 |
'force_render': solara.reactive(False),
|
| 30 |
'visible': solara.reactive(False),
|
| 31 |
'extra_cols': {'ds': 0, 'metric1': 0, 'metric2': 0, 'metric3': 0,'metric4': 0, 'metric5': 0,'metric6': 0,'metric7': 0},
|
| 32 |
-
'
|
| 33 |
-
'
|
| 34 |
'landuse': {
|
| 35 |
'render_order': 20,
|
| 36 |
'map_info_tooltip': 'Number of landuse zones',
|
| 37 |
-
'
|
| 38 |
'map_layer': solara.reactive(None),
|
| 39 |
'force_render': solara.reactive(False),
|
| 40 |
'visible': solara.reactive(False),
|
| 41 |
'extra_cols': {},
|
| 42 |
-
'
|
| 43 |
-
'
|
| 44 |
'household': {
|
| 45 |
'render_order': 0,
|
| 46 |
-
'
|
| 47 |
'map_info_tooltip': 'Number of households',
|
| 48 |
'map_layer': solara.reactive(None),
|
| 49 |
'force_render': solara.reactive(False),
|
| 50 |
'visible': solara.reactive(False),
|
| 51 |
'extra_cols': {},
|
| 52 |
-
'
|
| 53 |
-
'
|
| 54 |
'individual': {
|
| 55 |
'render_order': 0,
|
| 56 |
-
'
|
| 57 |
'map_info_tooltip': 'Number of individuals',
|
| 58 |
'map_layer': solara.reactive(None),
|
| 59 |
'force_render': solara.reactive(False),
|
| 60 |
'visible': solara.reactive(False),
|
| 61 |
'extra_cols': {},
|
| 62 |
-
'
|
| 63 |
-
'
|
| 64 |
'intensity': {
|
| 65 |
'render_order': 0,
|
| 66 |
-
'
|
| 67 |
'map_info_tooltip': 'Number of intensity measurements',
|
| 68 |
'map_layer': solara.reactive(None),
|
| 69 |
'force_render': solara.reactive(False),
|
| 70 |
'visible': solara.reactive(False),
|
| 71 |
'extra_cols': {},
|
| 72 |
-
'
|
| 73 |
-
'
|
| 74 |
'fragility': {
|
| 75 |
'render_order': 0,
|
| 76 |
-
'
|
| 77 |
'map_info_tooltip': 'Number of records in fragility configuration',
|
| 78 |
'map_layer': solara.reactive(None),
|
| 79 |
'force_render': solara.reactive(False),
|
| 80 |
'visible': solara.reactive(False),
|
| 81 |
'extra_cols': {},
|
| 82 |
-
'
|
| 83 |
-
'
|
| 84 |
'vulnerability': {
|
| 85 |
'render_order': 0,
|
| 86 |
-
'
|
| 87 |
'map_info_tooltip': 'Number of records in vulnerabilty configuration',
|
| 88 |
'map_layer': solara.reactive(None),
|
| 89 |
'force_render': solara.reactive(False),
|
| 90 |
'visible': solara.reactive(False),
|
| 91 |
'extra_cols': {},
|
| 92 |
-
'
|
| 93 |
-
'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
'power nodes': {
|
| 95 |
'render_order': 90,
|
| 96 |
-
'
|
| 97 |
'map_info_tooltip': 'Number of electrical power nodes',
|
| 98 |
'map_layer': solara.reactive(None),
|
| 99 |
'force_render': solara.reactive(False),
|
| 100 |
'visible': solara.reactive(False),
|
| 101 |
'extra_cols': {'ds': 0, 'is_damaged': False, 'is_operational': True},
|
| 102 |
-
'
|
| 103 |
-
'
|
| 104 |
'node_id', 'x_coord', 'y_coord', 'pwr_plant', 'serv_area', 'n_bldgs',
|
| 105 |
'income', 'eq_vuln'])},
|
| 106 |
'power edges': {
|
| 107 |
'render_order': 80,
|
| 108 |
-
'
|
| 109 |
'map_info_tooltip': 'Number of connections in power grid',
|
| 110 |
'map_layer': solara.reactive(None),
|
| 111 |
'force_render': solara.reactive(False),
|
| 112 |
'visible': solara.reactive(False),
|
| 113 |
'extra_cols': {},
|
| 114 |
-
'
|
| 115 |
-
'
|
| 116 |
'geometry', 'to_node', 'length'])},
|
| 117 |
'power fragility': {
|
| 118 |
'render_order': 0,
|
| 119 |
-
'
|
| 120 |
'map_info_tooltip': 'Number of records in fragility configuration for power',
|
| 121 |
'map_layer': solara.reactive(None),
|
| 122 |
'force_render': solara.reactive(False),
|
| 123 |
'visible': solara.reactive(False),
|
| 124 |
'extra_cols': {},
|
| 125 |
-
'
|
| 126 |
'beta_slight', 'beta_moderate', 'beta_extensive', 'beta_complete']),
|
| 127 |
-
'
|
| 128 |
'beta_slight', 'beta_moderate', 'beta_extensive', 'beta_complete', 'description'])}
|
| 129 |
},
|
| 130 |
'center': solara.reactive((41.01,28.98)),
|
|
@@ -350,6 +362,138 @@ def read_tiff(file_bytes):
|
|
| 350 |
return gdf[gdf['im'] > 0]
|
| 351 |
#return gdf.sort_values(by='im',ascending=False).head(10000)
|
| 352 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 353 |
@solara.component
|
| 354 |
def MetricWidget(name, description, value, max_value, render_count):
|
| 355 |
value, set_value = solara.use_state_or_update(value)
|
|
@@ -386,42 +530,51 @@ def MetricWidget(name, description, value, max_value, render_count):
|
|
| 386 |
|
| 387 |
|
| 388 |
def import_data(fileinfo: solara.components.file_drop.FileInfo):
|
| 389 |
-
|
| 390 |
extension = fileinfo['name'].split('.')[-1]
|
| 391 |
if extension == 'xlsx':
|
| 392 |
-
|
| 393 |
elif extension in ['tiff','tif']:
|
| 394 |
-
|
|
|
|
|
|
|
| 395 |
else:
|
| 396 |
-
json_string =
|
| 397 |
json_data = json.loads(json_string)
|
| 398 |
if "features" in json_data.keys():
|
| 399 |
-
|
| 400 |
else:
|
| 401 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 402 |
|
| 403 |
-
df.columns = df.columns.str.lower()
|
| 404 |
|
| 405 |
# in the first pass, look for exact column match
|
| 406 |
name = None
|
| 407 |
for layer_name, layer in layers.value['layers'].items():
|
| 408 |
-
if layer['
|
| 409 |
name = layer_name
|
| 410 |
break
|
| 411 |
# if not, check only the required columns
|
| 412 |
if name is None:
|
| 413 |
for layer_name, layer in layers.value['layers'].items():
|
| 414 |
-
if layer['
|
| 415 |
name = layer_name
|
| 416 |
-
logging.debug('There are extra columns',
|
| 417 |
break
|
| 418 |
|
| 419 |
|
| 420 |
# Inject columns
|
| 421 |
-
if name is not None:
|
| 422 |
for col, val in layers.value['layers'][name]['extra_cols'].items():
|
| 423 |
-
|
| 424 |
-
return (name,
|
| 425 |
|
| 426 |
|
| 427 |
@solara.component
|
|
@@ -433,15 +586,21 @@ def FileDropZone():
|
|
| 433 |
def load():
|
| 434 |
if fileinfo is not None:
|
| 435 |
print('processing file')
|
| 436 |
-
name,
|
| 437 |
-
if name is not None and
|
| 438 |
-
|
| 439 |
-
|
| 440 |
-
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 445 |
else:
|
| 446 |
return False
|
| 447 |
return True
|
|
@@ -480,7 +639,7 @@ def FileDropZone():
|
|
| 480 |
@solara.component
|
| 481 |
def LayerDisplayer():
|
| 482 |
print(f'{layers.value["bounds"].value}')
|
| 483 |
-
nonempty_layers = {name: layer for name, layer in layers.value['layers'].items() if layer['
|
| 484 |
nonempty_layer_names = list(nonempty_layers.keys())
|
| 485 |
selected = layers.value['selected_layer'].value
|
| 486 |
def set_selected(s):
|
|
@@ -491,22 +650,24 @@ def LayerDisplayer():
|
|
| 491 |
if selected is None and len(nonempty_layer_names) > 0:
|
| 492 |
set_selected(nonempty_layer_names[0])
|
| 493 |
if selected is not None:
|
| 494 |
-
|
| 495 |
-
if
|
| 496 |
-
|
| 497 |
-
|
| 498 |
-
|
| 499 |
-
|
| 500 |
-
|
| 501 |
-
|
| 502 |
-
|
| 503 |
-
|
| 504 |
-
solara.
|
| 505 |
-
|
|
|
|
|
|
|
| 506 |
|
| 507 |
@solara.component
|
| 508 |
def MetricPanel():
|
| 509 |
-
building = layers.value['layers']['building']['
|
| 510 |
filtered_metrics = {name: 0 for name in layers.value['metrics'].keys()}
|
| 511 |
if building is not None and layers.value['bounds'].value is not None:
|
| 512 |
((ymin,xmin),(ymax,xmax)) = layers.value['bounds'].value
|
|
@@ -547,14 +708,13 @@ def MapViewer():
|
|
| 547 |
|
| 548 |
render_order = [l['render_order'] for _, l in layers.value['layers'].items()]
|
| 549 |
for _, (layer_name, layer) in sorted(zip(render_order,layers.value['layers'].items())):
|
| 550 |
-
df = layer['
|
| 551 |
-
if df
|
| 552 |
-
|
| 553 |
-
|
| 554 |
-
|
| 555 |
-
|
| 556 |
-
|
| 557 |
-
map_layers.append(map_layer)
|
| 558 |
|
| 559 |
|
| 560 |
ipyleaflet.Map.element(
|
|
@@ -587,7 +747,7 @@ def ExecutePanel():
|
|
| 587 |
execute_error.set("")
|
| 588 |
|
| 589 |
def is_ready_to_run(infra, hazard):
|
| 590 |
-
existing_layers = set([name for name, l in layers.value['layers'].items() if l['
|
| 591 |
missing = []
|
| 592 |
|
| 593 |
if hazard == "earthquake":
|
|
@@ -611,10 +771,10 @@ def ExecutePanel():
|
|
| 611 |
|
| 612 |
|
| 613 |
def execute_infra():
|
| 614 |
-
nodes = layers.value['layers']['power nodes']['
|
| 615 |
-
edges = layers.value['layers']['power edges']['
|
| 616 |
-
intensity = layers.value['layers']['intensity']['
|
| 617 |
-
power_fragility = layers.value['layers']['power fragility']['
|
| 618 |
|
| 619 |
|
| 620 |
eq_ds, is_damaged, is_operational = compute_power_infra(nodes,
|
|
@@ -629,14 +789,14 @@ def ExecutePanel():
|
|
| 629 |
return nodes
|
| 630 |
|
| 631 |
def execute_building():
|
| 632 |
-
landuse = layers.value['layers']['landuse']['
|
| 633 |
-
buildings = layers.value['layers']['building']['
|
| 634 |
-
household = layers.value['layers']['household']['
|
| 635 |
-
individual = layers.value['layers']['individual']['
|
| 636 |
-
intensity = layers.value['layers']['intensity']['
|
| 637 |
|
| 638 |
-
fragility = layers.value['layers']['fragility']['
|
| 639 |
-
vulnerability = layers.value['layers']['vulnerability']['
|
| 640 |
|
| 641 |
policies = [p['id'] for id, p in layers.value['policies'].items() if p['applied'].value]
|
| 642 |
|
|
@@ -676,10 +836,10 @@ def ExecutePanel():
|
|
| 676 |
|
| 677 |
if 'power' in infra:
|
| 678 |
nodes = execute_infra()
|
| 679 |
-
layers.value['layers']['power nodes']['
|
| 680 |
if 'building' in infra:
|
| 681 |
buildings = execute_building()
|
| 682 |
-
layers.value['layers']['building']['
|
| 683 |
|
| 684 |
# trigger render event
|
| 685 |
layers.value['render_count'].set(layers.value['render_count'].value + 1)
|
|
@@ -751,13 +911,17 @@ def MapInfo():
|
|
| 751 |
if layers.value['map_info_button'].value == "summary":
|
| 752 |
with solara.GridFixed(columns=2,row_gap="1px"):
|
| 753 |
for layer_name,layer in layers.value['layers'].items():
|
|
|
|
| 754 |
with solara.Tooltip(layer['map_info_tooltip']):
|
| 755 |
solara.Text(f'{layer_name}')
|
| 756 |
with solara.Row(justify="right"):
|
| 757 |
-
if
|
| 758 |
solara.Text('0')
|
| 759 |
else:
|
| 760 |
-
|
|
|
|
|
|
|
|
|
|
| 761 |
else:
|
| 762 |
with solara.GridFixed(columns=2,row_gap="1px"):
|
| 763 |
for key, value in layers.value['map_info_detail'].value.items():
|
|
|
|
| 13 |
import rasterio
|
| 14 |
from rasterio.warp import calculate_default_transform, reproject, Resampling
|
| 15 |
import io
|
| 16 |
+
import xml
|
| 17 |
import logging, sys
|
| 18 |
#logging.basicConfig(stream=sys.stderr, level=logging.INFO)
|
| 19 |
|
|
|
|
| 25 |
'building': {
|
| 26 |
'render_order': 50,
|
| 27 |
'map_info_tooltip': 'Number of buildings',
|
| 28 |
+
'data': solara.reactive(None),
|
| 29 |
'map_layer': solara.reactive(None),
|
| 30 |
'force_render': solara.reactive(False),
|
| 31 |
'visible': solara.reactive(False),
|
| 32 |
'extra_cols': {'ds': 0, 'metric1': 0, 'metric2': 0, 'metric3': 0,'metric4': 0, 'metric5': 0,'metric6': 0,'metric7': 0},
|
| 33 |
+
'attributes_required': set(['residents', 'fptarea', 'repvalue', 'nhouse', 'zoneid', 'expstr', 'bldid', 'geometry', 'specialfac']),
|
| 34 |
+
'attributes': set(['residents', 'fptarea', 'repvalue', 'nhouse', 'zoneid', 'expstr', 'bldid', 'geometry', 'specialfac'])},
|
| 35 |
'landuse': {
|
| 36 |
'render_order': 20,
|
| 37 |
'map_info_tooltip': 'Number of landuse zones',
|
| 38 |
+
'data': solara.reactive(None),
|
| 39 |
'map_layer': solara.reactive(None),
|
| 40 |
'force_render': solara.reactive(False),
|
| 41 |
'visible': solara.reactive(False),
|
| 42 |
'extra_cols': {},
|
| 43 |
+
'attributes_required': set(['geometry', 'zoneid', 'luf', 'population', 'densitycap', 'avgincome']),
|
| 44 |
+
'attributes': set(['geometry', 'zoneid', 'luf', 'population', 'densitycap', 'floorarat', 'setback', 'avgincome'])},
|
| 45 |
'household': {
|
| 46 |
'render_order': 0,
|
| 47 |
+
'data': solara.reactive(None),
|
| 48 |
'map_info_tooltip': 'Number of households',
|
| 49 |
'map_layer': solara.reactive(None),
|
| 50 |
'force_render': solara.reactive(False),
|
| 51 |
'visible': solara.reactive(False),
|
| 52 |
'extra_cols': {},
|
| 53 |
+
'attributes_required':set(['hhid', 'nind', 'income', 'bldid', 'commfacid']),
|
| 54 |
+
'attributes':set(['hhid', 'nind', 'income', 'bldid', 'commfacid'])},
|
| 55 |
'individual': {
|
| 56 |
'render_order': 0,
|
| 57 |
+
'data': solara.reactive(None),
|
| 58 |
'map_info_tooltip': 'Number of individuals',
|
| 59 |
'map_layer': solara.reactive(None),
|
| 60 |
'force_render': solara.reactive(False),
|
| 61 |
'visible': solara.reactive(False),
|
| 62 |
'extra_cols': {},
|
| 63 |
+
'attributes_required': set(['individ', 'hhid', 'gender', 'age', 'eduattstat', 'head', 'indivfacid']),
|
| 64 |
+
'attributes': set(['individ', 'hhid', 'gender', 'age', 'eduattstat', 'head', 'indivfacid'])},
|
| 65 |
'intensity': {
|
| 66 |
'render_order': 0,
|
| 67 |
+
'data': solara.reactive(None),
|
| 68 |
'map_info_tooltip': 'Number of intensity measurements',
|
| 69 |
'map_layer': solara.reactive(None),
|
| 70 |
'force_render': solara.reactive(False),
|
| 71 |
'visible': solara.reactive(False),
|
| 72 |
'extra_cols': {},
|
| 73 |
+
'attributes_required': set(['geometry','im']),
|
| 74 |
+
'attributes': set(['geometry','im'])},
|
| 75 |
'fragility': {
|
| 76 |
'render_order': 0,
|
| 77 |
+
'data': solara.reactive(None),
|
| 78 |
'map_info_tooltip': 'Number of records in fragility configuration',
|
| 79 |
'map_layer': solara.reactive(None),
|
| 80 |
'force_render': solara.reactive(False),
|
| 81 |
'visible': solara.reactive(False),
|
| 82 |
'extra_cols': {},
|
| 83 |
+
'attributes_required': set(['expstr','muds1_g','muds2_g','muds3_g','muds4_g','sigmads1','sigmads2','sigmads3','sigmads4']),
|
| 84 |
+
'attributes': set(['expstr','muds1_g','muds2_g','muds3_g','muds4_g','sigmads1','sigmads2','sigmads3','sigmads4'])},
|
| 85 |
'vulnerability': {
|
| 86 |
'render_order': 0,
|
| 87 |
+
'data': solara.reactive(None),
|
| 88 |
'map_info_tooltip': 'Number of records in vulnerabilty configuration',
|
| 89 |
'map_layer': solara.reactive(None),
|
| 90 |
'force_render': solara.reactive(False),
|
| 91 |
'visible': solara.reactive(False),
|
| 92 |
'extra_cols': {},
|
| 93 |
+
'attributes_required': set(['expstr', 'hw0', 'hw0_5', 'hw1', 'hw1_5', 'hw2', 'hw3', 'hw4', 'hw5','hw6']),
|
| 94 |
+
'attributes': set(['expstr', 'hw0', 'hw0_5', 'hw1', 'hw1_5', 'hw2', 'hw3', 'hw4', 'hw5','hw6'])},
|
| 95 |
+
'gem_vulnerability': {
|
| 96 |
+
'render_order': 0,
|
| 97 |
+
'data': solara.reactive(None),
|
| 98 |
+
'map_info_tooltip': 'Number of functions in gem vulnerabilty',
|
| 99 |
+
'map_layer': solara.reactive(None),
|
| 100 |
+
'force_render': solara.reactive(False),
|
| 101 |
+
'visible': solara.reactive(False),
|
| 102 |
+
'extra_cols': {},
|
| 103 |
+
'attributes_required': set(['id', 'assetCategory', 'lossCategory', 'description', 'vulnerabilityFunctions']),
|
| 104 |
+
'attributes': set(['id', 'assetCategory', 'lossCategory', 'description', 'vulnerabilityFunctions']),
|
| 105 |
+
},
|
| 106 |
'power nodes': {
|
| 107 |
'render_order': 90,
|
| 108 |
+
'data': solara.reactive(None),
|
| 109 |
'map_info_tooltip': 'Number of electrical power nodes',
|
| 110 |
'map_layer': solara.reactive(None),
|
| 111 |
'force_render': solara.reactive(False),
|
| 112 |
'visible': solara.reactive(False),
|
| 113 |
'extra_cols': {'ds': 0, 'is_damaged': False, 'is_operational': True},
|
| 114 |
+
'attributes_required': set(['geometry', 'node_id', 'pwr_plant', 'n_bldgs', 'eq_vuln']),
|
| 115 |
+
'attributes': set(['geometry', 'fltytype', 'strctype', 'utilfcltyc', 'indpnode', 'guid',
|
| 116 |
'node_id', 'x_coord', 'y_coord', 'pwr_plant', 'serv_area', 'n_bldgs',
|
| 117 |
'income', 'eq_vuln'])},
|
| 118 |
'power edges': {
|
| 119 |
'render_order': 80,
|
| 120 |
+
'data': solara.reactive(None),
|
| 121 |
'map_info_tooltip': 'Number of connections in power grid',
|
| 122 |
'map_layer': solara.reactive(None),
|
| 123 |
'force_render': solara.reactive(False),
|
| 124 |
'visible': solara.reactive(False),
|
| 125 |
'extra_cols': {},
|
| 126 |
+
'attributes_required': set(['geometry','from_node','to_node', 'edge_id']),
|
| 127 |
+
'attributes': set(['from_node', 'direction', 'pipetype', 'edge_id', 'guid', 'capacity',
|
| 128 |
'geometry', 'to_node', 'length'])},
|
| 129 |
'power fragility': {
|
| 130 |
'render_order': 0,
|
| 131 |
+
'data': solara.reactive(None),
|
| 132 |
'map_info_tooltip': 'Number of records in fragility configuration for power',
|
| 133 |
'map_layer': solara.reactive(None),
|
| 134 |
'force_render': solara.reactive(False),
|
| 135 |
'visible': solara.reactive(False),
|
| 136 |
'extra_cols': {},
|
| 137 |
+
'attributes_required': set(['vuln_string', 'med_slight', 'med_moderate', 'med_extensive', 'med_complete',
|
| 138 |
'beta_slight', 'beta_moderate', 'beta_extensive', 'beta_complete']),
|
| 139 |
+
'attributes': set(['vuln_string', 'med_slight', 'med_moderate', 'med_extensive', 'med_complete',
|
| 140 |
'beta_slight', 'beta_moderate', 'beta_extensive', 'beta_complete', 'description'])}
|
| 141 |
},
|
| 142 |
'center': solara.reactive((41.01,28.98)),
|
|
|
|
| 362 |
return gdf[gdf['im'] > 0]
|
| 363 |
#return gdf.sort_values(by='im',ascending=False).head(10000)
|
| 364 |
|
| 365 |
+
|
| 366 |
+
def read_gem_xml(data: [bytes]):
|
| 367 |
+
content_as_string = data.decode('utf-8')
|
| 368 |
+
content_as_string = content_as_string.replace('\n','')
|
| 369 |
+
dom = xml.dom.minidom.parseString(content_as_string)
|
| 370 |
+
|
| 371 |
+
def getText(node):
|
| 372 |
+
nodelist = node.childNodes
|
| 373 |
+
rc = []
|
| 374 |
+
for node in nodelist:
|
| 375 |
+
if node.nodeType == node.TEXT_NODE:
|
| 376 |
+
rc.append(node.data)
|
| 377 |
+
return ''.join(rc)
|
| 378 |
+
|
| 379 |
+
d = dict()
|
| 380 |
+
node = dom.getElementsByTagName('vulnerabilityModel')[0]
|
| 381 |
+
for i in range(node.attributes.length):
|
| 382 |
+
d[node.attributes.item(i).name] = node.attributes.item(i).value
|
| 383 |
+
|
| 384 |
+
d['description'] = getText(dom.getElementsByTagName('description')[0])
|
| 385 |
+
|
| 386 |
+
|
| 387 |
+
d['vulnerabilityFunctions'] = []
|
| 388 |
+
for node in dom.getElementsByTagName('vulnerabilityFunction'):
|
| 389 |
+
v = dict()
|
| 390 |
+
for i in range(node.attributes.length):
|
| 391 |
+
v[node.attributes.item(i).name] = node.attributes.item(i).value
|
| 392 |
+
imls = node.getElementsByTagName('imls')[0]
|
| 393 |
+
v['imt'] = imls.getAttribute('imt')
|
| 394 |
+
v['imls'] = np.fromstring(getText(imls),dtype=float, sep=' ')
|
| 395 |
+
v['meanLRs'] = np.fromstring(getText(node.getElementsByTagName('meanLRs')[0]),dtype=float, sep=' ')
|
| 396 |
+
v['covLRs'] = np.fromstring(getText(node.getElementsByTagName('covLRs')[0]),dtype=float, sep=' ')
|
| 397 |
+
d['vulnerabilityFunctions'].append(v)
|
| 398 |
+
|
| 399 |
+
return d
|
| 400 |
+
|
| 401 |
+
|
| 402 |
+
@solara.component
|
| 403 |
+
def VulnerabilityFunctionDisplayer(vuln_func):
|
| 404 |
+
vuln_func, _ = solara.use_state_or_update(vuln_func)
|
| 405 |
+
|
| 406 |
+
x = vuln_func['imls']
|
| 407 |
+
y = vuln_func['meanLRs']
|
| 408 |
+
s = vuln_func['covLRs']
|
| 409 |
+
xlabel = vuln_func['imt']
|
| 410 |
+
|
| 411 |
+
options = {
|
| 412 |
+
'title': {
|
| 413 |
+
'text': vuln_func['id'],
|
| 414 |
+
'left': 'center'},
|
| 415 |
+
'tooltip': {
|
| 416 |
+
'trigger': 'axis',
|
| 417 |
+
'axisPointer': {
|
| 418 |
+
'type': 'cross'
|
| 419 |
+
}
|
| 420 |
+
},
|
| 421 |
+
#'legend': {'data': ['Covariance','Mean']},
|
| 422 |
+
'xAxis': {
|
| 423 |
+
'axisTick': {
|
| 424 |
+
'alignWithLabel': True
|
| 425 |
+
},
|
| 426 |
+
'data': list(x),
|
| 427 |
+
'name': xlabel,
|
| 428 |
+
'nameLocation': 'middle',
|
| 429 |
+
'nameTextStyle': {'verticalAlign': 'top','padding': [10, 0, 0, 0]}
|
| 430 |
+
},
|
| 431 |
+
'yAxis': [
|
| 432 |
+
{
|
| 433 |
+
'type': 'value',
|
| 434 |
+
'name': "Covariance",
|
| 435 |
+
'position': 'left',
|
| 436 |
+
'alignTicks': True,
|
| 437 |
+
'axisLine': {
|
| 438 |
+
'show': True,
|
| 439 |
+
'lineStyle': {'color': 'green'}}
|
| 440 |
+
},
|
| 441 |
+
{
|
| 442 |
+
'type': 'value',
|
| 443 |
+
'name': "Mean",
|
| 444 |
+
'position': 'right',
|
| 445 |
+
'alignTicks': True,
|
| 446 |
+
'axisLine': {
|
| 447 |
+
'show': True,
|
| 448 |
+
'lineStyle': {'color': 'blue'}}
|
| 449 |
+
},
|
| 450 |
+
|
| 451 |
+
],
|
| 452 |
+
'series': [
|
| 453 |
+
{
|
| 454 |
+
'name': 'Mean',
|
| 455 |
+
'data': list(y),
|
| 456 |
+
'type': 'line',
|
| 457 |
+
'yAxisIndex': 1
|
| 458 |
+
},
|
| 459 |
+
{
|
| 460 |
+
'name': 'Covariance',
|
| 461 |
+
'data': list(s),
|
| 462 |
+
'type': 'line',
|
| 463 |
+
'yAxisIndex': 0
|
| 464 |
+
},
|
| 465 |
+
],
|
| 466 |
+
}
|
| 467 |
+
solara.FigureEcharts(option=options)
|
| 468 |
+
|
| 469 |
+
@solara.component
|
| 470 |
+
def VulnerabiliyDisplayer(vuln_xml: dict):
|
| 471 |
+
vuln_xml, set_vuln_xml = solara.use_state_or_update(vuln_xml)
|
| 472 |
+
|
| 473 |
+
func_labels = [f'{v["imt"]}---{v["id"]}' for v in vuln_xml['vulnerabilityFunctions']]
|
| 474 |
+
func_label, set_func_label = solara.use_state_or_update(func_labels[0])
|
| 475 |
+
|
| 476 |
+
with solara.GridFixed(columns=2):
|
| 477 |
+
with solara.Column(gap="1px"):
|
| 478 |
+
solara.Text('Description:',style={'fontWeight': 'bold'})
|
| 479 |
+
with solara.Row(justify="left"):
|
| 480 |
+
solara.Text(f'{vuln_xml["description"]}')
|
| 481 |
+
with solara.GridFixed(columns=2,row_gap="1px"):
|
| 482 |
+
solara.Text('Asset Category:',style={'fontWeight': 'bold'})
|
| 483 |
+
with solara.Row(justify="right"):
|
| 484 |
+
solara.Text(f'{vuln_xml["assetCategory"]}')
|
| 485 |
+
solara.Text('Loss Category:',style={'fontWeight': 'bold'})
|
| 486 |
+
with solara.Row(justify="right"):
|
| 487 |
+
solara.Text(f'{vuln_xml["lossCategory"]}')
|
| 488 |
+
solara.Text('# of vulnerability functions:',style={'fontWeight': 'bold'})
|
| 489 |
+
with solara.Row(justify="right"):
|
| 490 |
+
solara.Text(f'{len(vuln_xml["vulnerabilityFunctions"])}')
|
| 491 |
+
solara.Text('Select vulnerability function:',style={'fontWeight': 'bold'})
|
| 492 |
+
solara.Select(label='',value=func_label, values=func_labels,
|
| 493 |
+
on_value=set_func_label)
|
| 494 |
+
with solara.Column():
|
| 495 |
+
VulnerabilityFunctionDisplayer(vuln_xml['vulnerabilityFunctions'][func_labels.index(func_label)])
|
| 496 |
+
|
| 497 |
@solara.component
|
| 498 |
def MetricWidget(name, description, value, max_value, render_count):
|
| 499 |
value, set_value = solara.use_state_or_update(value)
|
|
|
|
| 530 |
|
| 531 |
|
| 532 |
def import_data(fileinfo: solara.components.file_drop.FileInfo):
|
| 533 |
+
data_array = fileinfo['data']
|
| 534 |
extension = fileinfo['name'].split('.')[-1]
|
| 535 |
if extension == 'xlsx':
|
| 536 |
+
data = pd.read_excel(data_array)
|
| 537 |
elif extension in ['tiff','tif']:
|
| 538 |
+
data = read_tiff(data_array)
|
| 539 |
+
elif extension.lower() in ['xml']:
|
| 540 |
+
data = read_gem_xml(data_array)
|
| 541 |
else:
|
| 542 |
+
json_string = data_array.decode('utf-8')
|
| 543 |
json_data = json.loads(json_string)
|
| 544 |
if "features" in json_data.keys():
|
| 545 |
+
data = gpd.GeoDataFrame.from_features(json_data['features'])
|
| 546 |
else:
|
| 547 |
+
data = pd.read_json(json_string)
|
| 548 |
+
|
| 549 |
+
if isinstance(data, gpd.GeoDataFrame) or isinstance(data, pd.DataFrame):
|
| 550 |
+
data.columns = data.columns.str.lower()
|
| 551 |
+
attributes = set(data.columns)
|
| 552 |
+
elif isinstance(data, dict):
|
| 553 |
+
attributes = set(data.keys())
|
| 554 |
+
else:
|
| 555 |
+
return (None, None)
|
| 556 |
|
|
|
|
| 557 |
|
| 558 |
# in the first pass, look for exact column match
|
| 559 |
name = None
|
| 560 |
for layer_name, layer in layers.value['layers'].items():
|
| 561 |
+
if layer['attributes'] == attributes:
|
| 562 |
name = layer_name
|
| 563 |
break
|
| 564 |
# if not, check only the required columns
|
| 565 |
if name is None:
|
| 566 |
for layer_name, layer in layers.value['layers'].items():
|
| 567 |
+
if layer['attributes_required'].issubset(attributes):
|
| 568 |
name = layer_name
|
| 569 |
+
logging.debug('There are extra columns', attributes - layer['attributes_required'])
|
| 570 |
break
|
| 571 |
|
| 572 |
|
| 573 |
# Inject columns
|
| 574 |
+
if name is not None and (isinstance(data, gpd.GeoDataFrame) or isinstance(data, pd.DataFrame)):
|
| 575 |
for col, val in layers.value['layers'][name]['extra_cols'].items():
|
| 576 |
+
data[col] = val
|
| 577 |
+
return (name, data)
|
| 578 |
|
| 579 |
|
| 580 |
@solara.component
|
|
|
|
| 586 |
def load():
|
| 587 |
if fileinfo is not None:
|
| 588 |
print('processing file')
|
| 589 |
+
name, data = import_data(fileinfo)
|
| 590 |
+
if name is not None and data is not None:
|
| 591 |
+
if isinstance(data, gpd.GeoDataFrame) or isinstance(data, pd.DataFrame):
|
| 592 |
+
layers.value['layers'][name]['data'].set(data)
|
| 593 |
+
layers.value['selected_layer'].set(name)
|
| 594 |
+
layers.value['layers'][name]['visible'].set(True)
|
| 595 |
+
layers.value['layers'][name]['force_render'].set(True)
|
| 596 |
+
if "geometry" in list(data.columns):
|
| 597 |
+
center = (data.geometry.centroid.y.mean(), data.geometry.centroid.x.mean())
|
| 598 |
+
layers.value['center'].set(center)
|
| 599 |
+
elif isinstance(data, dict):
|
| 600 |
+
layers.value['layers'][name]['data'].set(data)
|
| 601 |
+
layers.value['selected_layer'].set(name)
|
| 602 |
+
layers.value['layers'][name]['visible'].set(True)
|
| 603 |
+
layers.value['layers'][name]['force_render'].set(True)
|
| 604 |
else:
|
| 605 |
return False
|
| 606 |
return True
|
|
|
|
| 639 |
@solara.component
|
| 640 |
def LayerDisplayer():
|
| 641 |
print(f'{layers.value["bounds"].value}')
|
| 642 |
+
nonempty_layers = {name: layer for name, layer in layers.value['layers'].items() if layer['data'].value is not None}
|
| 643 |
nonempty_layer_names = list(nonempty_layers.keys())
|
| 644 |
selected = layers.value['selected_layer'].value
|
| 645 |
def set_selected(s):
|
|
|
|
| 650 |
if selected is None and len(nonempty_layer_names) > 0:
|
| 651 |
set_selected(nonempty_layer_names[0])
|
| 652 |
if selected is not None:
|
| 653 |
+
data = nonempty_layers[selected]['data'].value
|
| 654 |
+
if isinstance(data, gpd.GeoDataFrame) or isinstance(data, pd.DataFrame):
|
| 655 |
+
if "geometry" in data.columns:
|
| 656 |
+
((ymin,xmin),(ymax,xmax)) = layers.value['bounds'].value
|
| 657 |
+
df_filtered = data.cx[xmin:xmax,ymin:ymax].drop(columns='geometry')
|
| 658 |
+
solara.DataFrame(df_filtered)
|
| 659 |
+
else:
|
| 660 |
+
solara.DataFrame(data)
|
| 661 |
+
if selected == "building":
|
| 662 |
+
file_object = data.to_json()
|
| 663 |
+
with solara.FileDownload(file_object, "building_export.geojson", mime_type="application/geo+json"):
|
| 664 |
+
solara.Button("Download GeoJSON", icon_name="mdi-cloud-download-outline", color="primary")
|
| 665 |
+
if selected == 'gem_vulnerability':
|
| 666 |
+
VulnerabiliyDisplayer(data)
|
| 667 |
|
| 668 |
@solara.component
|
| 669 |
def MetricPanel():
|
| 670 |
+
building = layers.value['layers']['building']['data'].value
|
| 671 |
filtered_metrics = {name: 0 for name in layers.value['metrics'].keys()}
|
| 672 |
if building is not None and layers.value['bounds'].value is not None:
|
| 673 |
((ymin,xmin),(ymax,xmax)) = layers.value['bounds'].value
|
|
|
|
| 708 |
|
| 709 |
render_order = [l['render_order'] for _, l in layers.value['layers'].items()]
|
| 710 |
for _, (layer_name, layer) in sorted(zip(render_order,layers.value['layers'].items())):
|
| 711 |
+
df = layer['data'].value
|
| 712 |
+
if isinstance(df, gpd.GeoDataFrame):
|
| 713 |
+
# we have something to display on map
|
| 714 |
+
if "geometry" in list(df.columns) and layer['visible'].value:
|
| 715 |
+
map_layer = create_map_layer(df, layer_name)
|
| 716 |
+
if map_layer is not None:
|
| 717 |
+
map_layers.append(map_layer)
|
|
|
|
| 718 |
|
| 719 |
|
| 720 |
ipyleaflet.Map.element(
|
|
|
|
| 747 |
execute_error.set("")
|
| 748 |
|
| 749 |
def is_ready_to_run(infra, hazard):
|
| 750 |
+
existing_layers = set([name for name, l in layers.value['layers'].items() if l['data'].value is not None])
|
| 751 |
missing = []
|
| 752 |
|
| 753 |
if hazard == "earthquake":
|
|
|
|
| 771 |
|
| 772 |
|
| 773 |
def execute_infra():
|
| 774 |
+
nodes = layers.value['layers']['power nodes']['data'].value
|
| 775 |
+
edges = layers.value['layers']['power edges']['data'].value
|
| 776 |
+
intensity = layers.value['layers']['intensity']['data'].value
|
| 777 |
+
power_fragility = layers.value['layers']['power fragility']['data'].value
|
| 778 |
|
| 779 |
|
| 780 |
eq_ds, is_damaged, is_operational = compute_power_infra(nodes,
|
|
|
|
| 789 |
return nodes
|
| 790 |
|
| 791 |
def execute_building():
|
| 792 |
+
landuse = layers.value['layers']['landuse']['data'].value
|
| 793 |
+
buildings = layers.value['layers']['building']['data'].value
|
| 794 |
+
household = layers.value['layers']['household']['data'].value
|
| 795 |
+
individual = layers.value['layers']['individual']['data'].value
|
| 796 |
+
intensity = layers.value['layers']['intensity']['data'].value
|
| 797 |
|
| 798 |
+
fragility = layers.value['layers']['fragility']['data'].value
|
| 799 |
+
vulnerability = layers.value['layers']['vulnerability']['data'].value
|
| 800 |
|
| 801 |
policies = [p['id'] for id, p in layers.value['policies'].items() if p['applied'].value]
|
| 802 |
|
|
|
|
| 836 |
|
| 837 |
if 'power' in infra:
|
| 838 |
nodes = execute_infra()
|
| 839 |
+
layers.value['layers']['power nodes']['data'].set(nodes)
|
| 840 |
if 'building' in infra:
|
| 841 |
buildings = execute_building()
|
| 842 |
+
layers.value['layers']['building']['data'].set(buildings)
|
| 843 |
|
| 844 |
# trigger render event
|
| 845 |
layers.value['render_count'].set(layers.value['render_count'].value + 1)
|
|
|
|
| 911 |
if layers.value['map_info_button'].value == "summary":
|
| 912 |
with solara.GridFixed(columns=2,row_gap="1px"):
|
| 913 |
for layer_name,layer in layers.value['layers'].items():
|
| 914 |
+
data = layer['data'].value
|
| 915 |
with solara.Tooltip(layer['map_info_tooltip']):
|
| 916 |
solara.Text(f'{layer_name}')
|
| 917 |
with solara.Row(justify="right"):
|
| 918 |
+
if data is None:
|
| 919 |
solara.Text('0')
|
| 920 |
else:
|
| 921 |
+
if isinstance(data, gpd.GeoDataFrame) or isinstance(data, pd.DataFrame):
|
| 922 |
+
solara.Text(f"{len(data)}")
|
| 923 |
+
elif isinstance(data, dict) and layer_name == 'gem_vulnerability':
|
| 924 |
+
solara.Text(f"{len(data['vulnerabilityFunctions'])}")
|
| 925 |
else:
|
| 926 |
with solara.GridFixed(columns=2,row_gap="1px"):
|
| 927 |
for key, value in layers.value['map_info_detail'].value.items():
|