Spaces:
Running
Running
Added Temp Diff Feature
Browse files- .gitattributes +3 -0
- .gitignore +4 -0
- Dockerfile +35 -0
- README.md +58 -3
- klimainsights/__pycache__/app.cpython-311.pyc +0 -0
- klimainsights/app.py +11 -0
- klimainsights/assets/icon.svg +3 -0
- klimainsights/assets/styles.css +283 -0
- klimainsights/data/biodiversity.geojson +3 -0
- klimainsights/data/disaster.geojson +3 -0
- klimainsights/data/temperature.geojson +3 -0
- klimainsights/environment/.env +4 -0
- klimainsights/environment/__pycache__/settings.cpython-311.pyc +0 -0
- klimainsights/environment/settings.py +12 -0
- klimainsights/index.py +44 -0
- klimainsights/pages/__pycache__/biodiversity.cpython-311.pyc +0 -0
- klimainsights/pages/__pycache__/disaster.cpython-311.pyc +0 -0
- klimainsights/pages/__pycache__/initial.cpython-311.pyc +0 -0
- klimainsights/pages/__pycache__/temperature.cpython-311.pyc +0 -0
- klimainsights/pages/biodiversity.py +23 -0
- klimainsights/pages/disaster.py +325 -0
- klimainsights/pages/initial.py +32 -0
- klimainsights/pages/temperature.py +310 -0
- plotly_interactions.ipynb +1101 -0
- requirements.txt +20 -0
.gitattributes
CHANGED
@@ -33,3 +33,6 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
klimainsights/data/biodiversity.geojson filter=lfs diff=lfs merge=lfs -text
|
37 |
+
klimainsights/data/disaster.geojson filter=lfs diff=lfs merge=lfs -text
|
38 |
+
klimainsights/data/temperature.geojson filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
**/EDA_temperature/*
|
2 |
+
**/EDA_biodiversity/*
|
3 |
+
**/EDA_disaster/*
|
4 |
+
**/raw_data/*
|
Dockerfile
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:3.11.8
|
2 |
+
|
3 |
+
# Create non-root group and user
|
4 |
+
RUN addgroup --system dash_app \
|
5 |
+
&& adduser --system --home /var/cache/dash_app --ingroup dash_app --uid 1001 dashuser
|
6 |
+
|
7 |
+
USER dashuser
|
8 |
+
|
9 |
+
WORKDIR /dash_app/
|
10 |
+
|
11 |
+
COPY --chown=dashuser requirements.txt /dash_app/
|
12 |
+
|
13 |
+
# Elegantly activating a venv in Dockerfile: https://pythonspeed.com/articles/activate-virtualenv-dockerfile/
|
14 |
+
ENV VIRTUAL_ENV=/dash_app/venv
|
15 |
+
RUN python3 -m venv $VIRTUAL_ENV
|
16 |
+
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
|
17 |
+
|
18 |
+
# Install requirements
|
19 |
+
RUN pip install --trusted-host pypi.python.org -r requirements.txt
|
20 |
+
|
21 |
+
COPY --chown=dashuser /klimainsights/ /dash_app/klimainsights/
|
22 |
+
|
23 |
+
WORKDIR /dash_app/klimainsights/
|
24 |
+
|
25 |
+
# set enviroment variables
|
26 |
+
# This prevent Python from writing out pyc files
|
27 |
+
ENV PYTHONDONTWRITEBYTECODE=1
|
28 |
+
# This keeps Python from buffering stdin/stdout
|
29 |
+
ENV PYTHONUNBUFFERED=1
|
30 |
+
|
31 |
+
ENV ENV_FILE=".env"
|
32 |
+
|
33 |
+
EXPOSE 7000
|
34 |
+
|
35 |
+
ENTRYPOINT ["gunicorn", "index:server", "-b", "0.0.0.0:7860", "--workers=1"]
|
README.md
CHANGED
@@ -1,10 +1,65 @@
|
|
1 |
---
|
2 |
title: Klima Insights
|
3 |
-
emoji:
|
4 |
-
colorFrom:
|
5 |
-
colorTo:
|
6 |
sdk: docker
|
7 |
pinned: false
|
8 |
---
|
9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
|
|
1 |
---
|
2 |
title: Klima Insights
|
3 |
+
emoji: 🌳
|
4 |
+
colorFrom: green
|
5 |
+
colorTo: indigo
|
6 |
sdk: docker
|
7 |
pinned: false
|
8 |
---
|
9 |
|
10 |
+
# DATA101 Final Application
|
11 |
+
|
12 |
+
This is the repository for the data visualization application made from Plotly Dash for the DATA101 Final Project
|
13 |
+
|
14 |
+
## Consolidated EDA Files
|
15 |
+
|
16 |
+
All files that are used to do Exploratory Data Analysis is stored in this [Google Drive Link](https://drive.google.com/drive/folders/16gl_XkzblRBFiXGtehRJmr24hS63GvAw?usp=sharing)
|
17 |
+
|
18 |
+
## How to Run the Application
|
19 |
+
|
20 |
+
### Prerequisites:
|
21 |
+
|
22 |
+
- Preferred if Python and Anaconda is installed in the system
|
23 |
+
- Can run `pip` and `conda` commands
|
24 |
+
- Can create a virtual environment to ensure no dependency errors (optional)
|
25 |
+
|
26 |
+
### Creating a Virtual Environment with Conda
|
27 |
+
|
28 |
+
- Open Command Line Interface `cmd`
|
29 |
+
- Create a conda environment by running:
|
30 |
+
|
31 |
+
```bash
|
32 |
+
conda create --name <name of your choice> python=3.11.8
|
33 |
+
```
|
34 |
+
|
35 |
+
- Activate the environment if it is not yet activated. It should show "(name of your choice)" before the path in your CLI.
|
36 |
+
|
37 |
+
```bash
|
38 |
+
conda activate <name of your choice>
|
39 |
+
```
|
40 |
+
|
41 |
+
### Installing Dependencies for the application
|
42 |
+
|
43 |
+
- In your CLI, go to the root directory of the repository. There should be a requirements.txt file there.
|
44 |
+
- Install dependencies by running the following commands: (Note that you should still be inside your virtual environment)
|
45 |
+
```bash
|
46 |
+
conda install -c conda-forge gdal
|
47 |
+
```
|
48 |
+
```bash
|
49 |
+
pip install -r requirements.txt
|
50 |
+
```
|
51 |
+
- If there are no errors, you should be able to run the app smoothly along with the EDA files.
|
52 |
+
- Remember to run everything inside your virtual environment.
|
53 |
+
|
54 |
+
### Running the Application
|
55 |
+
|
56 |
+
- go to the `/klimainsights` directory
|
57 |
+
- Run:
|
58 |
+
|
59 |
+
```bash
|
60 |
+
python index.py
|
61 |
+
```
|
62 |
+
|
63 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
64 |
+
|
65 |
+
App deployed using Docker and Hugging Face.
|
klimainsights/__pycache__/app.cpython-311.pyc
ADDED
Binary file (615 Bytes). View file
|
|
klimainsights/app.py
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from dash import Dash, html, page_container
|
2 |
+
import dash_bootstrap_components as dbc
|
3 |
+
|
4 |
+
APP_TITLE = "Klima Insights"
|
5 |
+
|
6 |
+
app = Dash(__name__,
|
7 |
+
title=APP_TITLE,
|
8 |
+
update_title='Loading...',
|
9 |
+
suppress_callback_exceptions=True,
|
10 |
+
use_pages=True,
|
11 |
+
external_stylesheets=[dbc.themes.SOLAR, 'styles.css'])
|
klimainsights/assets/icon.svg
ADDED
klimainsights/assets/styles.css
ADDED
@@ -0,0 +1,283 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
html, body {
|
2 |
+
box-sizing: border-box;
|
3 |
+
overflow-x: hidden;
|
4 |
+
overflow-y: scroll;
|
5 |
+
scrollbar-width: none; /* Firefox */
|
6 |
+
-ms-overflow-style: none; /* IE and Edge */
|
7 |
+
width: 100%;
|
8 |
+
max-width: 100%;
|
9 |
+
background: #222;
|
10 |
+
margin: 0;
|
11 |
+
padding: 0;
|
12 |
+
}
|
13 |
+
|
14 |
+
#main {
|
15 |
+
display: flex;
|
16 |
+
flex-direction: column;
|
17 |
+
width: 100%;
|
18 |
+
height: 100%;
|
19 |
+
justify-content: start;
|
20 |
+
align-items: center;
|
21 |
+
}
|
22 |
+
|
23 |
+
@media (max-width: 767px) {
|
24 |
+
.card {
|
25 |
+
width: 95%;
|
26 |
+
}
|
27 |
+
.scale-button {
|
28 |
+
transform: scale(1);
|
29 |
+
}
|
30 |
+
.full-width-container {
|
31 |
+
width: 85vw;
|
32 |
+
}
|
33 |
+
#temp-map {
|
34 |
+
width: 90vw;
|
35 |
+
height: 95vh;
|
36 |
+
margin: 0;
|
37 |
+
padding: 0;
|
38 |
+
}
|
39 |
+
}
|
40 |
+
@media (min-width: 768px) {
|
41 |
+
.card {
|
42 |
+
width: 70%;
|
43 |
+
}
|
44 |
+
.card:hover span {
|
45 |
+
top: 0;
|
46 |
+
font-size: 1.2em;
|
47 |
+
}
|
48 |
+
.card:hover {
|
49 |
+
background: rgba(255, 255, 255, 0.3);
|
50 |
+
}
|
51 |
+
.card:hover>div,.card:hover>strong {
|
52 |
+
opacity: 0;
|
53 |
+
}
|
54 |
+
.scale-button {
|
55 |
+
transform: scale(0);
|
56 |
+
}
|
57 |
+
#temp-map {
|
58 |
+
width: 100%;
|
59 |
+
height: 95vh;
|
60 |
+
margin: 0;
|
61 |
+
padding: 0;
|
62 |
+
}
|
63 |
+
}
|
64 |
+
|
65 |
+
.card {
|
66 |
+
--hover-bg: white;
|
67 |
+
--hover-text: black;
|
68 |
+
background-color: rgba(0, 0, 0, 0.4);
|
69 |
+
backdrop-filter: blur(5px);
|
70 |
+
text-align: center;
|
71 |
+
padding: 1.5em;
|
72 |
+
padding-block: 1.8em;
|
73 |
+
border-radius: 5px;
|
74 |
+
position: relative;
|
75 |
+
overflow: hidden;
|
76 |
+
transition: .3s cubic-bezier(.6,.4,0,1),transform .15s ease;
|
77 |
+
display: flex;
|
78 |
+
flex-direction: column;
|
79 |
+
justify-content: center;
|
80 |
+
align-items: center;
|
81 |
+
gap: 1em;
|
82 |
+
}
|
83 |
+
|
84 |
+
.card__body {
|
85 |
+
color: #d8d8d8;
|
86 |
+
line-height: 1.5em;
|
87 |
+
font-size: 1.2em;
|
88 |
+
}
|
89 |
+
|
90 |
+
.card > :not(span) {
|
91 |
+
transition: .3s cubic-bezier(.6,.4,0,1);
|
92 |
+
}
|
93 |
+
|
94 |
+
.card > strong {
|
95 |
+
display: block;
|
96 |
+
font-size: 1.5rem;
|
97 |
+
letter-spacing: -.035em;
|
98 |
+
}
|
99 |
+
|
100 |
+
.card span {
|
101 |
+
position: absolute;
|
102 |
+
inset: 0;
|
103 |
+
width: 100%;
|
104 |
+
height: 100%;
|
105 |
+
display: flex;
|
106 |
+
justify-content: center;
|
107 |
+
align-items: center;
|
108 |
+
color: var(--hover-text);
|
109 |
+
border-radius: 5px;
|
110 |
+
font-weight: bold;
|
111 |
+
top: 100%;
|
112 |
+
transition: all .3s cubic-bezier(.6,.4,0,1);
|
113 |
+
}
|
114 |
+
|
115 |
+
.full-height {
|
116 |
+
height: 100%;
|
117 |
+
}
|
118 |
+
|
119 |
+
.pt-5 {
|
120 |
+
padding-top: 5rem;
|
121 |
+
}
|
122 |
+
|
123 |
+
.full-width {
|
124 |
+
width: 95%;
|
125 |
+
}
|
126 |
+
|
127 |
+
.w-100 {
|
128 |
+
width: 100%;
|
129 |
+
}
|
130 |
+
|
131 |
+
.flex-gap-20 {
|
132 |
+
display: flex;
|
133 |
+
gap: 20px;
|
134 |
+
}
|
135 |
+
|
136 |
+
.temp-bar-container {
|
137 |
+
height: 800px;
|
138 |
+
min-height: 800px;
|
139 |
+
}
|
140 |
+
|
141 |
+
.disaster-map-height {
|
142 |
+
height: 55rem;
|
143 |
+
}
|
144 |
+
|
145 |
+
.overflow-hidden {
|
146 |
+
overflow: hidden;
|
147 |
+
}
|
148 |
+
|
149 |
+
/* BACKGROUND */
|
150 |
+
#leaves {position:fixed;width:100%;height:100%;text-align: right;}
|
151 |
+
|
152 |
+
#leaves i {
|
153 |
+
display: inline-block;
|
154 |
+
width: 200px;
|
155 |
+
height: 150px;
|
156 |
+
background: linear-gradient(to bottom right, #309900, #005600);
|
157 |
+
transform: skew(20deg);
|
158 |
+
border-radius: 5% 40% 70%;
|
159 |
+
box-shadow: inset 0px 0px 1px #222;
|
160 |
+
border: 1px solid #333;
|
161 |
+
z-index: 1;
|
162 |
+
-webkit-animation: falling 5s 0s infinite;
|
163 |
+
}
|
164 |
+
|
165 |
+
#leaves i:nth-of-type(2n) { -webkit-animation: falling2 5s 0s infinite; }
|
166 |
+
#leaves i:nth-of-type(3n) { -webkit-animation: falling3 5s 0s infinite; }
|
167 |
+
|
168 |
+
#leaves i:before {
|
169 |
+
position: absolute;
|
170 |
+
content: '';
|
171 |
+
top: 117px;
|
172 |
+
right: 9px;
|
173 |
+
height: 27px;
|
174 |
+
width: 32px;
|
175 |
+
transform: rotate(49deg);
|
176 |
+
border-radius: 0% 15% 15% 0%;
|
177 |
+
border-top: 1px solid #222;
|
178 |
+
border-bottom: 1px solid #222;
|
179 |
+
border-left: 0px solid #222;
|
180 |
+
border-right: 1px solid #222;
|
181 |
+
background: linear-gradient(to right, rgba(0,100,0,1), #005600);
|
182 |
+
z-index: 1;
|
183 |
+
}
|
184 |
+
|
185 |
+
#leaves i:after {
|
186 |
+
content: '';
|
187 |
+
height: 125px;
|
188 |
+
width: 10px;
|
189 |
+
background: linear-gradient(to right, rgba(0,0,0,.15), rgba(0,0,0,0));
|
190 |
+
display: block;
|
191 |
+
transform: rotate(125deg);
|
192 |
+
position: absolute;
|
193 |
+
left: 85px;
|
194 |
+
border-radius:50%;
|
195 |
+
}
|
196 |
+
|
197 |
+
|
198 |
+
#leaves i:nth-of-type(n) { height:23px; width:30px; }
|
199 |
+
#leaves i:nth-of-type(n):before { width:7px; height:5px; top:17px; right:1px; }
|
200 |
+
#leaves i:nth-of-type(n):after { width:2px; height:17px; left: 12px; top:0px; }
|
201 |
+
|
202 |
+
#leaves i:nth-of-type(2n+1) { height:11px; width:16px; }
|
203 |
+
#leaves i:nth-of-type(2n+1):before { width:4px; height:3px; top:7px; right:0px; }
|
204 |
+
#leaves i:nth-of-type(2n+1):after { width:2px; height:6px; left: 5px; top:1px; }
|
205 |
+
|
206 |
+
#leaves i:nth-of-type(3n+2) { height:17px; width:23px; }
|
207 |
+
#leaves i:nth-of-type(3n+2):before { height:4px; width:4px; top:12px; right:1px; }
|
208 |
+
#leaves i:nth-of-type(3n+2):after { height:10px; width:2px; top:1px; left:8px; }
|
209 |
+
|
210 |
+
#leaves i:nth-of-type(n) { -webkit-animation-delay: 1.9s;}
|
211 |
+
#leaves i:nth-of-type(2n) { -webkit-animation-delay: 3.9s;}
|
212 |
+
#leaves i:nth-of-type(3n) { -webkit-animation-delay: 2.3s;}
|
213 |
+
#leaves i:nth-of-type(4n) { -webkit-animation-delay: 4.4s;}
|
214 |
+
#leaves i:nth-of-type(5n) { -webkit-animation-delay: 5s; }
|
215 |
+
#leaves i:nth-of-type(6n) { -webkit-animation-delay: 3.5s;}
|
216 |
+
#leaves i:nth-of-type(7n) { -webkit-animation-delay: 2.8s;}
|
217 |
+
#leaves i:nth-of-type(8n) { -webkit-animation-delay: 1.5s;}
|
218 |
+
#leaves i:nth-of-type(9n) { -webkit-animation-delay: 3.3s;}
|
219 |
+
#leaves i:nth-of-type(10n) { -webkit-animation-delay: 2.5s;}
|
220 |
+
#leaves i:nth-of-type(11n) { -webkit-animation-delay: 1.2s;}
|
221 |
+
#leaves i:nth-of-type(12n) { -webkit-animation-delay: 4.1s;}
|
222 |
+
#leaves i:nth-of-type(13n) { -webkit-animation-delay: 1s; }
|
223 |
+
#leaves i:nth-of-type(14n) { -webkit-animation-delay: 4.7s;}
|
224 |
+
#leaves i:nth-of-type(15n) { -webkit-animation-delay: 3s; }
|
225 |
+
|
226 |
+
#leaves i:nth-of-type(n) { background: linear-gradient(to bottom right, #309900, #005600); }
|
227 |
+
#leaves i:nth-of-type(2n+2) { background: linear-gradient(to bottom right, #5e9900, #2b5600); }
|
228 |
+
#leaves i:nth-of-type(4n+1) { background: linear-gradient(to bottom right, #990, #564500); }
|
229 |
+
|
230 |
+
#leaves i:nth-of-type(n) { opacity: .7;}
|
231 |
+
#leaves i:nth-of-type(3n+1) { opacity: .5;}
|
232 |
+
#leaves i:nth-of-type(3n+2) { opacity: .3;}
|
233 |
+
|
234 |
+
#leaves i:nth-of-type(n) {transform: rotate(180deg);}
|
235 |
+
|
236 |
+
|
237 |
+
#leaves i:nth-of-type(n) { -webkit-animation-timing-function:ease-in-out;}
|
238 |
+
|
239 |
+
@-webkit-keyframes falling {
|
240 |
+
|
241 |
+
0% {
|
242 |
+
-webkit-transform:
|
243 |
+
translate3d(300,0,0)
|
244 |
+
rotate(0deg);
|
245 |
+
}
|
246 |
+
|
247 |
+
100% {
|
248 |
+
-webkit-transform:
|
249 |
+
translate3d(-350px,700px,0)
|
250 |
+
rotate(90deg);
|
251 |
+
opacity: 0;
|
252 |
+
}
|
253 |
+
}
|
254 |
+
|
255 |
+
@-webkit-keyframes falling3 {
|
256 |
+
0% {
|
257 |
+
-webkit-transform:
|
258 |
+
translate3d(0,0,0)
|
259 |
+
rotate(-20deg);
|
260 |
+
}
|
261 |
+
|
262 |
+
100% {
|
263 |
+
-webkit-transform:
|
264 |
+
translate3d(-230px,640px,0)
|
265 |
+
rotate(-70deg);
|
266 |
+
opacity: 0;
|
267 |
+
}
|
268 |
+
}
|
269 |
+
|
270 |
+
@-webkit-keyframes falling2 {
|
271 |
+
0% {
|
272 |
+
-webkit-transform:
|
273 |
+
translate3d(0,0,0)
|
274 |
+
rotate(90deg);
|
275 |
+
}
|
276 |
+
|
277 |
+
100% {
|
278 |
+
-webkit-transform:
|
279 |
+
translate3d(-400px,680px,0)
|
280 |
+
rotate(0deg);
|
281 |
+
opacity: 0;
|
282 |
+
}
|
283 |
+
}
|
klimainsights/data/biodiversity.geojson
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:2108d044af326308e6800b1fc0a91a842b907f2a8682628f5c6091301d0d0ed2
|
3 |
+
size 58413480
|
klimainsights/data/disaster.geojson
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:c11fa6c413a768e9055b089a8aca275650d3b010a0636a2525668b43405f33a3
|
3 |
+
size 24675297
|
klimainsights/data/temperature.geojson
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:5ade3a8756ca96695f1a74cfc50ed356176dfaebd91ff66f21b4f347ac9d6697
|
3 |
+
size 24702277
|
klimainsights/environment/.env
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
HOST=127.0.0.1
|
2 |
+
PORT=7000
|
3 |
+
DEBUG=True
|
4 |
+
MAPBOX_TOKEN=pk.eyJ1Ijoicml1LXJkIiwiYSI6ImNsc3NybmFvNzFwZTkybG93bHA1djNidGMifQ.G8s9vUxBpIxg-xqBvbgBHg
|
klimainsights/environment/__pycache__/settings.cpython-311.pyc
ADDED
Binary file (1.08 kB). View file
|
|
klimainsights/environment/settings.py
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from dotenv import load_dotenv
|
3 |
+
|
4 |
+
env_path = os.path.join(os.path.dirname(__file__), os.getenv('ENV_FILE') or ".env")
|
5 |
+
load_dotenv(dotenv_path=env_path, override=True)
|
6 |
+
|
7 |
+
VERSION = os.environ.get("VERSION")
|
8 |
+
|
9 |
+
APP_HOST = os.environ.get("HOST")
|
10 |
+
APP_PORT = os.environ.get("PORT")
|
11 |
+
APP_DEBUG = bool(os.environ.get("DEBUG"))
|
12 |
+
MAPBOX_TOKEN = os.environ.get("MAPBOX_TOKEN")
|
klimainsights/index.py
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from dash import Dash, html, page_container
|
2 |
+
import dash_bootstrap_components as dbc
|
3 |
+
|
4 |
+
from app import app
|
5 |
+
from environment.settings import APP_HOST, APP_PORT, APP_DEBUG
|
6 |
+
|
7 |
+
server = app.server
|
8 |
+
|
9 |
+
def serve_content():
|
10 |
+
navbar = dbc.NavbarSimple(className='container-fluid z-3', brand="Klima Insights", brand_href="/", color="primary", dark=True, children=[
|
11 |
+
dbc.NavItem(dbc.NavLink("Climate History", href="/temperature")),
|
12 |
+
dbc.NavItem(dbc.NavLink("Biodiversity Insights", href="/biodiversity")),
|
13 |
+
dbc.NavItem(dbc.NavLink("Disaster Occurrences", href="/disaster")),
|
14 |
+
])
|
15 |
+
|
16 |
+
return html.Main(id='main', children=[
|
17 |
+
html.Div(
|
18 |
+
id="leaves",
|
19 |
+
children=[
|
20 |
+
html.I(),
|
21 |
+
html.I(),
|
22 |
+
html.I(),
|
23 |
+
html.I(),
|
24 |
+
html.I(),
|
25 |
+
html.I(),
|
26 |
+
html.I(),
|
27 |
+
html.I(),
|
28 |
+
html.I(),
|
29 |
+
html.I(),
|
30 |
+
html.I(),
|
31 |
+
html.I(),
|
32 |
+
html.I(),
|
33 |
+
html.I(),
|
34 |
+
html.I()
|
35 |
+
]),
|
36 |
+
navbar,
|
37 |
+
page_container
|
38 |
+
])
|
39 |
+
|
40 |
+
app._favicon = ("icon.svg")
|
41 |
+
app.layout = serve_content()
|
42 |
+
|
43 |
+
if __name__ == '__main__':
|
44 |
+
app.run_server(debug=APP_DEBUG, host=APP_HOST, port=APP_PORT)
|
klimainsights/pages/__pycache__/biodiversity.cpython-311.pyc
ADDED
Binary file (1.27 kB). View file
|
|
klimainsights/pages/__pycache__/disaster.cpython-311.pyc
ADDED
Binary file (15.8 kB). View file
|
|
klimainsights/pages/__pycache__/initial.cpython-311.pyc
ADDED
Binary file (2.2 kB). View file
|
|
klimainsights/pages/__pycache__/temperature.cpython-311.pyc
ADDED
Binary file (14.9 kB). View file
|
|
klimainsights/pages/biodiversity.py
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Setup Folders, Tokens, and Dependencies
|
2 |
+
from dash import Dash, html, dcc, callback, Output, Input, register_page
|
3 |
+
import dash_bootstrap_components as dbc
|
4 |
+
import plotly.express as px
|
5 |
+
import pandas as pd
|
6 |
+
import numpy as np
|
7 |
+
import geopandas as gpd
|
8 |
+
from pathlib import Path
|
9 |
+
from environment.settings import MAPBOX_TOKEN
|
10 |
+
|
11 |
+
datasets_folder = Path('./data')
|
12 |
+
px.set_mapbox_access_token(MAPBOX_TOKEN)
|
13 |
+
|
14 |
+
# Import Data
|
15 |
+
biodiversity_gdf = gpd.read_file(datasets_folder / 'biodiversity.geojson')
|
16 |
+
|
17 |
+
# Initialize Page
|
18 |
+
register_page(__name__, path='/biodiversity', name='Biodiversity', title='Klima Insights | Biodiversity')
|
19 |
+
|
20 |
+
layout = html.Div(children=[
|
21 |
+
html.H1(["BIODIVERSITY"])
|
22 |
+
|
23 |
+
])
|
klimainsights/pages/disaster.py
ADDED
@@ -0,0 +1,325 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Setup Folders, Tokens, and Dependencies
|
2 |
+
from dash import html, dcc, callback, Output, Input, State, register_page
|
3 |
+
import dash_bootstrap_components as dbc
|
4 |
+
import plotly.express as px
|
5 |
+
import pandas as pd
|
6 |
+
import numpy as np
|
7 |
+
import geopandas as gpd
|
8 |
+
from pathlib import Path
|
9 |
+
from environment.settings import MAPBOX_TOKEN
|
10 |
+
|
11 |
+
datasets_folder = Path('./data')
|
12 |
+
px.set_mapbox_access_token(MAPBOX_TOKEN)
|
13 |
+
|
14 |
+
# Import Data
|
15 |
+
temperature_gdf = gpd.read_file(datasets_folder / "temperature.geojson")
|
16 |
+
disaster_gdf = gpd.read_file(datasets_folder / 'disaster.geojson')
|
17 |
+
Region_gdf = disaster_gdf.copy()
|
18 |
+
Region_tot_ave = Region_gdf.drop(columns=['geometry']).groupby('Region').sum()
|
19 |
+
def region_count(disaster_type,col_name):
|
20 |
+
Region_gdf[col_name] = np.nan #Create a new column that contains the number of disasters per region
|
21 |
+
|
22 |
+
for i1,r1 in Region_gdf.iterrows():
|
23 |
+
for i2,r2 in Region_tot_ave.iterrows():
|
24 |
+
if r1['Region'] == i2:
|
25 |
+
Region_gdf.at[i1, col_name] = r2[disaster_type]
|
26 |
+
else:
|
27 |
+
continue
|
28 |
+
region_count('Total Disaster Count','Region_tot')
|
29 |
+
region_count('Storm Count','Region_storm')
|
30 |
+
region_count('Flood Count','Region_flood')
|
31 |
+
region_count('Earthquake Count','Region_earth')
|
32 |
+
region_count('Volcanic Activity Count','Region_vol')
|
33 |
+
region_count('Mass Movement Count','Region_mass')
|
34 |
+
region_count('Drought Count','Region_drought')
|
35 |
+
|
36 |
+
melt_value = temperature_gdf.drop(columns=[col for col in temperature_gdf.columns if 'TempDiff' in col])
|
37 |
+
melt_value.columns = [col.split('_')[0] if '_value' in col else col for col in melt_value.columns]
|
38 |
+
melt_value = melt_value.melt(id_vars=['name', 'geometry', 'admin_div', 'island_group', 'Region'],
|
39 |
+
var_name='decade',
|
40 |
+
value_name='value')
|
41 |
+
melt_tempdiff = temperature_gdf.drop(columns=[col for col in temperature_gdf.columns if 'value' in col])
|
42 |
+
melt_tempdiff.columns = [col.split('_')[0] if '_TempDiff' in col else col for col in melt_tempdiff.columns]
|
43 |
+
melt_tempdiff = melt_tempdiff.melt(id_vars=['name', 'geometry', 'admin_div', 'island_group', 'Region'],
|
44 |
+
var_name='decade',
|
45 |
+
value_name='TempDiff')
|
46 |
+
temp_melted_gdf = pd.merge(melt_value, melt_tempdiff, on=['name', 'geometry', 'admin_div', 'island_group', 'Region', 'decade'])
|
47 |
+
|
48 |
+
# Initialize Page
|
49 |
+
register_page(__name__, path='/disaster', name='Disaster', title='Klima Insights | Disaster')
|
50 |
+
|
51 |
+
layout = dbc.Container(className="d-flex justify-content-center align-items-center full-width my-3 z-3", fluid=True, children=[
|
52 |
+
dbc.Row(className="align-items-stretch", children=[
|
53 |
+
dbc.Col(className="bg-light rounded z-3 d-flex flex-col justify-content-center align-items-center", width=12, md=4, children=[
|
54 |
+
html.Div(className='full-width-container text-dark', children=[
|
55 |
+
html.Div(className='d-flex flex-row justify-content-start align-items-center mt-2 flex-gap-20', children=[
|
56 |
+
html.H5(className='mt-1', children=["Divide By: "]),
|
57 |
+
dcc.RadioItems(id='division-radio', options=['Region', 'Province'], value='Region', inline=True, labelStyle={"margin-right": "20px"})
|
58 |
+
]),
|
59 |
+
html.H4(className="mt-2", children=[
|
60 |
+
"Unveiling the Interplay of Temperature and Disaster Vulnerability in the Philippines"
|
61 |
+
]),
|
62 |
+
html.P(className="", children=[
|
63 |
+
"The Philippines, situated along the Pacific Ring of Fire, has long been highly susceptible to a range of disasters, including seismic and volcanic events. However, it's crucial to recognize that the nation's vulnerability to such calamities is not solely attributable to its geographical location. By utilizing the dropdown menu provided, one can explore the occurrences of each disaster type. Furthermore, clicking on specific areas in the map facilitates a deeper visualization of temperature changes in those regions."
|
64 |
+
]),
|
65 |
+
dcc.Loading(type="circle", children=[dcc.Graph(id="disaster-line")])
|
66 |
+
])
|
67 |
+
]),
|
68 |
+
dbc.Col(className="rounded z-3 disaster-map-height overflow-hidden", width=12, md=8, children=[
|
69 |
+
html.Div(className="text-dark z-3 align-self-start", children=[
|
70 |
+
html.Div(className='d-flex flex-row justify-content-between align-items-center', children=[
|
71 |
+
html.Div(className='w-100', children=[
|
72 |
+
dcc.Dropdown(options=['Total Disaster', 'Storm', 'Flood', 'Earthquake', 'Volcanic Activity', 'Mass Movement', 'Drought'], value='Total Disaster', id='disaster-type-dropdown', multi=False, searchable=False, clearable=False)
|
73 |
+
]),
|
74 |
+
dbc.Button("Compare", color="primary z-3", id="open-disaster-modal", n_clicks=0)
|
75 |
+
]),
|
76 |
+
html.Div(children=[
|
77 |
+
dcc.Loading(type="circle", children=[dcc.Graph(id="disaster-map")])
|
78 |
+
])
|
79 |
+
])
|
80 |
+
]),
|
81 |
+
dbc.Modal(
|
82 |
+
[
|
83 |
+
dbc.ModalHeader(dbc.ModalTitle(className="text-secondary", children=["Disaster Count per Area"])),
|
84 |
+
dbc.ModalBody(children=[
|
85 |
+
dbc.Row(children=[
|
86 |
+
dbc.Col(width=12, md=4, children=[
|
87 |
+
html.Div(children=[
|
88 |
+
html.H4(className="mt-2 text-light", children=[
|
89 |
+
"Deeper Insights into Disaster Trends"
|
90 |
+
]),
|
91 |
+
html.P(className="text-light", children=[
|
92 |
+
"Taking a closer look, we can compare the frequency of disasters in each province or region within a specific island group using the bar graph provided on the right. Noticing the disparity in disaster occurrences among different areas raises awareness of the necessity to take proactive measures, regardless of their scale, to mitigate and minimize future disasters."
|
93 |
+
]),
|
94 |
+
])
|
95 |
+
]),
|
96 |
+
dbc.Col(width=12, md=8, children=[
|
97 |
+
dcc.Dropdown(options=['Luzon', 'Visayas', 'Mindanao'], value='Luzon', id='disaster-bar-dropdown',
|
98 |
+
multi=False, searchable=False, clearable=False),
|
99 |
+
dcc.Loading(type="circle", children=[dcc.Graph(id="disaster-bar")])
|
100 |
+
])
|
101 |
+
])
|
102 |
+
]),
|
103 |
+
],
|
104 |
+
id="disaster-modal",
|
105 |
+
size="xl",
|
106 |
+
is_open=False,
|
107 |
+
)
|
108 |
+
])
|
109 |
+
])
|
110 |
+
|
111 |
+
# Compare Modal
|
112 |
+
@callback(
|
113 |
+
Output("disaster-modal", "is_open"),
|
114 |
+
Input("open-disaster-modal", "n_clicks"),
|
115 |
+
State("disaster-modal", "is_open"),
|
116 |
+
)
|
117 |
+
def toggle_modal(n1, is_open):
|
118 |
+
if n1:
|
119 |
+
return not is_open
|
120 |
+
return is_open
|
121 |
+
|
122 |
+
# Click Data
|
123 |
+
@callback(
|
124 |
+
Output("disaster-line", "figure"),
|
125 |
+
[Input('division-radio', 'value'), Input("disaster-map", "clickData")]
|
126 |
+
)
|
127 |
+
def update_line(division, click_data):
|
128 |
+
if click_data is not None:
|
129 |
+
data = click_data['points'][0]['customdata'][0]
|
130 |
+
else:
|
131 |
+
match division:
|
132 |
+
case 'Region':
|
133 |
+
data = 'RegionI'
|
134 |
+
case 'Province':
|
135 |
+
data = 'Abra'
|
136 |
+
|
137 |
+
curr_div = ''
|
138 |
+
if division == 'Region':
|
139 |
+
curr_div = 'Region'
|
140 |
+
elif division == 'Province':
|
141 |
+
curr_div = 'name'
|
142 |
+
else:
|
143 |
+
return
|
144 |
+
|
145 |
+
island_gdf = temp_melted_gdf[(temp_melted_gdf[curr_div].isin([data]) == True)].drop(columns=['geometry'])
|
146 |
+
|
147 |
+
line_fig = px.line(island_gdf, x='decade', y='value',color='name')
|
148 |
+
line_fig.update_layout(
|
149 |
+
autosize=True,
|
150 |
+
height=500,
|
151 |
+
title='Change in Avg Temperature in ' + data,
|
152 |
+
yaxis=dict(
|
153 |
+
range=[25, 32],
|
154 |
+
tickmode='linear',
|
155 |
+
dtick=0.5
|
156 |
+
),
|
157 |
+
margin=dict(l=20, r=20, t=100, b=100),
|
158 |
+
updatemenus=[{
|
159 |
+
'direction': 'left',
|
160 |
+
'pad': {'t': 0, 'b': 0, 'l': 0, 'r': 0},
|
161 |
+
'showactive': False,
|
162 |
+
'type': 'buttons',
|
163 |
+
'x': 0.06,
|
164 |
+
'xanchor': 'right',
|
165 |
+
'y': -0.46,
|
166 |
+
'yanchor': 'top'
|
167 |
+
}],
|
168 |
+
xaxis_tickangle=-45
|
169 |
+
)
|
170 |
+
hover_template = '<b>' + data + '</b><br>Average Temperature in<br>the %{x}:<br>%{y:.2f}°C<extra></extra>'
|
171 |
+
line_fig.update_traces(hovertemplate=hover_template)
|
172 |
+
|
173 |
+
return line_fig
|
174 |
+
|
175 |
+
# Map Figure
|
176 |
+
@callback(
|
177 |
+
Output('disaster-map', 'figure'),
|
178 |
+
[Input('division-radio', 'value'), Input('disaster-type-dropdown', 'value')]
|
179 |
+
)
|
180 |
+
def update_map(division, disaster_type):
|
181 |
+
curr_division = ''
|
182 |
+
curr_disaster = ''
|
183 |
+
if division == 'Region':
|
184 |
+
curr_division = 'Region'
|
185 |
+
match disaster_type:
|
186 |
+
case 'Total Disaster':
|
187 |
+
curr_disaster = 'Region_tot'
|
188 |
+
case 'Storm':
|
189 |
+
curr_disaster = 'Region_storm'
|
190 |
+
case 'Flood':
|
191 |
+
curr_disaster = 'Region_flood'
|
192 |
+
case 'Earthquake':
|
193 |
+
curr_disaster = 'Region_earth'
|
194 |
+
case 'Volcanic Activity':
|
195 |
+
curr_disaster = 'Region_vol'
|
196 |
+
case 'Mass Movement':
|
197 |
+
curr_disaster = 'Region_mass'
|
198 |
+
case 'Drought':
|
199 |
+
curr_disaster = 'Region_drought'
|
200 |
+
case _:
|
201 |
+
return
|
202 |
+
elif division == 'Province':
|
203 |
+
curr_division = 'Area Name'
|
204 |
+
match disaster_type:
|
205 |
+
case 'Total Disaster':
|
206 |
+
curr_disaster = 'Total Disaster Count'
|
207 |
+
case 'Storm':
|
208 |
+
curr_disaster = 'Storm Count'
|
209 |
+
case 'Flood':
|
210 |
+
curr_disaster = 'Flood Count'
|
211 |
+
case 'Earthquake':
|
212 |
+
curr_disaster = 'Earthquake Count'
|
213 |
+
case 'Volcanic Activity':
|
214 |
+
curr_disaster = 'Volcanic Activity Count'
|
215 |
+
case 'Mass Movement':
|
216 |
+
curr_disaster = 'Mass Movement Count'
|
217 |
+
case 'Drought':
|
218 |
+
curr_disaster = 'Drought Count'
|
219 |
+
case _:
|
220 |
+
return
|
221 |
+
else:
|
222 |
+
return
|
223 |
+
|
224 |
+
map_fig = px.choropleth_mapbox(Region_gdf,
|
225 |
+
height=845,
|
226 |
+
geojson=Region_gdf.geometry,
|
227 |
+
locations=Region_gdf.index,
|
228 |
+
color=curr_disaster, # Change based on dropdown value
|
229 |
+
color_continuous_scale='amp',
|
230 |
+
range_color=[Region_gdf[curr_disaster].min(), Region_gdf[curr_disaster].max()],
|
231 |
+
mapbox_style='streets',
|
232 |
+
zoom=5,
|
233 |
+
center={"lat": 12.8797, "lon": 122.7740},
|
234 |
+
opacity=0.6,
|
235 |
+
)
|
236 |
+
|
237 |
+
map_fig.update_layout(margin=dict(l=0, r=0, t=0, b=0))
|
238 |
+
|
239 |
+
map_fig.update_layout(coloraxis_colorbar=dict(title= disaster_type + "<br>Count",yanchor="top",xanchor='left',
|
240 |
+
y=1, x=0, ticks="outside", ticklabelposition="outside left", thickness=10, title_font_color='#0c232c',
|
241 |
+
tickfont=dict(
|
242 |
+
size=12,
|
243 |
+
color='#0c232c')
|
244 |
+
)
|
245 |
+
)
|
246 |
+
|
247 |
+
hover_template = '<b>%{customdata[0]}</b><br>' + disaster_type + ' Count: %{customdata[1]:.0f}<extra></extra>'
|
248 |
+
|
249 |
+
map_fig.update_traces(hovertemplate=hover_template,
|
250 |
+
customdata=Region_gdf[[curr_division, curr_disaster]])
|
251 |
+
|
252 |
+
return map_fig
|
253 |
+
|
254 |
+
# Bar Figure
|
255 |
+
@callback(
|
256 |
+
Output('disaster-bar', 'figure'),
|
257 |
+
[Input('division-radio', 'value'), Input('disaster-type-dropdown', 'value'), Input('disaster-bar-dropdown', 'value')]
|
258 |
+
)
|
259 |
+
def update_disaster_bar(division, disaster_type, island_group):
|
260 |
+
curr_division = ''
|
261 |
+
curr_disaster = ''
|
262 |
+
if division == 'Region':
|
263 |
+
curr_division = 'Region'
|
264 |
+
match disaster_type:
|
265 |
+
case 'Total Disaster':
|
266 |
+
curr_disaster = 'Region_tot'
|
267 |
+
case 'Storm':
|
268 |
+
curr_disaster = 'Region_storm'
|
269 |
+
case 'Flood':
|
270 |
+
curr_disaster = 'Region_flood'
|
271 |
+
case 'Earthquake':
|
272 |
+
curr_disaster = 'Region_earth'
|
273 |
+
case 'Volcanic Activity':
|
274 |
+
curr_disaster = 'Region_vol'
|
275 |
+
case 'Mass Movement':
|
276 |
+
curr_disaster = 'Region_mass'
|
277 |
+
case 'Drought':
|
278 |
+
curr_disaster = 'Region_drought'
|
279 |
+
case _:
|
280 |
+
return
|
281 |
+
island_disaster = Region_gdf[Region_gdf['Island Group'] == island_group].groupby('Region')[curr_disaster].sum().sort_values(ascending=True)
|
282 |
+
x = island_disaster.values
|
283 |
+
y = island_disaster.index
|
284 |
+
elif division == 'Province':
|
285 |
+
curr_division = 'Area Name'
|
286 |
+
match disaster_type:
|
287 |
+
case 'Total Disaster':
|
288 |
+
curr_disaster = 'Total Disaster Count'
|
289 |
+
case 'Storm':
|
290 |
+
curr_disaster = 'Storm Count'
|
291 |
+
case 'Flood':
|
292 |
+
curr_disaster = 'Flood Count'
|
293 |
+
case 'Earthquake':
|
294 |
+
curr_disaster = 'Earthquake Count'
|
295 |
+
case 'Volcanic Activity':
|
296 |
+
curr_disaster = 'Volcanic Activity Count'
|
297 |
+
case 'Mass Movement':
|
298 |
+
curr_disaster = 'Mass Movement Count'
|
299 |
+
case 'Drought':
|
300 |
+
curr_disaster = 'Drought Count'
|
301 |
+
case _:
|
302 |
+
return
|
303 |
+
island_disaster = Region_gdf[(Region_gdf['Island Group'] == island_group)].sort_values(by='Total Disaster Count', ascending=True)
|
304 |
+
x = curr_disaster
|
305 |
+
y = curr_division
|
306 |
+
else:
|
307 |
+
return
|
308 |
+
# Create stacked bar plot using Plotly Express
|
309 |
+
bar_fig = px.bar(island_disaster,
|
310 |
+
x=x,
|
311 |
+
y=y,
|
312 |
+
title=disaster_type + " in " + island_group + " per " + division,
|
313 |
+
orientation='h',
|
314 |
+
height=750,
|
315 |
+
color_discrete_sequence=['lightblue'],
|
316 |
+
)
|
317 |
+
bar_fig.update_layout(
|
318 |
+
xaxis_title=disaster_type,
|
319 |
+
yaxis_title=division,
|
320 |
+
)
|
321 |
+
hover_template = '<b>%{customdata[0]}</b><br>' + disaster_type + ' Count: %{x}<extra></extra>'
|
322 |
+
bar_fig.update_traces(hovertemplate=hover_template,
|
323 |
+
customdata=Region_gdf[[curr_division]])
|
324 |
+
|
325 |
+
return bar_fig
|
klimainsights/pages/initial.py
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Setup Folders, Tokens, and Dependencies
|
2 |
+
from dash import html, register_page
|
3 |
+
import dash_bootstrap_components as dbc
|
4 |
+
|
5 |
+
# Initialize Page
|
6 |
+
register_page(__name__, path='/', name='Start', title='Klima Insights')
|
7 |
+
|
8 |
+
layout = dbc.Container(className='d-flex justify-content-center align-items-center full-height full-width py-5 z-3', fluid=True, children=[
|
9 |
+
html.Div(className='card', children=[
|
10 |
+
html.Strong(className="text-primary", children=["Are You Ready to Uncover the Truth about Climate Change in the Philippines?"]),
|
11 |
+
html.Div(className='card__body', children=[
|
12 |
+
html.P(
|
13 |
+
"Delve into the heart of environmental change with Klima Insights. Our dynamic dashboard presents "
|
14 |
+
"a comprehensive overview of the climate's impact on the Philippines, revealing temperature increase over the decades, "
|
15 |
+
"biodiversity shifts, and the frequency of disasters."
|
16 |
+
),
|
17 |
+
html.P(
|
18 |
+
"Do you know how climate change is reshaping the Philippines? Join us as we uncover the hidden patterns, "
|
19 |
+
"discover the amount of endangered species struggling to survive, and witness the escalating disasters "
|
20 |
+
"threatening our communities. The time to act is now. Together, let's shape a sustainable future for generations to come."
|
21 |
+
),
|
22 |
+
html.Div(className="container-fluid d-flex justify-content-center scale-button",children=[
|
23 |
+
dbc.Button("Explore Climate History", color="info", href="/temperature", className="mt-3")
|
24 |
+
])
|
25 |
+
]),
|
26 |
+
html.Span(children=[
|
27 |
+
html.Div(className="container-fluid d-flex justify-content-center",children=[
|
28 |
+
dbc.Button("Explore Climate History", color="info", href="/temperature", className="mt-3")
|
29 |
+
])
|
30 |
+
])
|
31 |
+
])
|
32 |
+
])
|
klimainsights/pages/temperature.py
ADDED
@@ -0,0 +1,310 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Setup Folders, Tokens, and Dependencies
|
2 |
+
from dash import html, dcc, callback, Output, Input, State, register_page
|
3 |
+
import dash_bootstrap_components as dbc
|
4 |
+
import plotly.express as px
|
5 |
+
import pandas as pd
|
6 |
+
import geopandas as gpd
|
7 |
+
from pathlib import Path
|
8 |
+
from environment.settings import MAPBOX_TOKEN
|
9 |
+
import dash_daq as daq
|
10 |
+
|
11 |
+
datasets_folder = Path('./data')
|
12 |
+
px.set_mapbox_access_token(MAPBOX_TOKEN)
|
13 |
+
|
14 |
+
# Import Data
|
15 |
+
temperature_gdf = gpd.read_file(datasets_folder / "temperature.geojson")
|
16 |
+
def remove_value(string):
|
17 |
+
return string[:-6] if string.endswith('_value') else string
|
18 |
+
|
19 |
+
melt_value = temperature_gdf.drop(columns=[col for col in temperature_gdf.columns if 'TempDiff' in col])
|
20 |
+
melt_value.columns = [col.split('_')[0] if '_value' in col else col for col in melt_value.columns]
|
21 |
+
melt_value = melt_value.melt(id_vars=['name', 'geometry', 'admin_div', 'island_group', 'Region'],
|
22 |
+
var_name='decade',
|
23 |
+
value_name='value')
|
24 |
+
melt_tempdiff = temperature_gdf.drop(columns=[col for col in temperature_gdf.columns if 'value' in col])
|
25 |
+
melt_tempdiff.columns = [col.split('_')[0] if '_TempDiff' in col else col for col in melt_tempdiff.columns]
|
26 |
+
melt_tempdiff = melt_tempdiff.melt(id_vars=['name', 'geometry', 'admin_div', 'island_group', 'Region'],
|
27 |
+
var_name='decade',
|
28 |
+
value_name='TempDiff')
|
29 |
+
temp_melted_gdf = pd.merge(melt_value, melt_tempdiff, on=['name', 'geometry', 'admin_div', 'island_group', 'Region', 'decade'])
|
30 |
+
|
31 |
+
# Initialize Page
|
32 |
+
register_page(__name__, path='/temperature', name='Temperature', title='Klima Insights | Temperature')
|
33 |
+
|
34 |
+
layout = dbc.Container(className="d-flex justify-content-center align-items-center full-height full-width my-3 z-3", fluid=True, children=[
|
35 |
+
dbc.Row(children=[
|
36 |
+
dbc.Col(className="bg-light rounded z-3", width=12, md=4, children=[
|
37 |
+
html.Div(className="full-width-container text-dark", children=[
|
38 |
+
html.H3(className="mt-2", children=[
|
39 |
+
"Charting the Climate Shift: Examining Temperature Trends in the Philippines Across Decades"
|
40 |
+
]),
|
41 |
+
html.P(children=[
|
42 |
+
"Delving into the historical records reveals a subtle yet discernible shift in the climate dynamics of the Philippines. From the relatively mild conditions of the 1960s to the present-day realities of the 2020s, there's a noticeable uptick in temperature. While the increase may not be drastic, it remains a cause for concern and warrants careful observation. What was once a region known for its moderate temperatures has gradually warmed, signaling a shift that demands attention. Understanding these incremental changes is essential in navigating the evolving climate landscape and implementing effective measures to mitigate their impact."
|
43 |
+
]),
|
44 |
+
html.Div(className="container-fluid d-flex justify-content-center my-3",children=[
|
45 |
+
dbc.Button("Compare Provinces", color="primary", id="open-temp-modal", n_clicks=0)
|
46 |
+
]),
|
47 |
+
dbc.Modal(
|
48 |
+
[
|
49 |
+
dbc.ModalHeader(dbc.ModalTitle(className="text-secondary", children=["Average Temperature per Province"])),
|
50 |
+
dbc.ModalBody(children=[
|
51 |
+
dbc.Row(children=[
|
52 |
+
dbc.Col(width=12, md=4, children=[
|
53 |
+
html.Div(children=[
|
54 |
+
html.H4(className="mt-2 text-light", children=[
|
55 |
+
" Unraveling Temperature Trends Across Philippine Provinces from the 1960s to the 2020s"
|
56 |
+
]),
|
57 |
+
html.P(className="text-light", children=[
|
58 |
+
"This section delves into the temperature data per province across the Philippines spanning from the 1960s to the 2020s. Upon closer inspection, a discernible pattern emerges, indicating a modest uptick in temperatures as the decades progress. While the increase may seem subtle, it's a notable phenomenon worthy of attention. The comparative visualization reveals a trend reflective of broader climate shifts, hinting at the ongoing environmental changes affecting the nation's provinces. These findings underscore the importance of monitoring and understanding regional temperature variations, as they hold implications for both local communities and broader climate resilience efforts."
|
59 |
+
]),
|
60 |
+
daq.BooleanSwitch(
|
61 |
+
id='temp-bar-switch',
|
62 |
+
on=False,
|
63 |
+
color="#b58900",
|
64 |
+
label="Show Avg Temp Increase Relative to 1960s"
|
65 |
+
)
|
66 |
+
])
|
67 |
+
]),
|
68 |
+
dbc.Col(className="temp-bar-container", width=12, md=8, children=[
|
69 |
+
dcc.Dropdown(options=['Luzon', 'Visayas', 'Mindanao'], value='Luzon', id='temp-bar-dropdown',
|
70 |
+
multi=False, searchable=False, clearable=False),
|
71 |
+
dcc.Loading(type="circle", children=[dcc.Graph(id="temp-bar")])
|
72 |
+
])
|
73 |
+
])
|
74 |
+
]),
|
75 |
+
],
|
76 |
+
id="temp-modal",
|
77 |
+
size="xl",
|
78 |
+
is_open=False,
|
79 |
+
),
|
80 |
+
])
|
81 |
+
]),
|
82 |
+
dbc.Col(className="rounded", width=12, md=8, children=[
|
83 |
+
html.Div(className="text-dark", children=[
|
84 |
+
dcc.Dropdown(options=[{'label': '1960s', 'value': '1960s_value'},
|
85 |
+
{'label': '1970s', 'value': '1970s_value'},
|
86 |
+
{'label': '1980s', 'value': '1980s_value'},
|
87 |
+
{'label': '1990s', 'value': '1990s_value'},
|
88 |
+
{'label': '2000s', 'value': '2000s_value'},
|
89 |
+
{'label': '2010s', 'value': '2010s_value'},
|
90 |
+
{'label': '2020s', 'value': '2020s_value'}],
|
91 |
+
value='1960s_value', id='temp-map-dropdown',
|
92 |
+
multi=False, searchable=False, clearable=False),
|
93 |
+
dcc.Loading(type="circle", children=[dcc.Graph(id="temp-map", responsive=True)])
|
94 |
+
])
|
95 |
+
])
|
96 |
+
])
|
97 |
+
])
|
98 |
+
|
99 |
+
# Compare Modal
|
100 |
+
@callback(
|
101 |
+
Output("temp-modal", "is_open"),
|
102 |
+
Input("open-temp-modal", "n_clicks"),
|
103 |
+
State("temp-modal", "is_open"),
|
104 |
+
)
|
105 |
+
def toggle_modal(n1, is_open):
|
106 |
+
if n1:
|
107 |
+
return not is_open
|
108 |
+
return is_open
|
109 |
+
|
110 |
+
# Bar Figure
|
111 |
+
@callback(
|
112 |
+
Output('temp-bar', 'figure'),
|
113 |
+
[Input('temp-bar-dropdown', 'value'),Input('temp-bar-switch', 'on')]
|
114 |
+
)
|
115 |
+
def update_bar_fig(island_value, switch):
|
116 |
+
if switch:
|
117 |
+
island_gdf = temp_melted_gdf[(temp_melted_gdf['island_group'].isin([island_value])) & (temp_melted_gdf['decade'].isin(['1960s']) == False)].drop(columns=['geometry'])
|
118 |
+
# Create the Figure with horizontal orientation
|
119 |
+
bar1960_fig = px.bar(island_gdf, y='name', x='TempDiff', animation_frame="decade", orientation='h')
|
120 |
+
bar1960_fig.update_layout(
|
121 |
+
height=750,
|
122 |
+
title=f'Average Temperature Increase Relative to 1960s<br>Across {island_value} Provinces',
|
123 |
+
margin=dict(l=20, r=20, t=75, b=50),
|
124 |
+
yaxis=dict(
|
125 |
+
title="Province Name",
|
126 |
+
tickfont=dict(size=11) # Adjust tick font size to prevent overlap
|
127 |
+
),
|
128 |
+
xaxis=dict(
|
129 |
+
title="Temperature (°C)",
|
130 |
+
range=[0, 1.5],
|
131 |
+
tickmode='linear',
|
132 |
+
dtick=0.3,
|
133 |
+
tickfont=dict(size=13), # Adjust tick font size to prevent overlap
|
134 |
+
tickangle=0
|
135 |
+
),
|
136 |
+
font=dict( # Adjust font size for main title
|
137 |
+
size=12
|
138 |
+
)
|
139 |
+
)
|
140 |
+
hover_template = "Avg Temperature Increase<br>" + \
|
141 |
+
"Relative to 1960s<br>" + \
|
142 |
+
"<b>in %{y}</b><br>" + \
|
143 |
+
"during the %{customdata[0]}:<br>" + \
|
144 |
+
"%{customdata[1]:.2f}°C"
|
145 |
+
bar1960_fig.update_traces(hovertemplate=hover_template,
|
146 |
+
customdata=island_gdf[['decade', 'value']])
|
147 |
+
|
148 |
+
for frame in bar1960_fig.frames:
|
149 |
+
frame.data[0].hovertemplate = hover_template
|
150 |
+
frame.data[0].customdata = island_gdf[['decade', 'value']]
|
151 |
+
bar1960_fig.update_layout(
|
152 |
+
updatemenus=[{
|
153 |
+
'direction': 'left',
|
154 |
+
'pad': {'t': 10, 'b': 10, 'l': 10, 'r': 10},
|
155 |
+
'showactive': False,
|
156 |
+
'type': 'buttons',
|
157 |
+
'x': 0.06,
|
158 |
+
'xanchor': 'right',
|
159 |
+
'y': -0.14,
|
160 |
+
'yanchor': 'top'
|
161 |
+
}],
|
162 |
+
sliders=[{
|
163 |
+
'active': 0,
|
164 |
+
'x': 0.98,
|
165 |
+
'y': -0.08,
|
166 |
+
'xanchor': 'right',
|
167 |
+
'yanchor': 'top',
|
168 |
+
'transition': {'duration': 300, 'easing': 'cubic-in-out'},
|
169 |
+
'pad': {'t': 10, 'b': 10, 'l': 10, 'r': 10},
|
170 |
+
'currentvalue': {
|
171 |
+
'font': {'size': 15},
|
172 |
+
'prefix': 'Decade:',
|
173 |
+
'visible': True,
|
174 |
+
'xanchor': 'right',
|
175 |
+
},
|
176 |
+
'visible': True,
|
177 |
+
}]
|
178 |
+
)
|
179 |
+
return bar1960_fig
|
180 |
+
|
181 |
+
else:
|
182 |
+
island_gdf = temp_melted_gdf[(temp_melted_gdf['island_group'].isin([island_value]) == True)].drop(columns=['geometry'])
|
183 |
+
# Create the Figure with horizontal orientation
|
184 |
+
bar_fig = px.bar(island_gdf, y='name', x='value', animation_frame="decade", orientation='h')
|
185 |
+
bar_fig.update_layout(
|
186 |
+
height=750,
|
187 |
+
title=f'Average Temperature per Province<br>in {island_value}',
|
188 |
+
margin=dict(l=20, r=20, t=75, b=50),
|
189 |
+
yaxis=dict(
|
190 |
+
title="Province Name",
|
191 |
+
tickfont=dict(size=11) # Adjust tick font size to prevent overlap
|
192 |
+
),
|
193 |
+
xaxis=dict(
|
194 |
+
title="Avg Temperature (°C)",
|
195 |
+
range=[0, 35],
|
196 |
+
tickmode='linear',
|
197 |
+
dtick=5,
|
198 |
+
tickfont=dict(size=13), # Adjust tick font size to prevent overlap
|
199 |
+
tickangle=0
|
200 |
+
),
|
201 |
+
font=dict( # Adjust font size for main title
|
202 |
+
size=12
|
203 |
+
)
|
204 |
+
)
|
205 |
+
hover_template = "Average Temperature in<br>" + \
|
206 |
+
"<b>%{y}</b><br>" + \
|
207 |
+
"during the %{customdata[0]}:<br>" + \
|
208 |
+
"%{customdata[1]:.2f}°C"
|
209 |
+
bar_fig.update_traces(hovertemplate=hover_template,
|
210 |
+
customdata=island_gdf[['decade', 'value']])
|
211 |
+
|
212 |
+
for frame in bar_fig.frames:
|
213 |
+
frame.data[0].hovertemplate = hover_template
|
214 |
+
frame.data[0].customdata = island_gdf[['decade', 'value']]
|
215 |
+
bar_fig.update_layout(
|
216 |
+
updatemenus=[{
|
217 |
+
'direction': 'left',
|
218 |
+
'pad': {'t': 10, 'b': 10, 'l': 10, 'r': 10},
|
219 |
+
'showactive': False,
|
220 |
+
'type': 'buttons',
|
221 |
+
'x': 0.06,
|
222 |
+
'xanchor': 'right',
|
223 |
+
'y': -0.14,
|
224 |
+
'yanchor': 'top'
|
225 |
+
}],
|
226 |
+
sliders=[{
|
227 |
+
'active': 0,
|
228 |
+
'x': 0.98,
|
229 |
+
'y': -0.08,
|
230 |
+
'xanchor': 'right',
|
231 |
+
'yanchor': 'top',
|
232 |
+
'transition': {'duration': 300, 'easing': 'cubic-in-out'},
|
233 |
+
'pad': {'t': 10, 'b': 10, 'l': 10, 'r': 10},
|
234 |
+
'currentvalue': {
|
235 |
+
'font': {'size': 15},
|
236 |
+
'prefix': 'Decade:',
|
237 |
+
'visible': True,
|
238 |
+
'xanchor': 'right',
|
239 |
+
},
|
240 |
+
'visible': True,
|
241 |
+
}]
|
242 |
+
)
|
243 |
+
|
244 |
+
highest_temp_1960 = island_gdf[island_gdf['decade'] == '1960s']['value'].max() # Highest temperature for the decade 1960
|
245 |
+
bar_fig.add_shape( # Line representing highest temperature in the 1960s
|
246 |
+
type="line",
|
247 |
+
x0=highest_temp_1960,
|
248 |
+
y0=-1,
|
249 |
+
x1=highest_temp_1960,
|
250 |
+
y1=island_gdf.name.nunique()-0.5,
|
251 |
+
line=dict(
|
252 |
+
color="red",
|
253 |
+
width=1,
|
254 |
+
dash="dash",
|
255 |
+
),
|
256 |
+
)
|
257 |
+
|
258 |
+
bar_fig.add_annotation(
|
259 |
+
x=highest_temp_1960 + 1,
|
260 |
+
y=island_gdf.name.nunique() / 1.3, # Adjust the y position of the label
|
261 |
+
text="Highest Temp in the 1960s",
|
262 |
+
showarrow=True,
|
263 |
+
arrowhead=1,
|
264 |
+
ax=0,
|
265 |
+
ay=0, # Adjust the arrow position
|
266 |
+
font=dict(
|
267 |
+
color="red",
|
268 |
+
size=12
|
269 |
+
),
|
270 |
+
align="left",
|
271 |
+
textangle=90 # Tilt the text 90 degrees
|
272 |
+
)
|
273 |
+
|
274 |
+
return bar_fig
|
275 |
+
|
276 |
+
# Map Figure
|
277 |
+
@callback(
|
278 |
+
Output('temp-map', 'figure'),
|
279 |
+
Input('temp-map-dropdown', 'value')
|
280 |
+
)
|
281 |
+
def update_map_fig(decade_value):
|
282 |
+
map_fig = px.choropleth_mapbox(temperature_gdf,
|
283 |
+
geojson=temperature_gdf.geometry,
|
284 |
+
locations=temperature_gdf.index,
|
285 |
+
color=decade_value,
|
286 |
+
color_continuous_scale='turbo',
|
287 |
+
range_color=[25, 33],
|
288 |
+
mapbox_style='streets',
|
289 |
+
zoom=5,
|
290 |
+
center={"lat": 12.8797, "lon": 122.7740},
|
291 |
+
opacity=0.6,
|
292 |
+
)
|
293 |
+
map_fig.update_layout(
|
294 |
+
coloraxis_colorbar=dict(title=f"{remove_value(decade_value)}<br>Average<br>Temperature(°C)", yanchor="top", xanchor='left',
|
295 |
+
y=1, x=0, ticks="outside", ticklabelposition="outside left", thickness=10, title_font_color='#0c232c',
|
296 |
+
tickvals=[i for i in range(25, 33)],
|
297 |
+
tickmode='array',
|
298 |
+
ticksuffix='°C',
|
299 |
+
tickfont=dict(
|
300 |
+
size=12,
|
301 |
+
color='#0c232c')
|
302 |
+
)
|
303 |
+
)
|
304 |
+
map_fig.update_layout(
|
305 |
+
margin=dict(l=0, r=0, t=0, b=0),
|
306 |
+
)
|
307 |
+
hover_template = '<b>%{customdata[0]}</b><br>during the '+ remove_value(decade_value) +'<br>Average Temp: %{customdata[1]:.2f}°C<extra></extra>'
|
308 |
+
map_fig.update_traces(hovertemplate=hover_template,
|
309 |
+
customdata=temperature_gdf[['name', decade_value]])
|
310 |
+
return map_fig
|
plotly_interactions.ipynb
ADDED
@@ -0,0 +1,1101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"cells": [
|
3 |
+
{
|
4 |
+
"cell_type": "code",
|
5 |
+
"execution_count": null,
|
6 |
+
"metadata": {},
|
7 |
+
"outputs": [],
|
8 |
+
"source": [
|
9 |
+
"# Setup Folders, Tokens, and Dependencies\n",
|
10 |
+
"import numpy as np\n",
|
11 |
+
"import pandas as pd\n",
|
12 |
+
"import geopandas as gpd\n",
|
13 |
+
"import plotly.express as px\n",
|
14 |
+
"from pathlib import Path\n",
|
15 |
+
"from klimainsights.environment.settings import MAPBOX_TOKEN\n",
|
16 |
+
"\n",
|
17 |
+
"datasets_folder = Path('./klimainsights/data')\n",
|
18 |
+
"px.set_mapbox_access_token(MAPBOX_TOKEN)"
|
19 |
+
]
|
20 |
+
},
|
21 |
+
{
|
22 |
+
"cell_type": "markdown",
|
23 |
+
"metadata": {},
|
24 |
+
"source": [
|
25 |
+
"# Loading the Data"
|
26 |
+
]
|
27 |
+
},
|
28 |
+
{
|
29 |
+
"cell_type": "code",
|
30 |
+
"execution_count": null,
|
31 |
+
"metadata": {},
|
32 |
+
"outputs": [],
|
33 |
+
"source": [
|
34 |
+
"temperature_gdf = gpd.read_file(datasets_folder / \"temperature.geojson\")\n",
|
35 |
+
"biodiversity_gdf = gpd.read_file(datasets_folder / 'biodiversity.geojson')\n",
|
36 |
+
"disaster_gdf = gpd.read_file(datasets_folder / 'disaster.geojson')"
|
37 |
+
]
|
38 |
+
},
|
39 |
+
{
|
40 |
+
"cell_type": "markdown",
|
41 |
+
"metadata": {},
|
42 |
+
"source": [
|
43 |
+
"**APP IDEAS**\n",
|
44 |
+
"- The initial page will an animated of a short text on the center that catches the viewers' attention. It can be a call to action or a hook to incite curiosity.\n",
|
45 |
+
"- Below the Text will be a button to \"Click to Learn More\" or \"Continue\", leading to the Temperature page\n",
|
46 |
+
"- All pages will contain a header/nav that contains an offcanvas for easier navigation between pages"
|
47 |
+
]
|
48 |
+
},
|
49 |
+
{
|
50 |
+
"cell_type": "markdown",
|
51 |
+
"metadata": {},
|
52 |
+
"source": [
|
53 |
+
"# Temperature"
|
54 |
+
]
|
55 |
+
},
|
56 |
+
{
|
57 |
+
"cell_type": "code",
|
58 |
+
"execution_count": null,
|
59 |
+
"metadata": {},
|
60 |
+
"outputs": [],
|
61 |
+
"source": [
|
62 |
+
"def remove_value(string):\n",
|
63 |
+
" return string[:-6] if string.endswith('_value') else string\n",
|
64 |
+
"\n",
|
65 |
+
"melt_value = temperature_gdf.drop(columns=[col for col in temperature_gdf.columns if 'TempDiff' in col])\n",
|
66 |
+
"melt_value.columns = [col.split('_')[0] if '_value' in col else col for col in melt_value.columns]\n",
|
67 |
+
"melt_value = melt_value.melt(id_vars=['name', 'geometry', 'admin_div', 'island_group', 'Region'], \n",
|
68 |
+
" var_name='decade', \n",
|
69 |
+
" value_name='value')\n",
|
70 |
+
"melt_tempdiff = temperature_gdf.drop(columns=[col for col in temperature_gdf.columns if 'value' in col])\n",
|
71 |
+
"melt_tempdiff.columns = [col.split('_')[0] if '_TempDiff' in col else col for col in melt_tempdiff.columns]\n",
|
72 |
+
"melt_tempdiff = melt_tempdiff.melt(id_vars=['name', 'geometry', 'admin_div', 'island_group', 'Region'], \n",
|
73 |
+
" var_name='decade', \n",
|
74 |
+
" value_name='TempDiff')\n",
|
75 |
+
"temp_melted_gdf = pd.merge(melt_value, melt_tempdiff, on=['name', 'geometry', 'admin_div', 'island_group', 'Region', 'decade'])"
|
76 |
+
]
|
77 |
+
},
|
78 |
+
{
|
79 |
+
"cell_type": "code",
|
80 |
+
"execution_count": null,
|
81 |
+
"metadata": {},
|
82 |
+
"outputs": [],
|
83 |
+
"source": [
|
84 |
+
"print(temperature_gdf.shape)\n",
|
85 |
+
"temperature_gdf.head()"
|
86 |
+
]
|
87 |
+
},
|
88 |
+
{
|
89 |
+
"cell_type": "code",
|
90 |
+
"execution_count": null,
|
91 |
+
"metadata": {},
|
92 |
+
"outputs": [],
|
93 |
+
"source": [
|
94 |
+
"def update_bar_fig(island_value):\n",
|
95 |
+
" island_gdf = temp_melted_gdf[(temp_melted_gdf['island_group'].isin([island_value]) == True)].drop(columns=['geometry'])\n",
|
96 |
+
" # Create the Figure with horizontal orientation\n",
|
97 |
+
" bar_fig = px.bar(island_gdf, y='name', x='value', animation_frame=\"decade\", orientation='h')\n",
|
98 |
+
" bar_fig.update_layout(\n",
|
99 |
+
" height=750,\n",
|
100 |
+
" title=f'Average Temperature per Province<br>in {island_value}',\n",
|
101 |
+
" margin=dict(l=20, r=20, t=75, b=50),\n",
|
102 |
+
" yaxis=dict(\n",
|
103 |
+
" title=\"Province Name\",\n",
|
104 |
+
" tickfont=dict(size=11) # Adjust tick font size to prevent overlap\n",
|
105 |
+
" ),\n",
|
106 |
+
" xaxis=dict(\n",
|
107 |
+
" title=\"Avg Temperature (°C)\",\n",
|
108 |
+
" range=[0, 35],\n",
|
109 |
+
" tickmode='linear',\n",
|
110 |
+
" dtick=5,\n",
|
111 |
+
" tickfont=dict(size=13), # Adjust tick font size to prevent overlap\n",
|
112 |
+
" tickangle=0\n",
|
113 |
+
" ),\n",
|
114 |
+
" font=dict( # Adjust font size for main title\n",
|
115 |
+
" size=12\n",
|
116 |
+
" )\n",
|
117 |
+
" )\n",
|
118 |
+
" hover_template = \"Average Temperature in<br>\" + \\\n",
|
119 |
+
" \"<b>%{y}</b><br>\" + \\\n",
|
120 |
+
" \"during the %{customdata[0]}:<br>\" + \\\n",
|
121 |
+
" \"%{customdata[1]:.2f}°C\"\n",
|
122 |
+
" bar_fig.update_traces(hovertemplate=hover_template,\n",
|
123 |
+
" customdata=island_gdf[['decade', 'value']])\n",
|
124 |
+
"\n",
|
125 |
+
" for frame in bar_fig.frames:\n",
|
126 |
+
" frame.data[0].hovertemplate = hover_template\n",
|
127 |
+
" frame.data[0].customdata = island_gdf[['decade', 'value']]\n",
|
128 |
+
" bar_fig.update_layout(\n",
|
129 |
+
" updatemenus=[{\n",
|
130 |
+
" 'direction': 'left',\n",
|
131 |
+
" 'pad': {'t': 10, 'b': 10, 'l': 10, 'r': 10},\n",
|
132 |
+
" 'showactive': False,\n",
|
133 |
+
" 'type': 'buttons',\n",
|
134 |
+
" 'x': 0.06,\n",
|
135 |
+
" 'xanchor': 'right',\n",
|
136 |
+
" 'y': -0.14,\n",
|
137 |
+
" 'yanchor': 'top'\n",
|
138 |
+
" }],\n",
|
139 |
+
" sliders=[{\n",
|
140 |
+
" 'active': 0,\n",
|
141 |
+
" 'x': 0.98,\n",
|
142 |
+
" 'y': -0.08,\n",
|
143 |
+
" 'xanchor': 'right',\n",
|
144 |
+
" 'yanchor': 'top',\n",
|
145 |
+
" 'transition': {'duration': 300, 'easing': 'cubic-in-out'},\n",
|
146 |
+
" 'pad': {'t': 10, 'b': 10, 'l': 10, 'r': 10},\n",
|
147 |
+
" 'currentvalue': {\n",
|
148 |
+
" 'font': {'size': 15},\n",
|
149 |
+
" 'prefix': 'Decade:',\n",
|
150 |
+
" 'visible': True,\n",
|
151 |
+
" 'xanchor': 'right',\n",
|
152 |
+
" },\n",
|
153 |
+
" 'visible': True,\n",
|
154 |
+
" }]\n",
|
155 |
+
" )\n",
|
156 |
+
"\n",
|
157 |
+
" highest_temp_1960 = island_gdf[island_gdf['decade'] == '1960s']['value'].max() # Highest temperature for the decade 1960\n",
|
158 |
+
" bar_fig.add_shape( # Line representing highest temperature in the 1960s\n",
|
159 |
+
" type=\"line\",\n",
|
160 |
+
" x0=highest_temp_1960,\n",
|
161 |
+
" y0=-1,\n",
|
162 |
+
" x1=highest_temp_1960,\n",
|
163 |
+
" y1=island_gdf.name.nunique()-0.5,\n",
|
164 |
+
" line=dict(\n",
|
165 |
+
" color=\"red\",\n",
|
166 |
+
" width=1,\n",
|
167 |
+
" dash=\"dash\",\n",
|
168 |
+
" ),\n",
|
169 |
+
" )\n",
|
170 |
+
"\n",
|
171 |
+
" bar_fig.add_annotation(\n",
|
172 |
+
" x=highest_temp_1960 + 1,\n",
|
173 |
+
" y=island_gdf.name.nunique() / 1.3, # Adjust the y position of the label\n",
|
174 |
+
" text=\"Highest Temp in the 1960s\",\n",
|
175 |
+
" showarrow=True,\n",
|
176 |
+
" arrowhead=1,\n",
|
177 |
+
" ax=0,\n",
|
178 |
+
" ay=0, # Adjust the arrow position\n",
|
179 |
+
" font=dict(\n",
|
180 |
+
" color=\"red\",\n",
|
181 |
+
" size=12\n",
|
182 |
+
" ),\n",
|
183 |
+
" align=\"left\",\n",
|
184 |
+
" textangle=90 # Tilt the text 90 degrees\n",
|
185 |
+
" )\n",
|
186 |
+
"\n",
|
187 |
+
" return bar_fig\n",
|
188 |
+
"\n",
|
189 |
+
"bar_fig = update_bar_fig('Mindanao')\n",
|
190 |
+
"bar_fig.show()"
|
191 |
+
]
|
192 |
+
},
|
193 |
+
{
|
194 |
+
"cell_type": "markdown",
|
195 |
+
"metadata": {},
|
196 |
+
"source": [
|
197 |
+
"### TEMP 2: The second visualization for the temperature data is a **Chloropleth Map** that shows the Average Temperature per Province across all of the decades from 1960s to 2020s.\n",
|
198 |
+
"#### Interactions:\n",
|
199 |
+
"- Across the Decades Animation\n",
|
200 |
+
"\n",
|
201 |
+
"<em>Note: the reason that only four decades are used (1960s, 1980s, 2000s, and 2020s) is because using more decades/dimensions would make the load time of the figure significantly higher reaching up to an hour. This visualization takes approximately 2-3 minutes to load in this notebook. </em>"
|
202 |
+
]
|
203 |
+
},
|
204 |
+
{
|
205 |
+
"cell_type": "code",
|
206 |
+
"execution_count": null,
|
207 |
+
"metadata": {},
|
208 |
+
"outputs": [],
|
209 |
+
"source": [
|
210 |
+
"# def update_map_fig(n_intervals):\n",
|
211 |
+
"# temp_sliced = temp_gdf[(temp_gdf['decade'].isin(['1960s', '1980s', '2000s', '2020s']) == True)].reset_index()\n",
|
212 |
+
"# map_fig = px.choropleth_mapbox(temp_sliced,\n",
|
213 |
+
"# geojson=temp_sliced.geometry,\n",
|
214 |
+
"# locations=temp_sliced.index,\n",
|
215 |
+
"# color='value',\n",
|
216 |
+
"# color_continuous_scale='turbo',\n",
|
217 |
+
"# range_color=[25, 32.5],\n",
|
218 |
+
"# mapbox_style='streets',\n",
|
219 |
+
"# zoom=5,\n",
|
220 |
+
"# center={\"lat\": 12.8797, \"lon\": 121.7740},\n",
|
221 |
+
"# opacity=0.6,\n",
|
222 |
+
"# animation_frame=\"decade\"\n",
|
223 |
+
"# )\n",
|
224 |
+
"# map_fig.update_layout(\n",
|
225 |
+
"# coloraxis_colorbar=dict(title=f\"Average<br>Temperature(°C)\", yanchor=\"top\", xanchor='left',\n",
|
226 |
+
"# y=1, x=0, ticks=\"outside\", ticklabelposition=\"outside left\", thickness=10, title_font_color='#0c232c',\n",
|
227 |
+
"# tickvals=[i for i in range(25, 33)],\n",
|
228 |
+
"# tickmode='array',\n",
|
229 |
+
"# ticksuffix='°C',\n",
|
230 |
+
"# tickfont=dict(\n",
|
231 |
+
"# size=12,\n",
|
232 |
+
"# color='#0c232c')\n",
|
233 |
+
"# )\n",
|
234 |
+
"# )\n",
|
235 |
+
"# map_fig.update_layout(\n",
|
236 |
+
"# margin=dict(l=0, r=0, t=0, b=0),\n",
|
237 |
+
"# updatemenus=[{\n",
|
238 |
+
"# 'buttons': [\n",
|
239 |
+
"# {'visible': True}\n",
|
240 |
+
"# ],\n",
|
241 |
+
"# 'direction': 'left',\n",
|
242 |
+
"# 'pad': {'t': 0, 'b': 0, 'l': 0, 'r': 0},\n",
|
243 |
+
"# 'showactive': False,\n",
|
244 |
+
"# 'type': 'buttons',\n",
|
245 |
+
"# 'x': 0.15,\n",
|
246 |
+
"# 'xanchor': 'right',\n",
|
247 |
+
"# 'y': 0.21,\n",
|
248 |
+
"# 'yanchor': 'top'\n",
|
249 |
+
"# }],\n",
|
250 |
+
"# sliders=[{\n",
|
251 |
+
"# 'active': 0,\n",
|
252 |
+
"# 'x': 0.07,\n",
|
253 |
+
"# 'y': 0.19,\n",
|
254 |
+
"# 'len': 0.5,\n",
|
255 |
+
"# 'xanchor': 'left',\n",
|
256 |
+
"# 'yanchor': 'top',\n",
|
257 |
+
"# 'currentvalue': {\n",
|
258 |
+
"# 'font': {'size': 16},\n",
|
259 |
+
"# 'prefix': 'Decade:',\n",
|
260 |
+
"# 'visible': True,\n",
|
261 |
+
"# 'xanchor': 'right'\n",
|
262 |
+
"# },\n",
|
263 |
+
"# 'transition': {'duration': 1000, 'easing': 'cubic-in-out'},\n",
|
264 |
+
"# 'pad': {'t': 0, 'b': 0, 'l': 0, 'r': 0}\n",
|
265 |
+
"# }]\n",
|
266 |
+
"# )\n",
|
267 |
+
"# hover_template = '<b>%{customdata[0]}</b><br>Average Temp: %{customdata[1]:.2f}°C<extra></extra>'\n",
|
268 |
+
"# map_fig.update_traces(hovertemplate=hover_template,\n",
|
269 |
+
"# customdata=temp_sliced[['name', 'value']])\n",
|
270 |
+
"# for frame in map_fig.frames:\n",
|
271 |
+
"# frame.data[0].hovertemplate = hover_template\n",
|
272 |
+
"# frame.data[0].customdata = temp_sliced[['name', 'value']]\n",
|
273 |
+
"\n",
|
274 |
+
"# return map_fig\n",
|
275 |
+
"\n",
|
276 |
+
"# map_fig = update_map_fig(1)\n",
|
277 |
+
"# map_fig.show()"
|
278 |
+
]
|
279 |
+
},
|
280 |
+
{
|
281 |
+
"cell_type": "code",
|
282 |
+
"execution_count": null,
|
283 |
+
"metadata": {},
|
284 |
+
"outputs": [],
|
285 |
+
"source": [
|
286 |
+
"def update_map_fig(decade_value):\n",
|
287 |
+
" map_fig = px.choropleth_mapbox(temperature_gdf,\n",
|
288 |
+
" geojson=temperature_gdf.geometry,\n",
|
289 |
+
" locations=temperature_gdf.index,\n",
|
290 |
+
" color=decade_value,\n",
|
291 |
+
" color_continuous_scale='turbo',\n",
|
292 |
+
" range_color=[25, 33],\n",
|
293 |
+
" mapbox_style='streets',\n",
|
294 |
+
" zoom=5,\n",
|
295 |
+
" center={\"lat\": 12.8797, \"lon\": 122.7740},\n",
|
296 |
+
" opacity=0.6,\n",
|
297 |
+
" )\n",
|
298 |
+
" map_fig.update_layout(\n",
|
299 |
+
" coloraxis_colorbar=dict(title=f\"{remove_value(decade_value)}<br>Average<br>Temperature(°C)\", yanchor=\"top\", xanchor='left',\n",
|
300 |
+
" y=1, x=0, ticks=\"outside\", ticklabelposition=\"outside left\", thickness=10, title_font_color='#0c232c',\n",
|
301 |
+
" tickvals=[i for i in range(25, 33)],\n",
|
302 |
+
" tickmode='array',\n",
|
303 |
+
" ticksuffix='°C',\n",
|
304 |
+
" tickfont=dict(\n",
|
305 |
+
" size=12,\n",
|
306 |
+
" color='#0c232c')\n",
|
307 |
+
" )\n",
|
308 |
+
" )\n",
|
309 |
+
" map_fig.update_layout(\n",
|
310 |
+
" margin=dict(l=0, r=0, t=0, b=0),\n",
|
311 |
+
" )\n",
|
312 |
+
" hover_template = '<b>%{customdata[0]}</b><br>during the '+ remove_value(decade_value) +'<br>Average Temp: %{customdata[1]:.2f}°C<extra></extra>'\n",
|
313 |
+
" map_fig.update_traces(hovertemplate=hover_template,\n",
|
314 |
+
" customdata=temperature_gdf[['name', decade_value]])\n",
|
315 |
+
"\n",
|
316 |
+
" return map_fig\n",
|
317 |
+
"\n",
|
318 |
+
"map_fig = update_map_fig('1970s' + '_value')\n",
|
319 |
+
"map_fig.show()"
|
320 |
+
]
|
321 |
+
},
|
322 |
+
{
|
323 |
+
"cell_type": "markdown",
|
324 |
+
"metadata": {},
|
325 |
+
"source": [
|
326 |
+
"### TEMP 3"
|
327 |
+
]
|
328 |
+
},
|
329 |
+
{
|
330 |
+
"cell_type": "code",
|
331 |
+
"execution_count": null,
|
332 |
+
"metadata": {},
|
333 |
+
"outputs": [],
|
334 |
+
"source": [
|
335 |
+
"def update_line(division, click_data):\n",
|
336 |
+
" curr_div = ''\n",
|
337 |
+
" data = click_data\n",
|
338 |
+
" if division == 'Region':\n",
|
339 |
+
" curr_div = 'Region'\n",
|
340 |
+
" elif division == 'Province':\n",
|
341 |
+
" curr_div = 'name'\n",
|
342 |
+
" else:\n",
|
343 |
+
" return\n",
|
344 |
+
"\n",
|
345 |
+
" island_gdf = temp_melted_gdf[(temp_melted_gdf[curr_div].isin([data]) == True)].drop(columns=['geometry'])\n",
|
346 |
+
"\n",
|
347 |
+
" line_fig = px.line(island_gdf, x='decade', y='value',color='name')\n",
|
348 |
+
" line_fig.update_layout(\n",
|
349 |
+
" autosize=True, \n",
|
350 |
+
" height=700,\n",
|
351 |
+
" title='Change in Avg Temperature in ' + data,\n",
|
352 |
+
" yaxis=dict(\n",
|
353 |
+
" range=[25, 32],\n",
|
354 |
+
" tickmode='linear',\n",
|
355 |
+
" dtick=0.5\n",
|
356 |
+
" ),\n",
|
357 |
+
" margin=dict(l=20, r=20, t=100, b=100),\n",
|
358 |
+
" updatemenus=[{\n",
|
359 |
+
" 'direction': 'left', \n",
|
360 |
+
" 'pad': {'t': 0, 'b': 0, 'l': 0, 'r': 0}, \n",
|
361 |
+
" 'showactive': False,\n",
|
362 |
+
" 'type': 'buttons',\n",
|
363 |
+
" 'x': 0.06, \n",
|
364 |
+
" 'xanchor': 'right',\n",
|
365 |
+
" 'y': -0.46, \n",
|
366 |
+
" 'yanchor': 'top'\n",
|
367 |
+
" }],\n",
|
368 |
+
" xaxis_tickangle=-45 \n",
|
369 |
+
" )\n",
|
370 |
+
" hover_template = '<b>' + data + '</b><br>Average Temperature in<br>the %{x}:<br>%{y:.2f}°C<extra></extra>'\n",
|
371 |
+
" line_fig.update_traces(hovertemplate=hover_template)\n",
|
372 |
+
"\n",
|
373 |
+
" return line_fig\n",
|
374 |
+
"\n",
|
375 |
+
"done = update_line('Region', 'RegionI')\n",
|
376 |
+
"done.show()"
|
377 |
+
]
|
378 |
+
},
|
379 |
+
{
|
380 |
+
"cell_type": "markdown",
|
381 |
+
"metadata": {},
|
382 |
+
"source": [
|
383 |
+
"### TEMP 4"
|
384 |
+
]
|
385 |
+
},
|
386 |
+
{
|
387 |
+
"cell_type": "code",
|
388 |
+
"execution_count": null,
|
389 |
+
"metadata": {},
|
390 |
+
"outputs": [],
|
391 |
+
"source": [
|
392 |
+
"def update_bar1960_fig(island_value):\n",
|
393 |
+
" island_gdf = temp_melted_gdf[(temp_melted_gdf['island_group'].isin([island_value])) & (temp_melted_gdf['decade'].isin(['1960s']) == False)].drop(columns=['geometry'])\n",
|
394 |
+
" # Create the Figure with horizontal orientation\n",
|
395 |
+
" bar1960_fig = px.bar(island_gdf, y='name', x='TempDiff', animation_frame=\"decade\", orientation='h')\n",
|
396 |
+
" bar1960_fig.update_layout(\n",
|
397 |
+
" height=750,\n",
|
398 |
+
" title=f'Average Temperature Increase Relative to 1960s<br>Across {island_value} Provinces',\n",
|
399 |
+
" margin=dict(l=20, r=20, t=75, b=50),\n",
|
400 |
+
" yaxis=dict(\n",
|
401 |
+
" title=\"Province Name\",\n",
|
402 |
+
" tickfont=dict(size=11) # Adjust tick font size to prevent overlap\n",
|
403 |
+
" ),\n",
|
404 |
+
" xaxis=dict(\n",
|
405 |
+
" title=\"Temperature (°C)\",\n",
|
406 |
+
" range=[0, 1.5],\n",
|
407 |
+
" tickmode='linear',\n",
|
408 |
+
" dtick=0.3,\n",
|
409 |
+
" tickfont=dict(size=13), # Adjust tick font size to prevent overlap\n",
|
410 |
+
" tickangle=0\n",
|
411 |
+
" ),\n",
|
412 |
+
" font=dict( # Adjust font size for main title\n",
|
413 |
+
" size=12\n",
|
414 |
+
" )\n",
|
415 |
+
" )\n",
|
416 |
+
" hover_template = \"Avg Temperature Increase<br>\" + \\\n",
|
417 |
+
" \"Relative to 1960s<br>\" + \\\n",
|
418 |
+
" \"<b>in %{y}</b><br>\" + \\\n",
|
419 |
+
" \"during the %{customdata[0]}:<br>\" + \\\n",
|
420 |
+
" \"%{customdata[1]:.2f}°C\"\n",
|
421 |
+
" bar1960_fig.update_traces(hovertemplate=hover_template,\n",
|
422 |
+
" customdata=island_gdf[['decade', 'value']])\n",
|
423 |
+
"\n",
|
424 |
+
" for frame in bar1960_fig.frames:\n",
|
425 |
+
" frame.data[0].hovertemplate = hover_template\n",
|
426 |
+
" frame.data[0].customdata = island_gdf[['decade', 'value']]\n",
|
427 |
+
" bar1960_fig.update_layout(\n",
|
428 |
+
" updatemenus=[{\n",
|
429 |
+
" 'direction': 'left',\n",
|
430 |
+
" 'pad': {'t': 10, 'b': 10, 'l': 10, 'r': 10},\n",
|
431 |
+
" 'showactive': False,\n",
|
432 |
+
" 'type': 'buttons',\n",
|
433 |
+
" 'x': 0.06,\n",
|
434 |
+
" 'xanchor': 'right',\n",
|
435 |
+
" 'y': -0.14,\n",
|
436 |
+
" 'yanchor': 'top'\n",
|
437 |
+
" }],\n",
|
438 |
+
" sliders=[{\n",
|
439 |
+
" 'active': 0,\n",
|
440 |
+
" 'x': 0.98,\n",
|
441 |
+
" 'y': -0.08,\n",
|
442 |
+
" 'xanchor': 'right',\n",
|
443 |
+
" 'yanchor': 'top',\n",
|
444 |
+
" 'transition': {'duration': 300, 'easing': 'cubic-in-out'},\n",
|
445 |
+
" 'pad': {'t': 10, 'b': 10, 'l': 10, 'r': 10},\n",
|
446 |
+
" 'currentvalue': {\n",
|
447 |
+
" 'font': {'size': 15},\n",
|
448 |
+
" 'prefix': 'Decade:',\n",
|
449 |
+
" 'visible': True,\n",
|
450 |
+
" 'xanchor': 'right',\n",
|
451 |
+
" },\n",
|
452 |
+
" 'visible': True,\n",
|
453 |
+
" }]\n",
|
454 |
+
" )\n",
|
455 |
+
"\n",
|
456 |
+
" return bar1960_fig\n",
|
457 |
+
"\n",
|
458 |
+
"test_fig = update_bar1960_fig('Mindanao')\n",
|
459 |
+
"test_fig.show()"
|
460 |
+
]
|
461 |
+
},
|
462 |
+
{
|
463 |
+
"cell_type": "markdown",
|
464 |
+
"metadata": {},
|
465 |
+
"source": [
|
466 |
+
"**APP IDEAS**\n",
|
467 |
+
"- The temperature page will be in two columns 3:9\n",
|
468 |
+
"- The right one is the map TEMP 2\n",
|
469 |
+
"- The left one is a header and story explaining the map\n",
|
470 |
+
"- below the text will be a modal button \"See More\" that opens a modal\n",
|
471 |
+
"- The modal contains two columns, the left one is a text header and story, the right one is TEMP 1 bar graph\n",
|
472 |
+
"- The app will have a footer that has two buttons at its ends, \"< BACK\" and \"NEXT >\"\n",
|
473 |
+
"- Back will lead to the initial page and Next will lead to the Biodiversity Page"
|
474 |
+
]
|
475 |
+
},
|
476 |
+
{
|
477 |
+
"cell_type": "markdown",
|
478 |
+
"metadata": {},
|
479 |
+
"source": [
|
480 |
+
"# Biodiversity"
|
481 |
+
]
|
482 |
+
},
|
483 |
+
{
|
484 |
+
"cell_type": "code",
|
485 |
+
"execution_count": null,
|
486 |
+
"metadata": {},
|
487 |
+
"outputs": [],
|
488 |
+
"source": [
|
489 |
+
"print(biodiversity_gdf.shape)\n",
|
490 |
+
"biodiversity_gdf.head()"
|
491 |
+
]
|
492 |
+
},
|
493 |
+
{
|
494 |
+
"cell_type": "markdown",
|
495 |
+
"metadata": {},
|
496 |
+
"source": [
|
497 |
+
"### BIO 1: The first visualization for the biodiversity data is a **Chloropleth Map** that shows the number of [Total/CR/EN/VU] unique species per Province.\n",
|
498 |
+
"#### Interactions:\n",
|
499 |
+
"- DropDown/RadioButtons that switch between \"Total, CR, EN and VU\" through Dash"
|
500 |
+
]
|
501 |
+
},
|
502 |
+
{
|
503 |
+
"cell_type": "code",
|
504 |
+
"execution_count": null,
|
505 |
+
"metadata": {},
|
506 |
+
"outputs": [],
|
507 |
+
"source": [
|
508 |
+
"land_gdf = biodiversity_gdf[(biodiversity_gdf['area_type'].isin(['Land']))]\n",
|
509 |
+
"\n",
|
510 |
+
"fig = px.choropleth_mapbox(land_gdf,\n",
|
511 |
+
" geojson=land_gdf.geometry,\n",
|
512 |
+
" locations=land_gdf.index,\n",
|
513 |
+
" color='Critically Endangered', # Change this based on dropdown\n",
|
514 |
+
" color_continuous_scale='balance',\n",
|
515 |
+
" range_color=[land_gdf['Critically Endangered'].min(), land_gdf['Critically Endangered'].max()], \n",
|
516 |
+
" mapbox_style='streets', \n",
|
517 |
+
" zoom=5, \n",
|
518 |
+
" center={\"lat\": 12.8797, \"lon\": 122.7740}, \n",
|
519 |
+
" opacity=0.6,\n",
|
520 |
+
" )\n",
|
521 |
+
"\n",
|
522 |
+
"fig.update_layout(margin=dict(l=0, r=0, t=0, b=0))\n",
|
523 |
+
"\n",
|
524 |
+
"fig.update_layout(coloraxis_colorbar=dict(title=\"Critically Endangered<br>Species\",yanchor=\"top\",xanchor='right',\n",
|
525 |
+
" y=1, x=1, ticks=\"outside\", thickness=10, title_font_color='#0c232c', \n",
|
526 |
+
" ticksuffix=' Species',\n",
|
527 |
+
" tickfont=dict(\n",
|
528 |
+
" size=12,\n",
|
529 |
+
" color='#0c232c')\n",
|
530 |
+
" )\n",
|
531 |
+
" )\n",
|
532 |
+
"\n",
|
533 |
+
"hover_template = '<b>%{customdata[0]}</b><br>Unique Species: %{customdata[1]:.0f}<extra></extra>'\n",
|
534 |
+
"\n",
|
535 |
+
"fig.update_traces(hovertemplate=hover_template,\n",
|
536 |
+
" customdata=land_gdf[['name', 'Critically Endangered']])\n",
|
537 |
+
"\n",
|
538 |
+
"fig.show()"
|
539 |
+
]
|
540 |
+
},
|
541 |
+
{
|
542 |
+
"cell_type": "markdown",
|
543 |
+
"metadata": {},
|
544 |
+
"source": [
|
545 |
+
"### BIO 2: The second visualization for the biodiversity data is a DRILLDOWN operation on the previous **Chloropleth Map** that shows the number of [Total/CR/EN/VU] unique species per Province. In addition to this, the drilldown will be based on Island Groups\n",
|
546 |
+
"#### Interactions:\n",
|
547 |
+
"- A button that triggers the drilldown\n",
|
548 |
+
"- DropDown/RadioButtons that switch between \"Total, CR, EN and VU\" (from the previous map BIO 1)\n",
|
549 |
+
"- DropDown/RadioButtons that switch between \"Luzon, Visayas, and Mindanao\""
|
550 |
+
]
|
551 |
+
},
|
552 |
+
{
|
553 |
+
"cell_type": "code",
|
554 |
+
"execution_count": null,
|
555 |
+
"metadata": {},
|
556 |
+
"outputs": [],
|
557 |
+
"source": [
|
558 |
+
"# Set Island Group\n",
|
559 |
+
"land_gdf = biodiversity_gdf[(biodiversity_gdf['area_type'].isin(['Land'])) & (biodiversity_gdf['island_group'].isin(['Luzon']))]\n",
|
560 |
+
"\n",
|
561 |
+
"# Createe the figure\n",
|
562 |
+
"fig = px.choropleth_mapbox(land_gdf,\n",
|
563 |
+
" geojson=land_gdf.geometry,\n",
|
564 |
+
" locations=land_gdf.index,\n",
|
565 |
+
" color='Critically Endangered', # Change this based on dropdown\n",
|
566 |
+
" color_continuous_scale='balance',\n",
|
567 |
+
" range_color=[land_gdf['Critically Endangered'].min(), land_gdf['Critically Endangered'].max()], \n",
|
568 |
+
" mapbox_style='streets', \n",
|
569 |
+
" zoom=6, \n",
|
570 |
+
" center={\"lat\": 12.8797, \"lon\": 122.7740}, \n",
|
571 |
+
" opacity=0.6,\n",
|
572 |
+
" )\n",
|
573 |
+
"\n",
|
574 |
+
"fig.update_layout(margin=dict(l=0, r=0, t=0, b=0))\n",
|
575 |
+
"\n",
|
576 |
+
"fig.update_layout(coloraxis_colorbar=dict(title=\"Critically Endangered<br>Species\",yanchor=\"top\",xanchor='right',\n",
|
577 |
+
" y=1, x=1, ticks=\"outside\", thickness=10, title_font_color='#0c232c', \n",
|
578 |
+
" ticksuffix=' Species',\n",
|
579 |
+
" tickfont=dict(\n",
|
580 |
+
" size=12,\n",
|
581 |
+
" color='#0c232c')\n",
|
582 |
+
" )\n",
|
583 |
+
" )\n",
|
584 |
+
"\n",
|
585 |
+
"hover_template = '<b>%{customdata[0]}</b><br>Unique Species: %{customdata[1]:.0f}<extra></extra>'\n",
|
586 |
+
"\n",
|
587 |
+
"fig.update_traces(hovertemplate=hover_template,\n",
|
588 |
+
" customdata=land_gdf[['name', 'Critically Endangered']])\n",
|
589 |
+
"\n",
|
590 |
+
"fig.show()"
|
591 |
+
]
|
592 |
+
},
|
593 |
+
{
|
594 |
+
"cell_type": "markdown",
|
595 |
+
"metadata": {},
|
596 |
+
"source": [
|
597 |
+
"### BIO 3: The third visualization for the biodiversity data is ALSO a DRILLDOWN operation on the first chloropleth map and is a **Horizontal Stacked Bar Chart** that shows the number of [CR/EN/VU] unique species per Province. In addition to this, the drilldown will be based on Island Groups\n",
|
598 |
+
"#### Interactions:\n",
|
599 |
+
"- A button that triggers the drilldown (same button as BIO 2)\n",
|
600 |
+
"- DropDown/RadioButtons that switch between \"Luzon, Visayas, and Mindanao\" (same button as BIO 2)"
|
601 |
+
]
|
602 |
+
},
|
603 |
+
{
|
604 |
+
"cell_type": "code",
|
605 |
+
"execution_count": null,
|
606 |
+
"metadata": {},
|
607 |
+
"outputs": [],
|
608 |
+
"source": [
|
609 |
+
"# Set Island Group\n",
|
610 |
+
"land_gdf = biodiversity_gdf[(biodiversity_gdf['area_type'].isin(['Land'])) & (biodiversity_gdf['island_group'].isin(['Luzon']))].sort_values(by='total_species', ascending=True)\n",
|
611 |
+
"\n",
|
612 |
+
"# Create stacked bar plot using Plotly Express\n",
|
613 |
+
"fig = px.bar(land_gdf,\n",
|
614 |
+
" x=['Vulnerable', 'Endangered', 'Critically Endangered'], \n",
|
615 |
+
" y='name', \n",
|
616 |
+
" title='Number of Unique Endangered Species in Luzon',\n",
|
617 |
+
" labels={'Vulnerable', 'Endangered', 'Critically Endangered'}, \n",
|
618 |
+
" orientation='h', \n",
|
619 |
+
" height=600, \n",
|
620 |
+
" color_discrete_sequence=['yellow', 'orange', 'red'], \n",
|
621 |
+
" )\n",
|
622 |
+
"\n",
|
623 |
+
"fig.update_layout(\n",
|
624 |
+
" xaxis_title='Number of Unique Species', \n",
|
625 |
+
" yaxis_title='Province',\n",
|
626 |
+
" legend_title='Category',\n",
|
627 |
+
")\n",
|
628 |
+
"\n",
|
629 |
+
"hover_template = '<b>%{customdata[0]}</b><br>Unique Species: %{x}<extra></extra>'\n",
|
630 |
+
"\n",
|
631 |
+
"fig.update_traces(hovertemplate=hover_template,\n",
|
632 |
+
" customdata=land_gdf[['name']])\n",
|
633 |
+
"\n",
|
634 |
+
"fig.show()"
|
635 |
+
]
|
636 |
+
},
|
637 |
+
{
|
638 |
+
"cell_type": "markdown",
|
639 |
+
"metadata": {},
|
640 |
+
"source": [
|
641 |
+
"### BIO 4: The fourth visualization for the biodiversity data is a **Chloropleth Map** that shows the number of [Total/CR/EN/VU] unique species per sea.\n",
|
642 |
+
"#### Interactions:\n",
|
643 |
+
"- DropDown/RadioButtons that switch between \"Total, CR, EN and VU\" through Dash"
|
644 |
+
]
|
645 |
+
},
|
646 |
+
{
|
647 |
+
"cell_type": "code",
|
648 |
+
"execution_count": null,
|
649 |
+
"metadata": {},
|
650 |
+
"outputs": [],
|
651 |
+
"source": [
|
652 |
+
"sea_gdf = biodiversity_gdf[(biodiversity_gdf['area_type'].isin(['Sea']))]\n",
|
653 |
+
"\n",
|
654 |
+
"fig = px.choropleth_mapbox(sea_gdf,\n",
|
655 |
+
" geojson=sea_gdf.geometry,\n",
|
656 |
+
" locations=sea_gdf.index,\n",
|
657 |
+
" color='Critically Endangered', # Change this based on dropdown\n",
|
658 |
+
" color_continuous_scale='sunsetdark',\n",
|
659 |
+
" range_color=[sea_gdf['Critically Endangered'].min(), sea_gdf['Critically Endangered'].max()], \n",
|
660 |
+
" mapbox_style='streets', \n",
|
661 |
+
" zoom=5, \n",
|
662 |
+
" center={\"lat\": 12.8797, \"lon\": 122.7740}, \n",
|
663 |
+
" opacity=0.6,\n",
|
664 |
+
" )\n",
|
665 |
+
"\n",
|
666 |
+
"fig.update_layout(margin=dict(l=0, r=0, t=0, b=0))\n",
|
667 |
+
"\n",
|
668 |
+
"fig.update_layout(coloraxis_colorbar=dict(title=\"Critically Endangered<br>Species\",yanchor=\"top\",xanchor='right',\n",
|
669 |
+
" y=1, x=1, ticks=\"outside\", thickness=10, title_font_color='#0c232c', \n",
|
670 |
+
" ticksuffix=' Species',\n",
|
671 |
+
" tickfont=dict(\n",
|
672 |
+
" size=12,\n",
|
673 |
+
" color='#0c232c')\n",
|
674 |
+
" )\n",
|
675 |
+
" )\n",
|
676 |
+
"\n",
|
677 |
+
"hover_template = '<b>%{customdata[0]}</b><br>Unique Species: %{customdata[1]:.0f}<extra></extra>'\n",
|
678 |
+
"\n",
|
679 |
+
"fig.update_traces(hovertemplate=hover_template,\n",
|
680 |
+
" customdata=sea_gdf[['name', 'Critically Endangered']])\n",
|
681 |
+
"\n",
|
682 |
+
"fig.show()"
|
683 |
+
]
|
684 |
+
},
|
685 |
+
{
|
686 |
+
"cell_type": "markdown",
|
687 |
+
"metadata": {},
|
688 |
+
"source": [
|
689 |
+
"### BIO 5: The fifth visualization for the biodiversity data is a **Horizontal Stacked Bar Chart** that shows the number of [CR/EN/VU] unique species per Sea.\n",
|
690 |
+
"#### Interactions:\n",
|
691 |
+
"- A button that shows the visualization"
|
692 |
+
]
|
693 |
+
},
|
694 |
+
{
|
695 |
+
"cell_type": "code",
|
696 |
+
"execution_count": null,
|
697 |
+
"metadata": {},
|
698 |
+
"outputs": [],
|
699 |
+
"source": [
|
700 |
+
"sea_gdf = biodiversity_gdf[(biodiversity_gdf['area_type'].isin(['Sea']))]\n",
|
701 |
+
"# Create stacked bar plot using Plotly Express\n",
|
702 |
+
"fig = px.bar(sea_gdf,\n",
|
703 |
+
" x=['Vulnerable', 'Endangered', 'Critically Endangered'], \n",
|
704 |
+
" y='name', \n",
|
705 |
+
" title='Number of Unique Endangered Species in Sea',\n",
|
706 |
+
" labels={'Vulnerable', 'Endangered', 'Critically Endangered'}, \n",
|
707 |
+
" orientation='h', \n",
|
708 |
+
" height=600, \n",
|
709 |
+
" color_discrete_sequence=['yellow', 'orange', 'red'], \n",
|
710 |
+
" )\n",
|
711 |
+
"\n",
|
712 |
+
"fig.update_layout(\n",
|
713 |
+
" xaxis_title='Number of Unique Species', \n",
|
714 |
+
" yaxis_title='Province',\n",
|
715 |
+
" legend_title='Category',\n",
|
716 |
+
")\n",
|
717 |
+
"\n",
|
718 |
+
"hover_template = '<b>%{customdata[0]}</b><br>Unique Species: %{x}<extra></extra>'\n",
|
719 |
+
"\n",
|
720 |
+
"fig.update_traces(hovertemplate=hover_template,\n",
|
721 |
+
" customdata=sea_gdf[['name']])\n",
|
722 |
+
"\n",
|
723 |
+
"fig.show()"
|
724 |
+
]
|
725 |
+
},
|
726 |
+
{
|
727 |
+
"cell_type": "markdown",
|
728 |
+
"metadata": {},
|
729 |
+
"source": [
|
730 |
+
"**APP IDEAS**\n",
|
731 |
+
"- There is toggle that switches the page to show Land Data or Sea Data\n",
|
732 |
+
"- Bio 1, Bio 2, and Bio 3 is part of the Land and will have interactions that are connected to each other\n",
|
733 |
+
"- Land data will somehow be related to the temp data\n",
|
734 |
+
"- Bio 4, and Bio 5 is part of the Sea and will havee interactions that are also connected to each other\n",
|
735 |
+
"- Will also be compared to temp data"
|
736 |
+
]
|
737 |
+
},
|
738 |
+
{
|
739 |
+
"cell_type": "markdown",
|
740 |
+
"metadata": {},
|
741 |
+
"source": [
|
742 |
+
"# Disaster"
|
743 |
+
]
|
744 |
+
},
|
745 |
+
{
|
746 |
+
"cell_type": "code",
|
747 |
+
"execution_count": null,
|
748 |
+
"metadata": {},
|
749 |
+
"outputs": [],
|
750 |
+
"source": [
|
751 |
+
"print(disaster_gdf.shape)\n",
|
752 |
+
"disaster_gdf.head()"
|
753 |
+
]
|
754 |
+
},
|
755 |
+
{
|
756 |
+
"cell_type": "markdown",
|
757 |
+
"metadata": {},
|
758 |
+
"source": [
|
759 |
+
"### DISASTER 1: The first visualization for the disaster data is a **Chloropleth Map** that shows the number of [Disaster Type] per Province.\n",
|
760 |
+
"#### Interactions:\n",
|
761 |
+
"- DropDown/RadioButtons that switch between Disaster Types (Total, Storm, Flood, Earthquake, Volcanic, Mass Movement, Drought) through Dash"
|
762 |
+
]
|
763 |
+
},
|
764 |
+
{
|
765 |
+
"cell_type": "code",
|
766 |
+
"execution_count": null,
|
767 |
+
"metadata": {},
|
768 |
+
"outputs": [],
|
769 |
+
"source": [
|
770 |
+
"fig = px.choropleth_mapbox(disaster_gdf,\n",
|
771 |
+
" geojson=disaster_gdf.geometry,\n",
|
772 |
+
" locations=disaster_gdf.index,\n",
|
773 |
+
" color='Total Disaster Count', # Change based on dropdown value\n",
|
774 |
+
" color_continuous_scale='reds',\n",
|
775 |
+
" range_color=[disaster_gdf['Total Disaster Count'].min(), disaster_gdf['Total Disaster Count'].max()], \n",
|
776 |
+
" mapbox_style='streets', \n",
|
777 |
+
" zoom=5, \n",
|
778 |
+
" center={\"lat\": disaster_gdf.geometry.centroid.y.mean(), \n",
|
779 |
+
" \"lon\": disaster_gdf.geometry.centroid.x.mean()}, \n",
|
780 |
+
" opacity=0.6,\n",
|
781 |
+
" )\n",
|
782 |
+
"\n",
|
783 |
+
"fig.update_layout(margin=dict(l=0, r=0, t=0, b=0))\n",
|
784 |
+
"\n",
|
785 |
+
"fig.update_layout(coloraxis_colorbar=dict(title=\"Total Disaster<br>Count\",yanchor=\"top\",xanchor='right',\n",
|
786 |
+
" y=1, x=1, ticks=\"outside\", thickness=10, title_font_color='#0c232c', \n",
|
787 |
+
" # ticksuffix=' disasters',\n",
|
788 |
+
" tickfont=dict(\n",
|
789 |
+
" size=12,\n",
|
790 |
+
" color='#0c232c')\n",
|
791 |
+
" )\n",
|
792 |
+
" )\n",
|
793 |
+
"\n",
|
794 |
+
"hover_template = '<b>%{customdata[0]}</b><br>%{customdata[1]:.0f} disasters<extra></extra>'\n",
|
795 |
+
"\n",
|
796 |
+
"fig.update_traces(hovertemplate=hover_template,\n",
|
797 |
+
" customdata=disaster_gdf[['Area Name', 'Total Disaster Count']])\n",
|
798 |
+
"\n",
|
799 |
+
"fig.show()"
|
800 |
+
]
|
801 |
+
},
|
802 |
+
{
|
803 |
+
"cell_type": "markdown",
|
804 |
+
"metadata": {},
|
805 |
+
"source": [
|
806 |
+
"### DISASTER 2: The second visualization for the disaster data is a **Horizontal Bar Chart** that shows the number of [Disaster Type] per Province per [Island Group].\n",
|
807 |
+
"#### Interactions:\n",
|
808 |
+
"- DropDown/RadioButtons that switch between Disaster Types (Total, Storm, Flood, Earthquake, Volcanic, Mass Movement, Drought) through Dash (Same button as Disaster 1 visualization)\n",
|
809 |
+
"- DropDown/RadioButtons that switch between Island Groups (Luzon Visayas Mindanao)"
|
810 |
+
]
|
811 |
+
},
|
812 |
+
{
|
813 |
+
"cell_type": "markdown",
|
814 |
+
"metadata": {},
|
815 |
+
"source": [
|
816 |
+
"### DISASTER 3: The third visualization for the disaster data is a **Chloropleth Map** that shows the number of [Disaster Type] per REGION.\n",
|
817 |
+
"#### Interactions:\n",
|
818 |
+
"- DropDown/RadioButtons that switch between Disaster Types (Total, Storm, Flood, Earthquake, Volcanic, Mass Movement, Drought) through Dash"
|
819 |
+
]
|
820 |
+
},
|
821 |
+
{
|
822 |
+
"cell_type": "code",
|
823 |
+
"execution_count": null,
|
824 |
+
"metadata": {},
|
825 |
+
"outputs": [],
|
826 |
+
"source": [
|
827 |
+
"Region_gdf = disaster_gdf.copy()\n",
|
828 |
+
"Region_tot_ave = Region_gdf.drop(columns=['geometry']).groupby('Region').sum()\n",
|
829 |
+
"\n",
|
830 |
+
"def region_count(disaster_type,col_name):\n",
|
831 |
+
" Region_gdf[col_name] = np.nan #Create a new column that contains the number of disasters per region\n",
|
832 |
+
"\n",
|
833 |
+
" for i1,r1 in Region_gdf.iterrows():\n",
|
834 |
+
" for i2,r2 in Region_tot_ave.iterrows():\n",
|
835 |
+
" if r1['Region'] == i2:\n",
|
836 |
+
" Region_gdf.at[i1, col_name] = r2[disaster_type]\n",
|
837 |
+
" else:\n",
|
838 |
+
" continue\n",
|
839 |
+
"\n",
|
840 |
+
"# Total Disasters per Region\n",
|
841 |
+
"region_count('Total Disaster Count','Region_tot')\n",
|
842 |
+
"# Storm per Region\n",
|
843 |
+
"region_count('Storm Count','Region_storm')\n",
|
844 |
+
"# Flood per Region\n",
|
845 |
+
"region_count('Flood Count','Region_flood')\n",
|
846 |
+
"# Earthquake per Region\n",
|
847 |
+
"region_count('Earthquake Count','Region_earth')\n",
|
848 |
+
"# Volcanic Activity per Region\n",
|
849 |
+
"region_count('Volcanic Activity Count','Region_vol')\n",
|
850 |
+
"# Mass Movement per Region\n",
|
851 |
+
"region_count('Mass Movement Count','Region_mass')\n",
|
852 |
+
"# Drought per Region\n",
|
853 |
+
"region_count('Drought Count','Region_drought')\n",
|
854 |
+
"Region_gdf.head()"
|
855 |
+
]
|
856 |
+
},
|
857 |
+
{
|
858 |
+
"cell_type": "code",
|
859 |
+
"execution_count": null,
|
860 |
+
"metadata": {},
|
861 |
+
"outputs": [],
|
862 |
+
"source": [
|
863 |
+
"def update_map(division, disaster_type):\n",
|
864 |
+
" curr_division = ''\n",
|
865 |
+
" curr_disaster = ''\n",
|
866 |
+
" if division == 'Region':\n",
|
867 |
+
" curr_division = 'Region'\n",
|
868 |
+
" match disaster_type:\n",
|
869 |
+
" case 'Total Disaster':\n",
|
870 |
+
" curr_disaster = 'Region_tot'\n",
|
871 |
+
" case 'Storm':\n",
|
872 |
+
" curr_disaster = 'Region_storm'\n",
|
873 |
+
" case 'Flood':\n",
|
874 |
+
" curr_disaster = 'Region_flood'\n",
|
875 |
+
" case 'Earthquake':\n",
|
876 |
+
" curr_disaster = 'Region_earth'\n",
|
877 |
+
" case 'Volcanic Activity':\n",
|
878 |
+
" curr_disaster = 'Region_vol'\n",
|
879 |
+
" case 'Mass Movement':\n",
|
880 |
+
" curr_disaster = 'Region_mass'\n",
|
881 |
+
" case 'Drought':\n",
|
882 |
+
" curr_disaster = 'Region_drought'\n",
|
883 |
+
" case _:\n",
|
884 |
+
" return\n",
|
885 |
+
" elif division == 'Province':\n",
|
886 |
+
" curr_division = 'Area Name'\n",
|
887 |
+
" match disaster_type:\n",
|
888 |
+
" case 'Total Disaster':\n",
|
889 |
+
" curr_disaster = 'Total Disaster Count'\n",
|
890 |
+
" case 'Storm':\n",
|
891 |
+
" curr_disaster = 'Storm Count'\n",
|
892 |
+
" case 'Flood':\n",
|
893 |
+
" curr_disaster = 'Flood Count'\n",
|
894 |
+
" case 'Earthquake':\n",
|
895 |
+
" curr_disaster = 'Earthquake Count'\n",
|
896 |
+
" case 'Volcanic Activity':\n",
|
897 |
+
" curr_disaster = 'Volcanic Activity Count'\n",
|
898 |
+
" case 'Mass Movement':\n",
|
899 |
+
" curr_disaster = 'Mass Movement Count'\n",
|
900 |
+
" case 'Drought':\n",
|
901 |
+
" curr_disaster = 'Drought Count'\n",
|
902 |
+
" case _:\n",
|
903 |
+
" return\n",
|
904 |
+
" else:\n",
|
905 |
+
" return\n",
|
906 |
+
"\n",
|
907 |
+
" map_fig = px.choropleth_mapbox(Region_gdf,\n",
|
908 |
+
" geojson=Region_gdf.geometry,\n",
|
909 |
+
" locations=Region_gdf.index,\n",
|
910 |
+
" color=curr_disaster, # Change based on dropdown value\n",
|
911 |
+
" color_continuous_scale='amp',\n",
|
912 |
+
" range_color=[Region_gdf[curr_disaster].min(), Region_gdf[curr_disaster].max()], \n",
|
913 |
+
" mapbox_style='streets', \n",
|
914 |
+
" zoom=5, \n",
|
915 |
+
" center={\"lat\": 12.8797, \"lon\": 122.7740}, \n",
|
916 |
+
" opacity=0.6,\n",
|
917 |
+
" )\n",
|
918 |
+
"\n",
|
919 |
+
" map_fig.update_layout(margin=dict(l=0, r=0, t=0, b=0))\n",
|
920 |
+
"\n",
|
921 |
+
" map_fig.update_layout(coloraxis_colorbar=dict(title= disaster_type + \"<br>Count\",yanchor=\"top\",xanchor='left',\n",
|
922 |
+
" y=1, x=0, ticks=\"outside\", ticklabelposition=\"outside left\", thickness=10, title_font_color='#0c232c', \n",
|
923 |
+
" tickfont=dict(\n",
|
924 |
+
" size=12,\n",
|
925 |
+
" color='#0c232c')\n",
|
926 |
+
" )\n",
|
927 |
+
" )\n",
|
928 |
+
"\n",
|
929 |
+
" hover_template = '<b>%{customdata[0]}</b><br>' + disaster_type + ' Count: %{customdata[1]:.0f}<extra></extra>'\n",
|
930 |
+
"\n",
|
931 |
+
" map_fig.update_traces(hovertemplate=hover_template,\n",
|
932 |
+
" customdata=Region_gdf[[curr_division, curr_disaster]])\n",
|
933 |
+
"\n",
|
934 |
+
" return map_fig\n",
|
935 |
+
"\n",
|
936 |
+
"dis_map = update_map('Province', 'Storm')\n",
|
937 |
+
"dis_map.show()"
|
938 |
+
]
|
939 |
+
},
|
940 |
+
{
|
941 |
+
"cell_type": "code",
|
942 |
+
"execution_count": null,
|
943 |
+
"metadata": {},
|
944 |
+
"outputs": [],
|
945 |
+
"source": [
|
946 |
+
"def update_disaster_bar(division, disaster_type, island_group):\n",
|
947 |
+
" curr_division = ''\n",
|
948 |
+
" curr_disaster = ''\n",
|
949 |
+
" if division == 'Region':\n",
|
950 |
+
" curr_division = 'Region'\n",
|
951 |
+
" match disaster_type:\n",
|
952 |
+
" case 'Total Disaster':\n",
|
953 |
+
" curr_disaster = 'Region_tot'\n",
|
954 |
+
" case 'Storm':\n",
|
955 |
+
" curr_disaster = 'Region_storm'\n",
|
956 |
+
" case 'Flood':\n",
|
957 |
+
" curr_disaster = 'Region_flood'\n",
|
958 |
+
" case 'Earthquake':\n",
|
959 |
+
" curr_disaster = 'Region_earth'\n",
|
960 |
+
" case 'Volcanic Activity':\n",
|
961 |
+
" curr_disaster = 'Region_vol'\n",
|
962 |
+
" case 'Mass Movement':\n",
|
963 |
+
" curr_disaster = 'Region_mass'\n",
|
964 |
+
" case 'Drought':\n",
|
965 |
+
" curr_disaster = 'Region_drought'\n",
|
966 |
+
" case _:\n",
|
967 |
+
" return\n",
|
968 |
+
" island_disaster = Region_gdf[Region_gdf['Island Group'] == island_group].groupby('Region')[curr_disaster].sum().sort_values(ascending=True)\n",
|
969 |
+
" x = island_disaster.values\n",
|
970 |
+
" y = island_disaster.index\n",
|
971 |
+
" elif division == 'Province':\n",
|
972 |
+
" curr_division = 'Area Name'\n",
|
973 |
+
" match disaster_type:\n",
|
974 |
+
" case 'Total Disaster':\n",
|
975 |
+
" curr_disaster = 'Total Disaster Count'\n",
|
976 |
+
" case 'Storm':\n",
|
977 |
+
" curr_disaster = 'Storm Count'\n",
|
978 |
+
" case 'Flood':\n",
|
979 |
+
" curr_disaster = 'Flood Count'\n",
|
980 |
+
" case 'Earthquake':\n",
|
981 |
+
" curr_disaster = 'Earthquake Count'\n",
|
982 |
+
" case 'Volcanic Activity':\n",
|
983 |
+
" curr_disaster = 'Volcanic Activity Count'\n",
|
984 |
+
" case 'Mass Movement':\n",
|
985 |
+
" curr_disaster = 'Mass Movement Count'\n",
|
986 |
+
" case 'Drought':\n",
|
987 |
+
" curr_disaster = 'Drought Count'\n",
|
988 |
+
" case _:\n",
|
989 |
+
" return\n",
|
990 |
+
" island_disaster = Region_gdf[(Region_gdf['Island Group'] == island_group)].sort_values(by='Total Disaster Count', ascending=True)\n",
|
991 |
+
" x = curr_disaster\n",
|
992 |
+
" y = curr_division\n",
|
993 |
+
" else:\n",
|
994 |
+
" return\n",
|
995 |
+
" # Create stacked bar plot using Plotly Express\n",
|
996 |
+
" bar_fig = px.bar(island_disaster,\n",
|
997 |
+
" x=x, \n",
|
998 |
+
" y=y, \n",
|
999 |
+
" title=disaster_type + \" in \" + island_group + \" per \" + division,\n",
|
1000 |
+
" orientation='h', \n",
|
1001 |
+
" height=750, \n",
|
1002 |
+
" color_discrete_sequence=['lightblue'], \n",
|
1003 |
+
" )\n",
|
1004 |
+
" bar_fig.update_layout(\n",
|
1005 |
+
" xaxis_title=disaster_type, \n",
|
1006 |
+
" yaxis_title=division,\n",
|
1007 |
+
" )\n",
|
1008 |
+
" hover_template = '<b>%{customdata[0]}</b><br>' + disaster_type + ' Count: %{x}<extra></extra>'\n",
|
1009 |
+
" bar_fig.update_traces(hovertemplate=hover_template,\n",
|
1010 |
+
" customdata=Region_gdf[[curr_division]])\n",
|
1011 |
+
"\n",
|
1012 |
+
" return bar_fig\n",
|
1013 |
+
"\n",
|
1014 |
+
"test = update_disaster_bar('Province', 'Volcanic Activity', 'Visayas')\n",
|
1015 |
+
"test.show()"
|
1016 |
+
]
|
1017 |
+
},
|
1018 |
+
{
|
1019 |
+
"cell_type": "markdown",
|
1020 |
+
"metadata": {},
|
1021 |
+
"source": [
|
1022 |
+
"### DISASTER 4: The fourth visualization for the disaster data is a **Horizontal Bar Chart** that shows the number of [Disaster Type] per REGION per [Island Group].\n",
|
1023 |
+
"#### Interactions:\n",
|
1024 |
+
"- DropDown/RadioButtons that switch between Disaster Types (Total, Storm, Flood, Earthquake, Volcanic, Mass Movement, Drought) through Dash (Same button as Disaster 3 visualization)\n",
|
1025 |
+
"- DropDown/RadioButtons that switch between Island Groups (Luzon Visayas Mindanao)"
|
1026 |
+
]
|
1027 |
+
},
|
1028 |
+
{
|
1029 |
+
"cell_type": "code",
|
1030 |
+
"execution_count": null,
|
1031 |
+
"metadata": {},
|
1032 |
+
"outputs": [],
|
1033 |
+
"source": [
|
1034 |
+
"luzon_reg_disaster = Region_gdf[Region_gdf['Island Group'] == 'Luzon'].groupby('Region')['Region_tot'].sum().sort_values(ascending=True)"
|
1035 |
+
]
|
1036 |
+
},
|
1037 |
+
{
|
1038 |
+
"cell_type": "code",
|
1039 |
+
"execution_count": null,
|
1040 |
+
"metadata": {},
|
1041 |
+
"outputs": [],
|
1042 |
+
"source": [
|
1043 |
+
"# Island Group\n",
|
1044 |
+
"luzon_reg_disaster = Region_gdf[Region_gdf['Island Group'] == 'Luzon'].groupby('Region')['Region_tot'].sum().sort_values(ascending=True)\n",
|
1045 |
+
"\n",
|
1046 |
+
"# Create stacked bar plot using Plotly Express\n",
|
1047 |
+
"fig = px.bar(x=luzon_reg_disaster.values, \n",
|
1048 |
+
" y=luzon_reg_disaster.index, \n",
|
1049 |
+
" title='Total Disasters in Luzon per Region',\n",
|
1050 |
+
" orientation='h', \n",
|
1051 |
+
" height=600, \n",
|
1052 |
+
" color_discrete_sequence=['blue'], \n",
|
1053 |
+
" )\n",
|
1054 |
+
"\n",
|
1055 |
+
"fig.update_layout(\n",
|
1056 |
+
" xaxis_title='Total Disasters', \n",
|
1057 |
+
" yaxis_title='Region',\n",
|
1058 |
+
")\n",
|
1059 |
+
"\n",
|
1060 |
+
"hover_template = '<b>%{y}</b><br>%{x} disasters<extra></extra>'\n",
|
1061 |
+
"\n",
|
1062 |
+
"fig.update_traces(hovertemplate=hover_template,\n",
|
1063 |
+
" customdata=luzon_reg_disaster.reset_index().values)\n",
|
1064 |
+
"\n",
|
1065 |
+
"fig.show()\n"
|
1066 |
+
]
|
1067 |
+
},
|
1068 |
+
{
|
1069 |
+
"cell_type": "markdown",
|
1070 |
+
"metadata": {},
|
1071 |
+
"source": [
|
1072 |
+
"**APP IDEAS**\n",
|
1073 |
+
"- There is toggle that switches the visualizations to rollup or drilldown between visualizing by province or by region\n",
|
1074 |
+
"- Disaster 1 and 2 interactions will be connected\n",
|
1075 |
+
"- Disasteer 3 and 4 interactions will be connected\n",
|
1076 |
+
"- Header and stories to be provided as text "
|
1077 |
+
]
|
1078 |
+
}
|
1079 |
+
],
|
1080 |
+
"metadata": {
|
1081 |
+
"kernelspec": {
|
1082 |
+
"display_name": "Python 3",
|
1083 |
+
"language": "python",
|
1084 |
+
"name": "python3"
|
1085 |
+
},
|
1086 |
+
"language_info": {
|
1087 |
+
"codemirror_mode": {
|
1088 |
+
"name": "ipython",
|
1089 |
+
"version": 3
|
1090 |
+
},
|
1091 |
+
"file_extension": ".py",
|
1092 |
+
"mimetype": "text/x-python",
|
1093 |
+
"name": "python",
|
1094 |
+
"nbconvert_exporter": "python",
|
1095 |
+
"pygments_lexer": "ipython3",
|
1096 |
+
"version": "3.11.8"
|
1097 |
+
}
|
1098 |
+
},
|
1099 |
+
"nbformat": 4,
|
1100 |
+
"nbformat_minor": 2
|
1101 |
+
}
|
requirements.txt
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Python 3.11.8
|
2 |
+
# conda install -c conda-forge gdal
|
3 |
+
# matplotlib
|
4 |
+
# seaborn
|
5 |
+
# rasterio
|
6 |
+
# georasters
|
7 |
+
# openpyxl
|
8 |
+
Pyarrow
|
9 |
+
pandas
|
10 |
+
numpy
|
11 |
+
dash
|
12 |
+
plotly
|
13 |
+
geopandas
|
14 |
+
dash-bootstrap-components
|
15 |
+
dash_daq
|
16 |
+
flask-caching
|
17 |
+
traitlets
|
18 |
+
nbformat
|
19 |
+
gunicorn
|
20 |
+
python-dotenv
|