Spaces:
Running
Running
import streamlit as st | |
import streamlit.components.v1 as components | |
import base64 | |
import leafmap.maplibregl as leafmap | |
import altair as alt | |
import ibis | |
from ibis import _ | |
import ibis.selectors as s | |
import os | |
import pandas as pd | |
import geopandas as gpd | |
from shapely import wkb | |
import sqlalchemy | |
import pathlib | |
from typing import Optional | |
from functools import reduce | |
from variables import * | |
def get_summary(ca, combined_filter, column, colors=None): #summary stats, based on filtered data | |
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_10yr = (_.percent_fire_10yr *_.acres).sum()/_.acres.sum(), | |
mean_percent_rxburn_10yr = (_.percent_rxburn_10yr *_.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(ca, column, colors, filter_cols, filter_vals,colorby_vals): # get df for charts + df_tab for printed table | |
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 | |
return df, df_tab | |
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=290) | |
def bar_chart(df, x, y, title): #display summary stats for color_by column | |
#axis label angles / chart size | |
if x == "manager_type": #labels are too long, making vertical | |
angle = 270 | |
height = 373 | |
else: #other labels are horizontal | |
angle = 0 | |
height = 310 | |
# order of bars | |
if x == "established": # order labels in chronological order, not alphabetic. | |
sort = '-x' | |
elif x == "access_type": #order based on levels of openness | |
sort=['Open', 'Restricted', 'No Public', "Unknown"] | |
elif x == "manager_type": | |
sort = ["Federal","Tribal","State","Special District", "County", "City", "HOA","Joint","Non Profit","Private","Unknown"] | |
else: | |
sort = 'x' | |
x_title = next(key for key, value in select_column.items() if value == x) | |
chart = alt.Chart(df).mark_bar().transform_calculate( | |
access_label=f"replace(datum.{x}, ' Access', '')" #omit access from access_type labels so it fits in frame | |
).encode( | |
x=alt.X("access_label:N", | |
axis=alt.Axis(labelAngle=angle, title=x_title), | |
sort=sort), | |
y=alt.Y(y, axis=alt.Axis()), | |
color=alt.Color('color').scale(None) | |
).properties(width="container", height=height, title = title | |
) | |
# sizing for poster | |
# ).configure_title( | |
# fontSize=40 | |
# ).configure_axis( | |
# labelFontSize=24, | |
# titleFontSize=34 | |
# ) | |
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 | |
def fire_style(layer): | |
return {"version": 8, | |
"sources": { | |
"source1": { | |
"type": "vector", | |
"url": "pmtiles://" + url_calfire, | |
"attribution": "CAL FIRE" | |
} | |
}, | |
"layers": [ | |
{ | |
"id": "fire", | |
"source": "source1", | |
"source-layer": layer, | |
"type": "fill", | |
"paint": { | |
"fill-color": "#D22B2B", | |
} | |
} | |
] | |
} | |
def rx_style(layer): | |
return{ | |
"version": 8, | |
"sources": { | |
"source2": { | |
"type": "vector", | |
"url": "pmtiles://" + url_rxburn, | |
"attribution": "CAL FIRE" | |
} | |
}, | |
"layers": [ | |
{ | |
"id": "fire", | |
"source": "source2", | |
"source-layer": layer, | |
# "filter": [">=", ["get", "YEAR_"], year], | |
"type": "fill", | |
"paint": { | |
"fill-color": "#702963", | |
} | |
} | |
] | |
} | |
def get_sv_style(column): | |
return { | |
"layers": [ | |
{ | |
"id": "SVI", | |
"source": column, #need different "source" for multiple pmtiles layers w/ same file | |
"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 | |
] | |
} | |
} | |
] | |
} | |
def get_pmtiles_style(paint, alpha, filter_cols, filter_vals): | |
filters = [] | |
for col, val in zip(filter_cols, filter_vals): | |
filters.append(["match", ["get", col], val, True, False]) | |
combined_filters = ["all"] + filters | |
style = { | |
"version": 8, | |
"sources": { | |
"ca": { | |
"type": "vector", | |
"url": "pmtiles://" + ca_pmtiles, | |
} | |
}, | |
"layers": [ | |
{ | |
"id": "ca30x30", | |
"source": "ca", | |
"source-layer": "layer", | |
"type": "fill", | |
"filter": combined_filters, | |
"paint": { | |
"fill-color": paint, | |
"fill-opacity": alpha | |
} | |
} | |
] | |
} | |
return style | |
def get_pmtiles_style_llm(paint, ids): | |
combined_filters = ["all", ["match", ["get", "id"], ids, True, False]] | |
style = { | |
"version": 8, | |
"sources": { | |
"ca": { | |
"type": "vector", | |
"url": "pmtiles://" + ca_pmtiles, | |
} | |
}, | |
"layers": [ | |
{ | |
"id": "ca30x30", | |
"source": "ca", | |
"source-layer": "layer", | |
"type": "fill", | |
"filter": combined_filters, | |
"paint": { | |
"fill-color": paint, | |
"fill-opacity": 1, | |
# "fill-extrusion-height": 1000 | |
} | |
} | |
] | |
} | |
return style | |