ca-30x30-folium / app.py
cassiebuhler's picture
updating fire labels
2b288ca
raw
history blame
29.2 kB
import streamlit as st
import streamlit.components.v1 as components
import base64
# import leafmap.maplibregl as leafmap
import leafmap.foliumap as leafmap
import altair as alt
import ibis
from ibis import _
import ibis.selectors as s
# urls for main layer
ca_pmtiles = "https://huggingface.co/datasets/boettiger-lab/ca-30x30/resolve/main/cpad-stats.pmtiles"
# ca_parquet = "https://huggingface.co/datasets/boettiger-lab/ca-30x30/resolve/main/cpad-stats.parquet"
ca_parquet = "cpad-stats.parquet" #local copy is faster
ca_area_acres = 1.014e8 #acres
style_choice = "GAP Status Code"
con = ibis.duckdb.connect(extensions=["spatial"])
ca = (con
.read_parquet(ca_parquet)
.cast({"geom": "geometry"})
)
# urls for additional data layers
url_sr = "https://huggingface.co/datasets/boettiger-lab/ca-30x30/resolve/main/species-richness-ca/{z}/{x}/{y}.png"
url_rsr = "https://huggingface.co/datasets/boettiger-lab/ca-30x30/resolve/main/range-size-rarity/{z}/{x}/{y}.png"
url_irr_carbon = "https://huggingface.co/datasets/boettiger-lab/ca-30x30/resolve/main/ca_irrecoverable_c_2018_cog.tif"
url_man_carbon = "https://huggingface.co/datasets/boettiger-lab/ca-30x30/resolve/main/ca_manageable_c_2018_cog.tif"
url_svi = "https://data.source.coop/cboettig/social-vulnerability/svi2020_us_county.pmtiles"
url_justice40 = "https://data.source.coop/cboettig/justice40/disadvantaged-communities.pmtiles"
url_loss_carbon = "https://huggingface.co/datasets/boettiger-lab/ca-30x30/resolve/main/deforest-carbon-ca/{z}/{x}/{y}.png"
url_hi = "https://huggingface.co/datasets/boettiger-lab/ca-30x30/resolve/main/ca_human_impact_cog.tif"
url_calfire = "https://huggingface.co/datasets/boettiger-lab/ca-30x30/resolve/main/cal_fire_2022.pmtiles"
url_rxburn = "https://huggingface.co/datasets/boettiger-lab/ca-30x30/resolve/main/cal_rxburn_2022.pmtiles"
# colors for plotting
private_access_color = "#DE881E" # orange
public_access_color = "#3388ff" # blue
tribal_color = "#BF40BF" # purple
mixed_color = "#005a00" # green
year2023_color = "#26542C" # green
year2024_color = "#F3AB3D" # orange
federal_color = "#529642" # green
state_color = "#A1B03D" # light green
local_color = "#365591" # blue
special_color = "#0096FF" # blue
private_color = "#7A3F1A" # brown
joint_color = "#DAB0AE" # light pink
county_color = "#DE3163" # magenta
city_color = "#ADD8E6" #light blue
hoa_color = "#A89BBC" # purple
nonprofit_color = "#D77031" #orange
justice40_color = "#00008B" #purple
svi_color = "#850101" #red
white = "#FFFFFF"
# gap codes 3 and 4 are off by default.
default_gap = {
3: False,
4: False,
}
from functools import reduce
def get_summary(ca, combined_filter, column, colors=None): #summary stats, based on filtered data
# ca = ca.filter(_.reGAP.isin([1,2])) #only gap 1 and 2
df = ca.filter(combined_filter)
df = (df
.group_by(*column) # unpack the list for grouping
.aggregate(percent_protected=100 * _.Acres.sum() / ca_area_acres,
mean_richness = (_.richness * _.Acres).sum() / _.Acres.sum(),
mean_rsr = (_.rsr * _.Acres).sum() / _.Acres.sum(),
mean_irrecoverable_carbon = (_.irrecoverable_carbon * _.Acres).sum() / _.Acres.sum(),
mean_manageable_carbon = (_.manageable_carbon * _.Acres).sum() / _.Acres.sum(),
mean_percent_fire_20yr = (_.percent_fire_20yr *_.Acres).sum()/_.Acres.sum(),
mean_percent_fire_10yr = (_.percent_fire_10yr *_.Acres).sum()/_.Acres.sum(),
mean_percent_fire_5yr = (_.percent_fire_5yr *_.Acres).sum()/_.Acres.sum(),
mean_percent_fire_2yr = (_.percent_fire_2yr *_.Acres).sum()/_.Acres.sum(),
mean_percent_rxburn_20yr = (_.percent_rxburn_20yr *_.Acres).sum()/_.Acres.sum(),
mean_percent_rxburn_10yr = (_.percent_rxburn_10yr *_.Acres).sum()/_.Acres.sum(),
mean_percent_rxburn_5yr = (_.percent_rxburn_5yr *_.Acres).sum()/_.Acres.sum(),
mean_percent_rxburn_2yr = (_.percent_rxburn_2yr *_.Acres).sum()/_.Acres.sum(),
mean_percent_disadvantaged = (_.percent_disadvantaged * _.Acres).sum() / _.Acres.sum(),
mean_svi = (_.svi * _.Acres).sum() / _.Acres.sum(),
mean_svi_socioeconomic_status = (_.svi_socioeconomic_status * _.Acres).sum() / _.Acres.sum(),
mean_svi_household_char = (_.svi_household_char * _.Acres).sum() / _.Acres.sum(),
mean_svi_racial_ethnic_minority = (_.svi_racial_ethnic_minority * _.Acres).sum() / _.Acres.sum(),
mean_svi_housing_transit = (_.svi_housing_transit * _.Acres).sum() / _.Acres.sum(),
mean_carbon_lost = (_.deforest_carbon * _.Acres).sum() / _.Acres.sum(),
mean_human_impact = (_.human_impact * _.Acres).sum() / _.Acres.sum(),
)
.mutate(percent_protected=_.percent_protected.round(1))
)
if colors is not None and not colors.empty: #only the df will have colors, df_tab doesn't since we are printing it.
df = df.inner_join(colors, column)
df = df.cast({col: "string" for col in column})
df = df.to_pandas()
return df
def summary_table(column, colors, filter_cols, filter_vals,colorby_vals): # get df for charts + df_tab for printed table + df_percent for percentage (only gap 1 and 2)
filters = []
if filter_cols and filter_vals: #if a filter is selected, add to list of filters
for filter_col, filter_val in zip(filter_cols, filter_vals):
if len(filter_val) > 1:
filters.append(getattr(_, filter_col).isin(filter_val))
else:
filters.append(getattr(_, filter_col) == filter_val[0])
if column not in filter_cols: #show color_by column in table by adding it as a filter (if it's not already a filter)
filter_cols.append(column)
filters.append(getattr(_, column).isin(colorby_vals[column]))
combined_filter = reduce(lambda x, y: x & y, filters) #combining all the filters into ibis filter expression
df = get_summary(ca, combined_filter, [column], colors) # df used for charts
df_tab = get_summary(ca, combined_filter, filter_cols, colors = None) #df used for printed table
df_percent = get_summary(ca.filter(_.reGAP.isin([1,2])), combined_filter, [column], colors) # only gap 1 and 2 count towards percentage
return df, df_tab, df_percent
def area_plot(df, column): #percent protected pie chart
base = alt.Chart(df).encode(
alt.Theta("percent_protected:Q").stack(True),
)
pie = ( base
.mark_arc(innerRadius= 40, outerRadius=100)
.encode(alt.Color("color:N").scale(None).legend(None),
tooltip=['percent_protected', column])
)
text = ( base
.mark_text(radius=80, size=14, color="white")
.encode(text = column + ":N")
)
plot = pie # pie + text
return plot.properties(width="container", height=300)
def get_pmtiles_style(paint, alpha, cols, values): #style depends on the filters selected.
filters = []
for col, val in zip(cols, values):
filter_condition = ["match", ["get", col], val, True, False]
filters.append(filter_condition)
combined_filter = ["all"] + filters
return {
"version": 8,
"sources": {
"ca": {
"type": "vector",
"url": "pmtiles://" + ca_pmtiles,
}
},
"layers": [{
"id": "ca30x30",
"source": "ca",
"source-layer": "layer",
"type": "fill",
"filter": combined_filter, # Use the combined filter
"paint": {
"fill-color": paint,
"fill-opacity": alpha
}
}]
}
def bar_chart(df, x, y): #display summary stats for color_by column
chart = alt.Chart(df).mark_bar().encode(
x=x,
y=y,
color=alt.Color('color').scale(None)
).properties(width="container", height=300)
return chart
def getButtons(style_options, style_choice, default_gap=None): #finding the buttons selected to use as filters
column = style_options[style_choice]['property']
opts = [style[0] for style in style_options[style_choice]['stops']]
default_gap = default_gap or {}
buttons = {
name: st.checkbox(f"{name}", value=default_gap.get(name, True), key=column + str(name))
for name in opts
}
filter_choice = [key for key, value in buttons.items() if value] # return only selected
d = {}
d[column] = filter_choice
return d
def getColorVals(style_options, style_choice):
#df_tab only includes filters selected, we need to manually add "color_by" column (if it's not already a filter).
column = style_options[style_choice]['property']
opts = [style[0] for style in style_options[style_choice]['stops']]
d = {}
d[column] = opts
return d
manager = {
'property': 'manager_type',
'type': 'categorical',
'stops': [
['Federal', federal_color],
['State', state_color],
['Non Profit', nonprofit_color],
['Special District', special_color],
['Unknown', "grey"],
['County', county_color],
['City', city_color],
['Joint', joint_color],
['Tribal', tribal_color],
['Private', private_color],
['HOA', hoa_color]
]
}
easement = {
'property': 'Easement',
'type': 'categorical',
'stops': [
['Fee', public_access_color],
['Easement', private_access_color]
]
}
year = {
'property': 'established',
'type': 'categorical',
'stops': [
[2023, year2023_color],
[2024, year2024_color]
]
}
access = {
'property': 'access_type',
'type': 'categorical',
'stops': [
['Open Access', public_access_color],
['No Public Access', private_access_color],
['Unknown Access', "grey"],
['Restricted Access', tribal_color]
]
}
gap = {
'property': 'reGAP',
'type': 'categorical',
'stops': [
[1, "#26633d"],
[2, "#879647"],
[3, "#BBBBBB"],
[4, "#F8F8F8"]
]
}
style_options = {
"Year": year,
"GAP Status Code": gap,
"Manager Type": manager,
"Easement": easement,
"Public Access": access,
}
justice40_fill = {
'property': 'Disadvan',
'type': 'categorical',
'stops': [
[0, white],
[1, justice40_color]
]
}
def get_justice40_style(url_justice40,justice40_fill,alpha):
return {
"version": 8,
"sources": {
"source1": {
"type": "vector",
"url": "pmtiles://" + url_justice40,
"attribution": "Justice40"
}
},
"layers": [
{
"id": "layer1",
"source": "source1",
"source-layer": "DisadvantagedCommunitiesCEJST",
"filter": ["match", ["get", "StateName"], "California", True, False],
"type": "fill",
"paint": {
"fill-color": justice40_fill,
"fill-opacity": alpha
}
}
]
}
def get_sv_style(url,column,alpha):
return {
'version': 8,
'sources': {
'svi_source': {
'type': 'vector',
'url': "pmtiles://" + url,
'attribution': 'Social Vulnerability Index'
}
},
"layers": [
{
"id": "SVI",
"source": "svi_source",
"source-layer": "SVI2020_US_county",
"filter": ["match", ["get", "STATE"], "California", True, False],
"type": "fill",
"paint": {
"fill-color": [
"interpolate", ["linear"], ["get", column],
0, white,
1, svi_color
],
"fill-opacity": alpha
}
}
]
}
def get_fire_style(layer,alpha):
return {
'version': 8,
'sources': {
'source2': {
'type': 'vector',
'url': "pmtiles://" + url_calfire,
'attribution': 'Historical Fire Perimeters'
}
},
"layers": [
{
"id": "fire",
"source": "source2",
"source-layer": layer,
"type": "fill",
"paint": {
"fill-color": "#D22B2B",
"fill-opacity": alpha
}
}
]
}
def get_rx_style(layer,alpha):
return {
'version': 8,
'sources': {
'source2': {
'type': 'vector',
'url': "pmtiles://" + url_rxburn,
'attribution': 'Prescribed Burns'
}
},
"layers": [
{
"id": "fire",
"source": "source2",
"source-layer": layer,
"type": "fill",
"paint": {
"fill-color": "#702963",
"fill-opacity": alpha
}
}
]
}
st.set_page_config(layout="wide", page_title="CA Protected Areas Explorer", page_icon=":globe:")
'''
# CA 30X30 Prototype (Safari/iOS Compatible)
An interactive cloud-native geospatial tool for exploring and visualizing California’s protected lands through open data and generative AI.
- ⬅️ Use the left sidebar to color-code the map by different attributes, toggle on data layers and view summary charts, or filter data.
- ℹ️ For non-Safari/iOS users, see [this version](https://huggingface.co/spaces/boettiger-lab/ca-30x30) for a cleaner tooltip display.
'''
st.divider()
# m = leafmap.Map(style="positron")
m = leafmap.Map()
m.add_basemap("CartoDB.PositronNoLabels")
filters = {}
with st.sidebar:
color_choice = st.radio("Color by:", style_options)
colorby_vals = getColorVals(style_options, color_choice) #get options for selected color_by column
alpha = st.slider("transparency", 0.0, 1.0, 0.5)
st.divider()
"Data Layers:"
# Biodiversity Section
with st.expander("🦜 Biodiversity"):
a_bio = st.slider("transparency", 0.0, 1.0, 0.4, key = "biodiversity")
show_richness = st.toggle("Species Richness")
show_rsr = st.toggle("Range-Size Rarity")
if show_richness:
m.add_tile_layer(url_sr, name="MOBI Species Richness", attribution = "MOBI", opacity=a_bio)
if show_rsr:
m.add_tile_layer(url_rsr, name="MOBI Range-Size Rarity",attribution = "MOBI", opacity=a_bio)
#Carbon Section
with st.expander("⛅ Carbon & Climate"):
a_climate = st.slider("transparency", 0.0, 1.0, 0.3, key = "climate")
show_irrecoverable_carbon = st.toggle("Irrecoverable Carbon")
show_manageable_carbon = st.toggle("Manageable Carbon")
if show_irrecoverable_carbon:
m.add_cog_layer(url_irr_carbon, palette="reds", name="Irrecoverable Carbon (2010-2018)", attribution = "Conservation International", opacity = a_climate, fit_bounds=False)
if show_manageable_carbon:
m.add_cog_layer(url_man_carbon, palette="purples", name="Manageable Carbon (2010-2018)", attribution = "Conservation International", opacity = a_climate, fit_bounds=False)
# Fire Section
with st.expander("🔥 Fire"):
a_fire = st.slider("transparency", 0.0, 1.0, 0.3, key = "fire")
show_fire_20 = st.toggle("Fires (2003-2022)")
show_fire_10 = st.toggle("Fires (2013-2022)")
show_fire_5 = st.toggle("Fires (2018-2022)")
show_fire_2 = st.toggle("Fires (2022)")
show_rx_20 = st.toggle("Prescribed Burns (2003-2022)")
show_rx_10 = st.toggle("Prescribed Burns (2013-2022)")
show_rx_5 = st.toggle("Prescribed Burns (2018-2022)")
show_rx_2 = st.toggle("Prescribed Burns (2022)")
if show_fire_20:
m.add_pmtiles(url_calfire, style=get_fire_style("layer1",a_fire), name="CAL FIRE Fire Polygons (2003-2022)", tooltip=False, zoom_to_layer = True)
if show_fire_10:
m.add_pmtiles(url_calfire, style=get_fire_style("layer2",a_fire), name="CAL FIRE Fire Polygons (2013-2022)", tooltip=False, zoom_to_layer = True)
if show_fire_5:
m.add_pmtiles(url_calfire, style=get_fire_style("layer3",a_fire), name="CAL FIRE Fire Polygons (2018-2022)", tooltip=False, zoom_to_layer = True)
if show_fire_2:
m.add_pmtiles(url_calfire, style=get_fire_style("layer4",a_fire), name="CAL FIRE Fire Polygons (2022)", tooltip=False, zoom_to_layer = True)
if show_rx_20:
m.add_pmtiles(url_rxburn, style=get_rx_style("layer1",a_fire), name="CAL FIRE Prescribed Burns (2003-2022)", tooltip=False, zoom_to_layer = True)
if show_rx_10:
m.add_pmtiles(url_rxburn, style=get_rx_style("layer2",a_fire), name="CAL FIRE Prescribed Burns (2013-2022)", tooltip=False, zoom_to_layer = True)
if show_rx_5:
m.add_pmtiles(url_rxburn, style=get_rx_style("layer3",a_fire), name="CAL FIRE Prescribed Burns (2018-2022)", tooltip=False, zoom_to_layer = True)
if show_rx_2:
m.add_pmtiles(url_rxburn, style=get_rx_style("layer4",a_fire), name="CAL FIRE Prescribed Burns (2022)", tooltip=False, zoom_to_layer = True)
# Justice40 Section
with st.expander("🌱 Climate & Economic Justice"):
a_justice = st.slider("transparency", 0.0, 1.0, 0.3, key = "social justice")
show_justice40 = st.toggle("Justice40")
if show_justice40:
justice_style = get_justice40_style(url_justice40,justice40_fill,a_justice)
m.add_pmtiles(url_justice40, style=justice_style, name="Justice40", tooltip=False, zoom_to_layer = False)
# SVI Section
with st.expander("🏡 Social Vulnerability"):
a_svi = st.slider("transparency", 0.0, 1.0, 0.3, key = "SVI")
show_sv = st.toggle("Social Vulnerability Index (SVI)")
show_sv_socio = st.toggle("Socioeconomic Status")
show_sv_household = st.toggle("Household Characteristics")
show_sv_minority = st.toggle("Racial & Ethnic Minority Status")
show_sv_housing = st.toggle("Housing Type & Transportation")
if show_sv:
m.add_pmtiles(url_svi, style = get_sv_style(url_svi, "RPL_THEMES",a_svi), tooltip=False, name = "SVI (2020)", zoom_to_layer = False)
if show_sv_socio:
m.add_pmtiles(url_svi, style = get_sv_style(url_svi, "RPL_THEME1",a_svi), tooltip=False, name = "Socioeconomic Status - SVI (2020)", zoom_to_layer = False)
if show_sv_household:
m.add_pmtiles(url_svi, style = get_sv_style(url_svi, "RPL_THEME2",a_svi), tooltip=False, name = "Household Characteristics - SVI (2020)", zoom_to_layer = False)
if show_sv_minority:
m.add_pmtiles(url_svi, style = get_sv_style(url_svi, "RPL_THEME3",a_svi), tooltip=False, name = "Racial & Ethnic Minority Status - SVI (2020)", zoom_to_layer = False)
if show_sv_housing:
m.add_pmtiles(url_svi, style = get_sv_style(url_svi, "RPL_THEME4",a_svi), tooltip=False, name = "Housing Type & Transportation - SVI (2020)", zoom_to_layer = False)
# HI Section
with st.expander("🚜 Human Impacts"):
a_hi = st.slider("transparency", 0.0, 1.0, 0.5, key = "hi")
show_carbon_lost = st.toggle("Deforested Carbon (2002-2022)")
show_human_impact = st.toggle("Human Footprint (2017-2021)")
if show_carbon_lost:
m.add_tile_layer(url_loss_carbon, name="Deforested Carbon (2002-2022)",attribution = "Gassert et al. (2023)", opacity = a_hi)
if show_human_impact:
m.add_cog_layer(url_hi, name="Human Footprint (2017-2021)", attribution = "Gassert et al. (2023)", opacity = a_hi, fit_bounds=False)
st.divider()
"Filters:"
for label in style_options: # get selected filters (based on the buttons selected)
with st.expander(label):
if label == "GAP Status Code": # gap code 1 and 2 are on by default
opts = getButtons(style_options, label, default_gap)
else: # other buttons are not on by default.
opts = getButtons(style_options, label)
filters.update(opts)
selected = {k: v for k, v in filters.items() if v}
if selected:
filter_cols = list(selected.keys())
filter_vals = list(selected.values())
else:
filter_cols = []
filter_vals = []
# Display CA 30x30 Data
style = get_pmtiles_style(style_options[color_choice], alpha, filter_cols, filter_vals)
legend_d = {cat: color for cat, color in style_options[color_choice]['stops']}
m.add_legend(legend_dict = legend_d)
m.add_pmtiles(ca_pmtiles, style=style, name="CA 30x30", tooltip=True, overlay = True)
select_column = {
"Year": "established",
"GAP Status Code": "reGAP",
"Manager Type": "manager_type",
"Easement": "Easement",
"Public Access": "access_type",
}
column = select_column[color_choice]
select_colors = {
"Year": year["stops"],
"GAP Status Code": gap["stops"],
"Manager Type": manager["stops"],
"Easement": easement["stops"],
"Public Access": access["stops"],
}
colors = (
ibis
.memtable(select_colors[color_choice], columns=[column, "color"])
.to_pandas()
)
# get summary tables used for charts + printed table + percentage
# df - charts; df_tab - printed table (omits colors) + df_percent - only gap codes 1 & 2 count towards percentage
df,df_tab,df_percent = summary_table(column, colors, filter_cols, filter_vals, colorby_vals)
# compute area covered (only gap 1 and 2)
# df_onlygap = df[df.reGAP.isin([1,2])]
total_percent = df_percent.percent_protected.sum().round(1)
# charts displayed based on color_by variable
richness_chart = bar_chart(df, column, 'mean_richness')
rsr_chart = bar_chart(df, column, 'mean_rsr')
irr_carbon_chart = bar_chart(df, column, 'mean_irrecoverable_carbon')
man_carbon_chart = bar_chart(df, column, 'mean_manageable_carbon')
fire_20_chart = bar_chart(df, column, 'mean_percent_fire_20yr')
fire_10_chart = bar_chart(df, column, 'mean_percent_fire_10yr')
fire_5_chart = bar_chart(df, column, 'mean_percent_fire_5yr')
fire_2_chart = bar_chart(df, column, 'mean_percent_fire_2yr')
rx_20_chart = bar_chart(df, column, 'mean_percent_rxburn_20yr')
rx_10_chart = bar_chart(df, column, 'mean_percent_rxburn_10yr')
rx_5_chart = bar_chart(df, column, 'mean_percent_rxburn_5yr')
rx_2_chart = bar_chart(df, column, 'mean_percent_rxburn_2yr')
justice40_chart = bar_chart(df, column, 'mean_percent_disadvantaged')
svi_chart = bar_chart(df, column, 'mean_svi')
svi_socio_chart = bar_chart(df, column, 'mean_svi_socioeconomic_status')
svi_house_chart = bar_chart(df, column, 'mean_svi_household_char')
svi_minority_chart = bar_chart(df, column, 'mean_svi_racial_ethnic_minority')
svi_transit_chart = bar_chart(df, column, 'mean_svi_housing_transit')
carbon_loss_chart = bar_chart(df, column, 'mean_carbon_lost')
hi_chart = bar_chart(df, column, 'mean_human_impact')
main = st.container()
with main:
map_col, stats_col = st.columns([2,1])
with map_col:
m.to_streamlit(scrolling = True)
st.dataframe(df_tab, use_container_width = True)
with stats_col:
with st.container():
f"{total_percent}% CA Covered"
st.altair_chart(area_plot(df_percent, column), use_container_width=True)
if show_richness:
"Species Richness"
st.altair_chart(richness_chart, use_container_width=True)
if show_rsr:
"Range-Size Rarity"
st.altair_chart(rsr_chart, use_container_width=True)
if show_irrecoverable_carbon:
"Irrecoverable Carbon"
st.altair_chart(irr_carbon_chart, use_container_width=True)
if show_manageable_carbon:
"Manageable Carbon"
st.altair_chart(man_carbon_chart, use_container_width=True)
if show_fire_20:
"Fires (2003-2022)"
st.altair_chart(fire_20_chart, use_container_width=True)
if show_fire_10:
"Fires (2013-2022)"
st.altair_chart(fire_10_chart, use_container_width=True)
if show_fire_5:
"Fires (2018-2022)"
st.altair_chart(fire_5_chart, use_container_width=True)
if show_fire_2:
"Fires (2022)"
st.altair_chart(fire_2_chart, use_container_width=True)
if show_rx_20:
"Prescribed Burns (2003-2022)"
st.altair_chart(rx_20_chart, use_container_width=True)
if show_rx_10:
"Prescribed Burns (2013-2022)"
st.altair_chart(rx_10_chart, use_container_width=True)
if show_rx_5:
"Prescribed Burns (2018-2022)"
st.altair_chart(rx_5_chart, use_container_width=True)
if show_rx_2:
"Prescribed Burns (2022)"
st.altair_chart(rx_2_chart, use_container_width=True)
if show_justice40:
"Justice40"
st.altair_chart(justice40_chart, use_container_width=True)
if show_sv:
"Social Vulnerability Index"
st.altair_chart(svi_chart, use_container_width=True)
if show_sv_socio:
"SVI - Socioeconomic Status"
st.altair_chart(svi_socio_chart, use_container_width=True)
if show_sv_household:
"SVI - Household Characteristics"
st.altair_chart(svi_house_chart, use_container_width=True)
if show_sv_minority:
"SVI - Racial and Ethnic Minority"
st.altair_chart(svi_minority_chart, use_container_width=True)
if show_sv_housing:
"SVI - Housing Type and Transit"
st.altair_chart(svi_transit_chart, use_container_width=True)
if show_carbon_lost:
"Deforested Carbon (2002-2022)"
st.altair_chart(carbon_loss_chart, use_container_width=True)
if show_human_impact:
"Human Footprint (2017-2021)"
st.altair_chart(hi_chart, use_container_width=True)
st.divider()
footer = st.container()
st.caption("***The label 'established' is inferred from the California Protected Areas Database, which may introduce artifacts. For details on our methodology, please refer to our code: https://github.com/boettiger-lab/ca-30x30.")
st.caption("***Under California’s 30x30 framework, only GAP codes 1 and 2 are counted toward the conservation goal. While our dashboard displays GAP codes 1-4 for reference, the '25.2% of CA Covered' statistic reflects only GAP codes 1 and 2, as designated by CA 30x30 criteria.")
'''
## Credits
Authors: Cassie Buhler & Carl Boettiger, UC Berkeley
License: BSD-2-clause
Data: https://huggingface.co/datasets/boettiger-lab/ca-30x30
### Data sources
- California Protected Areas Database by CA Nature. Data: https://www.californianature.ca.gov/datasets/CAnature::30x30-conserved-areas-terrestrial-2024/about. License: Public Domain
- Imperiled Species Richness and Range-Size-Rarity from NatureServe (2022). Data: https://beta.source.coop/repositories/cboettig/mobi. License CC-BY-NC-ND
- Irrecoverable Carbon from Conservation International, reprocessed to COG on https://beta.source.coop/cboettig/carbon, citation: https://doi.org/10.1038/s41893-021-00803-6, License: CC-BY-NC
- Fire polygons by CAL FIRE (2022), reprocessed to PMTiles on https://beta.source.coop/cboettig/fire/. License: Public Domain
- Climate and Economic Justice Screening Tool, US Council on Environmental Quality, Justice40, data: https://beta.source.coop/repositories/cboettig/justice40/description/, License: Public Domain
- CDC 2020 Social Vulnerability Index by US Census Track. Data: https://source.coop/repositories/cboettig/social-vulnerability/description. License: Public Domain
- Carbon-loss by Vizzuality, on https://beta.source.coop/repositories/vizzuality/lg-land-carbon-data. Citation: https://doi.org/10.1101/2023.11.01.565036, License: CC-BY
- Human Footprint by Vizzuality, on https://beta.source.coop/repositories/vizzuality/hfp-100. Citation: https://doi.org/10.3389/frsen.2023.1130896, License: Public Domain
'''