riu-rd commited on
Commit
6161ee9
1 Parent(s): 275047c

Added Temp Diff Feature

Browse files
.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: blue
5
- colorTo: green
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