Spaces:
Sleeping
Sleeping
maplibre
Browse files- app.py +103 -69
- requirements.txt +1 -1
app.py
CHANGED
@@ -1,3 +1,4 @@
|
|
|
|
1 |
# -*- coding: utf-8 -*-
|
2 |
# Licensed under the Apache License, Version 2.0 (the "License");
|
3 |
# you may not use this file except in compliance with the License.
|
@@ -11,20 +12,50 @@
|
|
11 |
# See the License for the specific language governing permissions and
|
12 |
# limitations under the License.
|
13 |
|
14 |
-
|
15 |
import streamlit as st
|
|
|
|
|
|
|
16 |
import altair as alt
|
17 |
import ibis
|
18 |
from ibis import _
|
19 |
import ibis.selectors as s
|
20 |
|
21 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
22 |
|
|
|
23 |
#pad_pmtiles = "https://data.source.coop/cboettig/pad-us-3/pad-stats.pmtiles"
|
24 |
#parquet = "https://data.source.coop/cboettig/pad-us-3/pad-stats.parquet"
|
25 |
-
|
26 |
-
#parquet = "https://huggingface.co/spaces/boettiger-lab/pad-us/resolve/main/pad-stats.parquet"
|
27 |
-
pad_pmtiles = "https://huggingface.co/datasets/boettiger-lab/pad-us-3/resolve/main/pad-stats-subset.pmtiles"
|
28 |
parquet = "https://huggingface.co/datasets/boettiger-lab/pad-us-3/resolve/main/pad-stats.parquet"
|
29 |
|
30 |
|
@@ -42,6 +73,7 @@ style_choice = "Manager Type"
|
|
42 |
us_lower_48_area_m2 = 7.8e+12
|
43 |
|
44 |
|
|
|
45 |
## Helper functions
|
46 |
|
47 |
#@st.cache_resource
|
@@ -115,14 +147,7 @@ def pad_style(paint, alpha):
|
|
115 |
|
116 |
|
117 |
|
118 |
-
|
119 |
-
'''
|
120 |
-
# US Protected Area Database Explorer
|
121 |
-
|
122 |
-
'''
|
123 |
-
|
124 |
-
|
125 |
-
m = leafmap.Map(center=[35, -100], zoom=4, layers_control=True)
|
126 |
|
127 |
custom_style = '''
|
128 |
"blue"
|
@@ -220,7 +245,7 @@ style_options = {
|
|
220 |
|
221 |
code_ex='''
|
222 |
m.add_cog_layer("https://data.source.coop/vizzuality/lg-land-carbon-data/natcrop_expansion_100m_cog.tif",
|
223 |
-
palette="oranges", name="Cropland Expansion", transparent_bg=True, opacity = 0.7,
|
224 |
'''
|
225 |
|
226 |
|
@@ -249,21 +274,46 @@ justice40_style = {
|
|
249 |
}
|
250 |
|
251 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
252 |
|
253 |
|
254 |
# +
|
255 |
-
|
256 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
257 |
with st.sidebar:
|
258 |
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
|
268 |
with st.expander("π¦ Biodiversity"):
|
269 |
if st.toggle("Species Richness", False):
|
@@ -280,62 +330,50 @@ with st.sidebar:
|
|
280 |
opacity=0.9
|
281 |
)
|
282 |
#m.add_cog_layer("https://data.source.coop/cboettig/mobi/range-size-rarity-all/RSR_All.tif",
|
283 |
-
# palette="greens", name="Range-Size Rarity", transparent_bg=True, opacity = 0.9,
|
284 |
with st.expander("β
Carbon & Climate"):
|
285 |
if st.toggle("Carbon Lost (2002-2022)"):
|
286 |
m.add_cog_layer("https://data.source.coop/vizzuality/lg-land-carbon-data/deforest_carbon_100m_cog.tif",
|
287 |
-
palette="reds", name="Carbon Lost (2002-2022)", transparent_bg=True, opacity = 0.8,
|
288 |
|
289 |
if st.toggle("Irrecoverable Carbon"):
|
290 |
m.add_cog_layer("https://data.source.coop/cboettig/carbon/cogs/irrecoverable_c_total_2018.tif",
|
291 |
-
palette="purples", name="Irrecoverable Carbon", transparent_bg=True, opacity = 0.8,
|
292 |
|
293 |
if st.toggle("Manageable Carbon"):
|
294 |
m.add_cog_layer("https://data.source.coop/cboettig/carbon/cogs/manageable_c_total_2018.tif",
|
295 |
-
palette="greens", name="Manageable Carbon", transparent_bg=True, opacity = 0.8,
|
296 |
|
297 |
with st.expander("π Human Impacts"):
|
298 |
|
299 |
if st.toggle("Human Impact"):
|
300 |
hi="https://data.source.coop/vizzuality/hfp-100/hfp_2021_100m_v1-2_cog.tif"
|
301 |
-
m.add_cog_layer(hi, palette="purples", name="Human Impact", transparent_bg=True, opacity = 0.8,
|
302 |
|
303 |
if st.toggle("cropland expansion"):
|
304 |
-
m.add_cog_layer("https://data.source.coop/vizzuality/lg-land-carbon-data/natcrop_expansion_100m_cog.tif",
|
305 |
-
|
306 |
|
307 |
if st.toggle("Biodiversity Intactness Loss"):
|
308 |
m.add_cog_layer("https://data.source.coop/vizzuality/lg-land-carbon-data/natcrop_bii_100m_cog.tif",
|
309 |
-
palette="reds", name="biodiversity intactness loss", transparent_bg=True, opacity = 0.8,
|
310 |
|
311 |
if st.toggle("Forest Integrity Loss"):
|
312 |
m.add_cog_layer("https://data.source.coop/vizzuality/lg-land-carbon-data/natcrop_fii_100m_cog.tif",
|
313 |
-
palette="reds", name="forest integrity loss", transparent_bg=True, opacity = 0.8,
|
314 |
-
|
315 |
-
with st.expander("β Equity and Justice"):
|
316 |
-
if st.toggle("Climate & Economic Justice"):
|
317 |
-
m.add_pmtiles(justice40, name="Climate & Economic Justice", style = justice40_style, overlay=True, show=True, zoom_to_layer=False)
|
318 |
-
|
319 |
-
with st.expander("π₯ Wildfire"):
|
320 |
-
# Fire Polygons, USGS
|
321 |
-
if st.toggle("Fire boundaries"):
|
322 |
-
usgs = "https://data.source.coop/cboettig/fire/usgs-mtbs.pmtiles"
|
323 |
-
fire_style = {
|
324 |
-
"version": 8,
|
325 |
-
"sources": {
|
326 |
-
"source1": {
|
327 |
-
"type": "vector",
|
328 |
-
"url": "pmtiles://" + usgs,
|
329 |
-
"attribution": "USGS"}},
|
330 |
-
"layers": [{
|
331 |
-
"id": "usgs",
|
332 |
-
"source": "source1",
|
333 |
-
"source-layer": "mtbs_perims_DD",
|
334 |
-
"type": "fill",
|
335 |
-
"paint": {"fill-color": "#FFA500", "fill-opacity": 0.4}}]}
|
336 |
-
m.add_pmtiles(usgs, name="Fire", style=fire_style, overlay=True, show=True, zoom_to_layer=False)
|
337 |
|
|
|
|
|
|
|
|
|
|
|
|
|
338 |
|
|
|
|
|
|
|
|
|
|
|
339 |
with st.expander("π» Custom code"):
|
340 |
if st.toggle("Custom map layers"):
|
341 |
|
@@ -353,15 +391,9 @@ with st.sidebar:
|
|
353 |
# "custom"]
|
354 |
# )
|
355 |
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
m.add_basemap("Esri.WorldShadedRelief")
|
360 |
-
if st.toggle("Satellite"):
|
361 |
-
m.add_basemap("Esri.WorldImagery")
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
# Map radio buttons to corresponding column:
|
366 |
select_column = {
|
367 |
"GAP Status Code": "gap_code",
|
@@ -391,16 +423,19 @@ colors = (ibis
|
|
391 |
)
|
392 |
|
393 |
|
|
|
|
|
|
|
394 |
main = st.container()
|
395 |
|
396 |
with main:
|
397 |
map_col, stats_col = st.columns([2,1])
|
398 |
|
399 |
with map_col:
|
400 |
-
|
401 |
|
402 |
df = summary_table(column, colors)
|
403 |
-
total_percent = df.percent_protected.sum()
|
404 |
richness_chart = bar_chart(df, column, 'mean_richness')
|
405 |
rsr_chart = bar_chart(df, column, 'mean_rsr')
|
406 |
carbon_lost = bar_chart(df, column, 'carbon_lost')
|
@@ -437,11 +472,9 @@ with main:
|
|
437 |
st.altair_chart(human_impact, use_container_width=True)
|
438 |
|
439 |
|
|
|
440 |
st.divider()
|
441 |
-
|
442 |
footer = st.container()
|
443 |
-
|
444 |
-
|
445 |
with footer:
|
446 |
'''
|
447 |
## Custom queries
|
@@ -489,3 +522,4 @@ with footer:
|
|
489 |
using cloud-native data serializations in COG, PMTiles, and GeoParquet. Coded in pure python using leafmap and duckdb. Map styling with [MapLibre](https://maplibre.org/).
|
490 |
'''
|
491 |
|
|
|
|
1 |
+
# +
|
2 |
# -*- coding: utf-8 -*-
|
3 |
# Licensed under the Apache License, Version 2.0 (the "License");
|
4 |
# you may not use this file except in compliance with the License.
|
|
|
12 |
# See the License for the specific language governing permissions and
|
13 |
# limitations under the License.
|
14 |
|
15 |
+
# +
|
16 |
import streamlit as st
|
17 |
+
import streamlit.components.v1 as components
|
18 |
+
import base64
|
19 |
+
import leafmap.maplibregl as leafmap
|
20 |
import altair as alt
|
21 |
import ibis
|
22 |
from ibis import _
|
23 |
import ibis.selectors as s
|
24 |
|
25 |
+
from typing import Optional
|
26 |
+
def to_streamlit(
|
27 |
+
self,
|
28 |
+
width: Optional[int] = None,
|
29 |
+
height: Optional[int] = 600,
|
30 |
+
scrolling: Optional[bool] = False,
|
31 |
+
**kwargs,
|
32 |
+
):
|
33 |
+
|
34 |
+
try:
|
35 |
+
import streamlit.components.v1 as components
|
36 |
+
import base64
|
37 |
+
|
38 |
+
raw_html = self.to_html().encode("utf-8")
|
39 |
+
raw_html = base64.b64encode(raw_html).decode()
|
40 |
+
return components.iframe(
|
41 |
+
f"data:text/html;base64,{raw_html}",
|
42 |
+
width=width,
|
43 |
+
height=height,
|
44 |
+
scrolling=scrolling,
|
45 |
+
**kwargs,
|
46 |
+
)
|
47 |
+
|
48 |
+
except Exception as e:
|
49 |
+
raise Exception(e)
|
50 |
+
|
51 |
+
|
52 |
+
|
53 |
+
|
54 |
|
55 |
+
# +
|
56 |
#pad_pmtiles = "https://data.source.coop/cboettig/pad-us-3/pad-stats.pmtiles"
|
57 |
#parquet = "https://data.source.coop/cboettig/pad-us-3/pad-stats.parquet"
|
58 |
+
pad_pmtiles = "https://huggingface.co/datasets/boettiger-lab/pad-us-3/resolve/main/pad-stats.pmtiles"
|
|
|
|
|
59 |
parquet = "https://huggingface.co/datasets/boettiger-lab/pad-us-3/resolve/main/pad-stats.parquet"
|
60 |
|
61 |
|
|
|
73 |
us_lower_48_area_m2 = 7.8e+12
|
74 |
|
75 |
|
76 |
+
# +
|
77 |
## Helper functions
|
78 |
|
79 |
#@st.cache_resource
|
|
|
147 |
|
148 |
|
149 |
|
150 |
+
# +
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
151 |
|
152 |
custom_style = '''
|
153 |
"blue"
|
|
|
245 |
|
246 |
code_ex='''
|
247 |
m.add_cog_layer("https://data.source.coop/vizzuality/lg-land-carbon-data/natcrop_expansion_100m_cog.tif",
|
248 |
+
palette="oranges", name="Cropland Expansion", transparent_bg=True, opacity = 0.7, fit_bounds=False)
|
249 |
'''
|
250 |
|
251 |
|
|
|
274 |
}
|
275 |
|
276 |
|
277 |
+
bil_url = "https://huggingface.co/datasets/boettiger-lab/pad-us-3/resolve/main/bil.geojson"
|
278 |
+
bil_fill = {
|
279 |
+
"fill-extrusion-color": {
|
280 |
+
"property": "AtlasCateg",
|
281 |
+
"type": "categorical",
|
282 |
+
"stops": [
|
283 |
+
["America the Beautiful Challenge Grants", "orange"],
|
284 |
+
["Clean Energy and Power", "gray"],
|
285 |
+
["Environmental Remediation", "green"],
|
286 |
+
["Resilience and Ecosystem Restoration", "purple"],
|
287 |
+
["Water Infrastructure", "blue"]
|
288 |
+
],
|
289 |
+
},
|
290 |
+
#"fill-extrusion-height": ["*", .01, ["get", "FundingAmo"]],
|
291 |
+
"fill-extrusion-height": ["*", 50, ["sqrt", ["get", "FundingAmo"]]],
|
292 |
+
"fill-extrusion-opacity": 0.9,
|
293 |
+
}
|
294 |
|
295 |
|
296 |
# +
|
297 |
+
st.set_page_config(layout="wide", page_title="Protected Areas Explorer", page_icon=":globe:")
|
298 |
|
299 |
+
'''
|
300 |
+
# US Protected Area Database Explorer
|
301 |
+
|
302 |
+
'''
|
303 |
+
|
304 |
+
m = leafmap.Map(style="positron")
|
305 |
+
|
306 |
+
# +
|
307 |
with st.sidebar:
|
308 |
|
309 |
+
|
310 |
+
with st.expander("πΊ Basemaps"):
|
311 |
+
# radio selector would make more sense
|
312 |
+
if st.toggle("Topography"):
|
313 |
+
m.add_basemap("Esri.WorldShadedRelief")
|
314 |
+
if st.toggle("Satellite"):
|
315 |
+
m.add_basemap("Esri.WorldImagery")
|
316 |
+
|
317 |
|
318 |
with st.expander("π¦ Biodiversity"):
|
319 |
if st.toggle("Species Richness", False):
|
|
|
330 |
opacity=0.9
|
331 |
)
|
332 |
#m.add_cog_layer("https://data.source.coop/cboettig/mobi/range-size-rarity-all/RSR_All.tif",
|
333 |
+
# palette="greens", name="Range-Size Rarity", transparent_bg=True, opacity = 0.9, fit_bounds=False)
|
334 |
with st.expander("β
Carbon & Climate"):
|
335 |
if st.toggle("Carbon Lost (2002-2022)"):
|
336 |
m.add_cog_layer("https://data.source.coop/vizzuality/lg-land-carbon-data/deforest_carbon_100m_cog.tif",
|
337 |
+
palette="reds", name="Carbon Lost (2002-2022)", transparent_bg=True, opacity = 0.8, fit_bounds=False)
|
338 |
|
339 |
if st.toggle("Irrecoverable Carbon"):
|
340 |
m.add_cog_layer("https://data.source.coop/cboettig/carbon/cogs/irrecoverable_c_total_2018.tif",
|
341 |
+
palette="purples", name="Irrecoverable Carbon", transparent_bg=True, opacity = 0.8, fit_bounds=False)
|
342 |
|
343 |
if st.toggle("Manageable Carbon"):
|
344 |
m.add_cog_layer("https://data.source.coop/cboettig/carbon/cogs/manageable_c_total_2018.tif",
|
345 |
+
palette="greens", name="Manageable Carbon", transparent_bg=True, opacity = 0.8, fit_bounds=False)
|
346 |
|
347 |
with st.expander("π Human Impacts"):
|
348 |
|
349 |
if st.toggle("Human Impact"):
|
350 |
hi="https://data.source.coop/vizzuality/hfp-100/hfp_2021_100m_v1-2_cog.tif"
|
351 |
+
m.add_cog_layer(hi, palette="purples", name="Human Impact", transparent_bg=True, opacity = 0.8, fit_bounds=False)
|
352 |
|
353 |
if st.toggle("cropland expansion"):
|
354 |
+
m.add_cog_layer("https://data.source.coop/vizzuality/lg-land-carbon-data/natcrop_expansion_100m_cog.tif",)
|
355 |
+
# palette="greens", name="cropland expansion", transparent_bg=True, opacity = 0.8, fit_bounds=False)
|
356 |
|
357 |
if st.toggle("Biodiversity Intactness Loss"):
|
358 |
m.add_cog_layer("https://data.source.coop/vizzuality/lg-land-carbon-data/natcrop_bii_100m_cog.tif",
|
359 |
+
palette="reds", name="biodiversity intactness loss", transparent_bg=True, opacity = 0.8, fit_bounds=False)
|
360 |
|
361 |
if st.toggle("Forest Integrity Loss"):
|
362 |
m.add_cog_layer("https://data.source.coop/vizzuality/lg-land-carbon-data/natcrop_fii_100m_cog.tif",
|
363 |
+
palette="reds", name="forest integrity loss", transparent_bg=True, opacity = 0.8, fit_bounds=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
364 |
|
365 |
+
if st.toggle("Protected Areas", True):
|
366 |
+
|
367 |
+
style_choice = st.radio("Color by:", style_options)
|
368 |
+
alpha = st.slider("transparency", 0.0, 1.0, 0.5)
|
369 |
+
style = pad_style(style_options[style_choice], alpha)
|
370 |
+
m.add_pmtiles(pad_pmtiles, style=style, visible=True, opacity=alpha, tooltip=True)
|
371 |
|
372 |
+
|
373 |
+
with st.expander("π° Conservation Investment"):
|
374 |
+
if st.toggle("Bipartisan Infrastructure Law"):
|
375 |
+
m.add_geojson(bil_url, layer_type="fill-extrusion", paint=bil_fill, name="BIL", fit_bounds=False)
|
376 |
+
|
377 |
with st.expander("π» Custom code"):
|
378 |
if st.toggle("Custom map layers"):
|
379 |
|
|
|
391 |
# "custom"]
|
392 |
# )
|
393 |
|
394 |
+
|
395 |
+
# +
|
396 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
397 |
# Map radio buttons to corresponding column:
|
398 |
select_column = {
|
399 |
"GAP Status Code": "gap_code",
|
|
|
423 |
)
|
424 |
|
425 |
|
426 |
+
# +
|
427 |
+
|
428 |
+
|
429 |
main = st.container()
|
430 |
|
431 |
with main:
|
432 |
map_col, stats_col = st.columns([2,1])
|
433 |
|
434 |
with map_col:
|
435 |
+
to_streamlit(m, height=700)
|
436 |
|
437 |
df = summary_table(column, colors)
|
438 |
+
total_percent = df.percent_protected.sum().round(1)
|
439 |
richness_chart = bar_chart(df, column, 'mean_richness')
|
440 |
rsr_chart = bar_chart(df, column, 'mean_rsr')
|
441 |
carbon_lost = bar_chart(df, column, 'carbon_lost')
|
|
|
472 |
st.altair_chart(human_impact, use_container_width=True)
|
473 |
|
474 |
|
475 |
+
# +
|
476 |
st.divider()
|
|
|
477 |
footer = st.container()
|
|
|
|
|
478 |
with footer:
|
479 |
'''
|
480 |
## Custom queries
|
|
|
522 |
using cloud-native data serializations in COG, PMTiles, and GeoParquet. Coded in pure python using leafmap and duckdb. Map styling with [MapLibre](https://maplibre.org/).
|
523 |
'''
|
524 |
|
525 |
+
|
requirements.txt
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
pandas
|
2 |
pydeck
|
3 |
pmtiles
|
4 |
-
leafmap
|
5 |
ibis-framework[duckdb]
|
6 |
openai
|
7 |
streamlit==1.35.0
|
|
|
1 |
pandas
|
2 |
pydeck
|
3 |
pmtiles
|
4 |
+
leafmap[maplibre]
|
5 |
ibis-framework[duckdb]
|
6 |
openai
|
7 |
streamlit==1.35.0
|