cboettig commited on
Commit
f22d251
Β·
1 Parent(s): d231a5f
Files changed (2) hide show
  1. app.py +103 -69
  2. 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
- import leafmap.foliumap as leafmap
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
- st.set_page_config(layout="wide", page_title="Protected Areas Explorer", page_icon=":globe:")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- #pad_pmtiles = "https://huggingface.co/spaces/boettiger-lab/pad-us/resolve/main/pad-stats.pmtiles"
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, zoom_to_layer=False)
224
  '''
225
 
226
 
@@ -249,21 +274,46 @@ justice40_style = {
249
  }
250
 
251
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
 
253
 
254
  # +
255
- ## Map controls sidebar
256
 
 
 
 
 
 
 
 
 
257
  with st.sidebar:
258
 
259
- if st.toggle("Protected Areas", True):
260
-
261
- style_choice = st.radio("Color by:", style_options)
262
- alpha = st.slider("transparency", 0.0, 1.0, 0.5)
263
- style = pad_style(style_options[style_choice], alpha)
264
- m.add_pmtiles(pad_pmtiles, name="Protected Areas (PAD-US-3)", style=style, overlay=True, show=True, zoom_to_layer=False)
265
- ## Add legend based on selected style?
266
- # m.add_legend(legend_dict=legend_dict)
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, zoom_to_layer=False)
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, zoom_to_layer=False)
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, zoom_to_layer=False)
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, zoom_to_layer=False)
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, zoom_to_layer=False)
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
- palette="greens", name="cropland expansion", transparent_bg=True, opacity = 0.8, zoom_to_layer=False)
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, zoom_to_layer=False)
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, zoom_to_layer=False)
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
- with st.expander("πŸ—Ί Basemaps"):
357
- # radio selector would make more sense
358
- if st.toggle("Topography"):
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
- m.to_streamlit(height=700)
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