hkayabilisim commited on
Commit
0b69fd8
·
1 Parent(s): 0922c28

Added support for reading GEM XML format

Browse files
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
- There are may different possible strategies and data formats to describe and store data.
16
- In TCDSE, we generally use tabular data where each row corresponds to a unique object whereas the columns corresponds to the features of that object. Object here can refer to a building, individual, etc. Full list of the objects for which we use a dedicated data file is as follows:
17
 
18
- * landuse
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 Vulnerability Analysis
4
- description: Methodologies in flood damage assessment
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: "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://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
@@ -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 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
@@ -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://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: "Power Network Analysis"
8
  createdAt: 2023-10-10
9
  duration: 6 min read
@@ -11,4 +11,6 @@ category:
11
  - general
12
  ---
13
 
14
- ## Power Infrastructure Analysis
 
 
 
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://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: "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, title=metadata["title"], description=metadata["description"])
 
 
 
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
- 'df': solara.reactive(None),
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
- 'cols_required': set(['residents', 'fptarea', 'repvalue', 'nhouse', 'zoneid', 'expstr', 'bldid', 'geometry', 'specialfac']),
33
- 'cols': set(['residents', 'fptarea', 'repvalue', 'nhouse', 'zoneid', 'expstr', 'bldid', 'geometry', 'specialfac'])},
34
  'landuse': {
35
  'render_order': 20,
36
  'map_info_tooltip': 'Number of landuse zones',
37
- 'df': solara.reactive(None),
38
  'map_layer': solara.reactive(None),
39
  'force_render': solara.reactive(False),
40
  'visible': solara.reactive(False),
41
  'extra_cols': {},
42
- 'cols_required': set(['geometry', 'zoneid', 'luf', 'population', 'densitycap', 'avgincome']),
43
- 'cols': set(['geometry', 'zoneid', 'luf', 'population', 'densitycap', 'floorarat', 'setback', 'avgincome'])},
44
  'household': {
45
  'render_order': 0,
46
- 'df': solara.reactive(None),
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
- 'cols_required':set(['hhid', 'nind', 'income', 'bldid', 'commfacid']),
53
- 'cols':set(['hhid', 'nind', 'income', 'bldid', 'commfacid'])},
54
  'individual': {
55
  'render_order': 0,
56
- 'df': solara.reactive(None),
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
- 'cols_required': set(['individ', 'hhid', 'gender', 'age', 'eduattstat', 'head', 'indivfacid']),
63
- 'cols': set(['individ', 'hhid', 'gender', 'age', 'eduattstat', 'head', 'indivfacid'])},
64
  'intensity': {
65
  'render_order': 0,
66
- 'df': solara.reactive(None),
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
- 'cols_required': set(['geometry','im']),
73
- 'cols': set(['geometry','im'])},
74
  'fragility': {
75
  'render_order': 0,
76
- 'df': solara.reactive(None),
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
- 'cols_required': set(['expstr','muds1_g','muds2_g','muds3_g','muds4_g','sigmads1','sigmads2','sigmads3','sigmads4']),
83
- 'cols': set(['expstr','muds1_g','muds2_g','muds3_g','muds4_g','sigmads1','sigmads2','sigmads3','sigmads4'])},
84
  'vulnerability': {
85
  'render_order': 0,
86
- 'df': solara.reactive(None),
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
- 'cols_required': set(['expstr', 'hw0', 'hw0_5', 'hw1', 'hw1_5', 'hw2', 'hw3', 'hw4', 'hw5','hw6']),
93
- 'cols': set(['expstr', 'hw0', 'hw0_5', 'hw1', 'hw1_5', 'hw2', 'hw3', 'hw4', 'hw5','hw6'])},
 
 
 
 
 
 
 
 
 
 
 
94
  'power nodes': {
95
  'render_order': 90,
96
- 'df': solara.reactive(None),
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
- 'cols_required': set(['geometry', 'node_id', 'pwr_plant', 'n_bldgs', 'eq_vuln']),
103
- 'cols': set(['geometry', 'fltytype', 'strctype', 'utilfcltyc', 'indpnode', 'guid',
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
- 'df': solara.reactive(None),
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
- 'cols_required': set(['geometry','from_node','to_node', 'edge_id']),
115
- 'cols': set(['from_node', 'direction', 'pipetype', 'edge_id', 'guid', 'capacity',
116
  'geometry', 'to_node', 'length'])},
117
  'power fragility': {
118
  'render_order': 0,
119
- 'df': solara.reactive(None),
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
- 'cols_required': set(['vuln_string', 'med_slight', 'med_moderate', 'med_extensive', 'med_complete',
126
  'beta_slight', 'beta_moderate', 'beta_extensive', 'beta_complete']),
127
- 'cols': set(['vuln_string', 'med_slight', 'med_moderate', 'med_extensive', 'med_complete',
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
- data = fileinfo['data']
390
  extension = fileinfo['name'].split('.')[-1]
391
  if extension == 'xlsx':
392
- df = pd.read_excel(data)
393
  elif extension in ['tiff','tif']:
394
- df = read_tiff(data)
 
 
395
  else:
396
- json_string = data.decode('utf-8')
397
  json_data = json.loads(json_string)
398
  if "features" in json_data.keys():
399
- df = gpd.GeoDataFrame.from_features(json_data['features'])
400
  else:
401
- df = pd.read_json(json_string)
 
 
 
 
 
 
 
 
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['cols'] == set(df.columns):
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['cols_required'].issubset(set(df.columns)):
415
  name = layer_name
416
- logging.debug('There are extra columns', set(df.columns) - layer['cols_required'])
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
- df[col] = val
424
- return (name, df)
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, df = import_data(fileinfo)
437
- if name is not None and df is not None:
438
- layers.value['layers'][name]['df'].set(df)
439
- layers.value['selected_layer'].set(name)
440
- layers.value['layers'][name]['visible'].set(True)
441
- layers.value['layers'][name]['force_render'].set(True)
442
- if "geometry" in list(df.columns):
443
- center = (df.geometry.centroid.y.mean(), df.geometry.centroid.x.mean())
444
- layers.value['center'].set(center)
 
 
 
 
 
 
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['df'].value is not None}
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
- df = nonempty_layers[selected]['df'].value
495
- if "geometry" in df.columns:
496
- ((ymin,xmin),(ymax,xmax)) = layers.value['bounds'].value
497
- df_filtered = df.cx[xmin:xmax,ymin:ymax].drop(columns='geometry')
498
- solara.DataFrame(df_filtered)
499
- else:
500
- solara.DataFrame(df)
501
- if selected == "building":
502
- file_object = df.to_json()
503
- with solara.FileDownload(file_object, "building_export.geojson", mime_type="application/geo+json"):
504
- solara.Button("Download GeoJSON", icon_name="mdi-cloud-download-outline", color="primary")
505
-
 
 
506
 
507
  @solara.component
508
  def MetricPanel():
509
- building = layers.value['layers']['building']['df'].value
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['df'].value
551
- if df is None:
552
- continue
553
- # we have something to display on map
554
- if "geometry" in list(df.columns) and layer['visible'].value:
555
- map_layer = create_map_layer(df, layer_name)
556
- if map_layer is not None:
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['df'].value is not None])
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']['df'].value
615
- edges = layers.value['layers']['power edges']['df'].value
616
- intensity = layers.value['layers']['intensity']['df'].value
617
- power_fragility = layers.value['layers']['power fragility']['df'].value
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']['df'].value
633
- buildings = layers.value['layers']['building']['df'].value
634
- household = layers.value['layers']['household']['df'].value
635
- individual = layers.value['layers']['individual']['df'].value
636
- intensity = layers.value['layers']['intensity']['df'].value
637
 
638
- fragility = layers.value['layers']['fragility']['df'].value
639
- vulnerability = layers.value['layers']['vulnerability']['df'].value
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']['df'].set(nodes)
680
  if 'building' in infra:
681
  buildings = execute_building()
682
- layers.value['layers']['building']['df'].set(buildings)
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 layer['df'].value is None:
758
  solara.Text('0')
759
  else:
760
- solara.Text(f"{len(layer['df'].value)}")
 
 
 
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():