Spaces:
Running
Running
Commit
·
56784d5
1
Parent(s):
78fca1a
wip; ecoregion + filter updates
Browse filesseparated gap 3 and 4, ecoregions for non-conserved land, synced 30x30 status/gap filters, and fixed pmtiles filters
- app/app.py +39 -11
- app/utils.py +60 -15
- app/variables.py +28 -17
- preprocess/preprocess.ipynb +91 -4
app/app.py
CHANGED
|
@@ -25,7 +25,8 @@ current_tables = con.list_tables()
|
|
| 25 |
if "mydata" not in set(current_tables):
|
| 26 |
tbl = con.read_parquet(ca_parquet)
|
| 27 |
con.create_table("mydata", tbl)
|
| 28 |
-
|
|
|
|
| 29 |
ca = con.table("mydata")
|
| 30 |
|
| 31 |
|
|
@@ -190,12 +191,22 @@ def run_sql(query,color_choice):
|
|
| 190 |
elif ("id" and "geom" in result.columns):
|
| 191 |
style = get_pmtiles_style_llm(style_options[color_choice], result["id"].tolist())
|
| 192 |
legend_d = {cat: color for cat, color in style_options[color_choice]['stops']}
|
| 193 |
-
|
|
|
|
|
|
|
|
|
|
| 194 |
# shorten legend for ecoregions
|
| 195 |
if color_choice == "Ecoregion":
|
|
|
|
|
|
|
|
|
|
|
|
|
| 196 |
legend_d = {key.replace("California", "CA"): value for key, value in legend_d.items()}
|
| 197 |
-
|
| 198 |
-
|
|
|
|
|
|
|
|
|
|
| 199 |
m.add_pmtiles(ca_pmtiles, style=style, opacity=alpha, tooltip=True, fit_bounds=True)
|
| 200 |
m.fit_bounds(result.total_bounds.tolist())
|
| 201 |
result = result.drop('geom',axis = 1) #printing to streamlit so I need to drop geom
|
|
@@ -350,9 +361,14 @@ with st.sidebar:
|
|
| 350 |
|
| 351 |
st.divider()
|
| 352 |
st.markdown('<p class = "medium-font-sidebar"> Filters:</p>', help = "Apply filters to adjust what data is shown on the map.", unsafe_allow_html= True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 353 |
for label in style_options: # get selected filters (based on the buttons selected)
|
| 354 |
with st.expander(label):
|
| 355 |
-
if label
|
| 356 |
opts = getButtons(style_options, label, default_gap)
|
| 357 |
else: # other buttons are not on by default.
|
| 358 |
opts = getButtons(style_options, label)
|
|
@@ -365,7 +381,7 @@ with st.sidebar:
|
|
| 365 |
else:
|
| 366 |
filter_cols = []
|
| 367 |
filter_vals = []
|
| 368 |
-
|
| 369 |
st.divider()
|
| 370 |
st.markdown("""
|
| 371 |
<p class="medium-font-sidebar">
|
|
@@ -376,12 +392,23 @@ with st.sidebar:
|
|
| 376 |
if 'out' not in locals():
|
| 377 |
style = get_pmtiles_style(style_options[color_choice], alpha, filter_cols, filter_vals)
|
| 378 |
legend_d = {cat: color for cat, color in style_options[color_choice]['stops']}
|
| 379 |
-
|
|
|
|
|
|
|
|
|
|
| 380 |
# shorten legend for ecoregions
|
| 381 |
if color_choice == "Ecoregion":
|
|
|
|
|
|
|
|
|
|
|
|
|
| 382 |
legend_d = {key.replace("California", "CA"): value for key, value in legend_d.items()}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 383 |
|
| 384 |
-
m.add_legend(legend_dict = legend_d, position =
|
| 385 |
m.add_pmtiles(ca_pmtiles, style=style, name="CA", opacity=alpha, tooltip=True, fit_bounds=True)
|
| 386 |
|
| 387 |
|
|
@@ -408,11 +435,12 @@ colors = (
|
|
| 408 |
# get summary tables used for charts + printed table
|
| 409 |
# df - charts; df_tab - printed table (omits colors)
|
| 410 |
if 'out' not in locals():
|
| 411 |
-
df,df_tab = summary_table(ca, column, colors, filter_cols, filter_vals, colorby_vals)
|
|
|
|
|
|
|
| 412 |
else:
|
| 413 |
df = summary_table_sql(ca, column, colors, ids)
|
| 414 |
-
|
| 415 |
-
total_percent = df.percent_protected.sum().round(2)
|
| 416 |
|
| 417 |
|
| 418 |
# charts displayed based on color_by variable
|
|
|
|
| 25 |
if "mydata" not in set(current_tables):
|
| 26 |
tbl = con.read_parquet(ca_parquet)
|
| 27 |
con.create_table("mydata", tbl)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
ca = con.table("mydata")
|
| 31 |
|
| 32 |
|
|
|
|
| 191 |
elif ("id" and "geom" in result.columns):
|
| 192 |
style = get_pmtiles_style_llm(style_options[color_choice], result["id"].tolist())
|
| 193 |
legend_d = {cat: color for cat, color in style_options[color_choice]['stops']}
|
| 194 |
+
position = 'bottom-left'
|
| 195 |
+
fontsize = 15
|
| 196 |
+
bg_color = 'white'
|
| 197 |
+
|
| 198 |
# shorten legend for ecoregions
|
| 199 |
if color_choice == "Ecoregion":
|
| 200 |
+
legend_d = {key.replace("Northern California", "NorCal"): value for key, value in legend_d.items()}
|
| 201 |
+
legend_d = {key.replace("Southern California", "SoCal"): value for key, value in legend_d.items()}
|
| 202 |
+
legend_d = {key.replace("Southeastern", "SE."): value for key, value in legend_d.items()}
|
| 203 |
+
legend_d = {key.replace("and", "&"): value for key, value in legend_d.items()}
|
| 204 |
legend_d = {key.replace("California", "CA"): value for key, value in legend_d.items()}
|
| 205 |
+
legend_d = {key.replace("Northwestern", "NW."): value for key, value in legend_d.items()}
|
| 206 |
+
bg_color = 'rgba(255, 255, 255, 0.6)'
|
| 207 |
+
fontsize = 12
|
| 208 |
+
|
| 209 |
+
m.add_legend(legend_dict = legend_d, position = position, bg_color = bg_color, fontsize = fontsize)
|
| 210 |
m.add_pmtiles(ca_pmtiles, style=style, opacity=alpha, tooltip=True, fit_bounds=True)
|
| 211 |
m.fit_bounds(result.total_bounds.tolist())
|
| 212 |
result = result.drop('geom',axis = 1) #printing to streamlit so I need to drop geom
|
|
|
|
| 361 |
|
| 362 |
st.divider()
|
| 363 |
st.markdown('<p class = "medium-font-sidebar"> Filters:</p>', help = "Apply filters to adjust what data is shown on the map.", unsafe_allow_html= True)
|
| 364 |
+
for col,val in style_options.items():
|
| 365 |
+
for name in val['stops'][0]:
|
| 366 |
+
key = val['property']+str(name)
|
| 367 |
+
st.session_state[key] = default_gap.get(name, True)
|
| 368 |
+
|
| 369 |
for label in style_options: # get selected filters (based on the buttons selected)
|
| 370 |
with st.expander(label):
|
| 371 |
+
if label in ["GAP Code","30x30 Status"]: # gap code 1 and 2 are on by default
|
| 372 |
opts = getButtons(style_options, label, default_gap)
|
| 373 |
else: # other buttons are not on by default.
|
| 374 |
opts = getButtons(style_options, label)
|
|
|
|
| 381 |
else:
|
| 382 |
filter_cols = []
|
| 383 |
filter_vals = []
|
| 384 |
+
|
| 385 |
st.divider()
|
| 386 |
st.markdown("""
|
| 387 |
<p class="medium-font-sidebar">
|
|
|
|
| 392 |
if 'out' not in locals():
|
| 393 |
style = get_pmtiles_style(style_options[color_choice], alpha, filter_cols, filter_vals)
|
| 394 |
legend_d = {cat: color for cat, color in style_options[color_choice]['stops']}
|
| 395 |
+
position = 'bottom-left'
|
| 396 |
+
fontsize = 15
|
| 397 |
+
bg_color = 'white'
|
| 398 |
+
|
| 399 |
# shorten legend for ecoregions
|
| 400 |
if color_choice == "Ecoregion":
|
| 401 |
+
legend_d = {key.replace("Northern California", "NorCal"): value for key, value in legend_d.items()}
|
| 402 |
+
legend_d = {key.replace("Southern California", "SoCal"): value for key, value in legend_d.items()}
|
| 403 |
+
legend_d = {key.replace("Southeastern", "SE."): value for key, value in legend_d.items()}
|
| 404 |
+
legend_d = {key.replace("and", "&"): value for key, value in legend_d.items()}
|
| 405 |
legend_d = {key.replace("California", "CA"): value for key, value in legend_d.items()}
|
| 406 |
+
legend_d = {key.replace("Northwestern", "NW."): value for key, value in legend_d.items()}
|
| 407 |
+
bg_color = 'rgba(255, 255, 255, 0.6)'
|
| 408 |
+
fontsize = 12
|
| 409 |
+
|
| 410 |
|
| 411 |
+
m.add_legend(legend_dict = legend_d, position = position, bg_color = bg_color, fontsize = fontsize)
|
| 412 |
m.add_pmtiles(ca_pmtiles, style=style, name="CA", opacity=alpha, tooltip=True, fit_bounds=True)
|
| 413 |
|
| 414 |
|
|
|
|
| 435 |
# get summary tables used for charts + printed table
|
| 436 |
# df - charts; df_tab - printed table (omits colors)
|
| 437 |
if 'out' not in locals():
|
| 438 |
+
df,df_tab,df_percent = summary_table(ca, column, colors, filter_cols, filter_vals, colorby_vals)
|
| 439 |
+
total_percent = df_percent.percent_protected.sum().round(2)
|
| 440 |
+
|
| 441 |
else:
|
| 442 |
df = summary_table_sql(ca, column, colors, ids)
|
| 443 |
+
total_percent = df.percent_protected.sum().round(2)
|
|
|
|
| 444 |
|
| 445 |
|
| 446 |
# charts displayed based on color_by variable
|
app/utils.py
CHANGED
|
@@ -14,6 +14,7 @@ import sqlalchemy
|
|
| 14 |
import pathlib
|
| 15 |
from typing import Optional
|
| 16 |
from functools import reduce
|
|
|
|
| 17 |
|
| 18 |
from variables import *
|
| 19 |
|
|
@@ -52,13 +53,17 @@ def summary_table(ca, column, colors, filter_cols, filter_vals,colorby_vals): #
|
|
| 52 |
filter_cols.append(column)
|
| 53 |
filters.append(getattr(_, column).isin(colorby_vals[column]))
|
| 54 |
combined_filter = reduce(lambda x, y: x & y, filters) #combining all the filters into ibis filter expression
|
| 55 |
-
|
|
|
|
|
|
|
| 56 |
if column == "status": #need to include non-conserved in summary stats
|
| 57 |
combined_filter = (combined_filter) | (_.status.isin(['30x30-conserved','other-conserved','non-conserved']))
|
| 58 |
-
|
| 59 |
df = get_summary(ca, combined_filter, [column], colors) # df used for charts
|
|
|
|
| 60 |
df_tab = get_summary(ca, combined_filter, filter_cols, colors = None) #df used for printed table
|
| 61 |
-
|
|
|
|
| 62 |
|
| 63 |
|
| 64 |
|
|
@@ -120,19 +125,55 @@ def bar_chart(df, x, y, title): #display summary stats for color_by column
|
|
| 120 |
).properties(width="container", height=height, title = title)
|
| 121 |
return chart
|
| 122 |
|
| 123 |
-
|
| 124 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
column = style_options[style_choice]['property']
|
| 126 |
-
opts = [style[0] for style in style_options[style_choice]['stops']]
|
| 127 |
-
default_gap = default_gap or {}
|
| 128 |
-
buttons = {
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
filter_choice = [key for key, value in buttons.items() if value]
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
return d
|
| 136 |
|
| 137 |
|
| 138 |
def getColorVals(style_options, style_choice):
|
|
@@ -149,6 +190,10 @@ def get_pmtiles_style(paint, alpha, filter_cols, filter_vals):
|
|
| 149 |
for col, val in zip(filter_cols, filter_vals):
|
| 150 |
filters.append(["match", ["get", col], val, True, False])
|
| 151 |
combined_filters = ["all"] + filters
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
style = {
|
| 153 |
"version": 8,
|
| 154 |
"sources": {
|
|
|
|
| 14 |
import pathlib
|
| 15 |
from typing import Optional
|
| 16 |
from functools import reduce
|
| 17 |
+
from itertools import chain
|
| 18 |
|
| 19 |
from variables import *
|
| 20 |
|
|
|
|
| 53 |
filter_cols.append(column)
|
| 54 |
filters.append(getattr(_, column).isin(colorby_vals[column]))
|
| 55 |
combined_filter = reduce(lambda x, y: x & y, filters) #combining all the filters into ibis filter expression
|
| 56 |
+
|
| 57 |
+
df_percent = get_summary(ca, combined_filter, [column], colors) # df used for charts
|
| 58 |
+
|
| 59 |
if column == "status": #need to include non-conserved in summary stats
|
| 60 |
combined_filter = (combined_filter) | (_.status.isin(['30x30-conserved','other-conserved','non-conserved']))
|
| 61 |
+
|
| 62 |
df = get_summary(ca, combined_filter, [column], colors) # df used for charts
|
| 63 |
+
|
| 64 |
df_tab = get_summary(ca, combined_filter, filter_cols, colors = None) #df used for printed table
|
| 65 |
+
|
| 66 |
+
return df, df_tab, df_percent
|
| 67 |
|
| 68 |
|
| 69 |
|
|
|
|
| 125 |
).properties(width="container", height=height, title = title)
|
| 126 |
return chart
|
| 127 |
|
| 128 |
+
def sync_checkboxes(source):
|
| 129 |
+
# gap 1 and gap 2 on -> 30x30-conserved on
|
| 130 |
+
if source in ["gap_code1", "gap_code2"]:
|
| 131 |
+
st.session_state['status30x30-conserved'] = st.session_state.gap_code1 and st.session_state.gap_code2
|
| 132 |
+
|
| 133 |
+
# 30x30-conserved on -> gap 1 and gap 2 on
|
| 134 |
+
elif source == "status30x30-conserved":
|
| 135 |
+
st.session_state.gap_code1 = st.session_state['status30x30-conserved']
|
| 136 |
+
st.session_state.gap_code2 = st.session_state['status30x30-conserved']
|
| 137 |
+
|
| 138 |
+
# other-conserved on <-> gap 3 on
|
| 139 |
+
elif source == "gap_code3":
|
| 140 |
+
st.session_state["statusother-conserved"] = st.session_state.gap_code3
|
| 141 |
+
rerun_needed = True
|
| 142 |
+
elif source == "statusother-conserved":
|
| 143 |
+
if "gap_code3" in st.session_state and st.session_state["statusother-conserved"] != st.session_state.gap_code3:
|
| 144 |
+
st.session_state.gap_code3 = st.session_state["statusother-conserved"]
|
| 145 |
+
rerun_needed = True # Ensure UI updates
|
| 146 |
+
|
| 147 |
+
# unknown on <-> gap 4 on
|
| 148 |
+
elif source == "gap_code4":
|
| 149 |
+
st.session_state.statusunknown = st.session_state.gap_code4
|
| 150 |
+
rerun_needed = True
|
| 151 |
+
elif source == "statusunknown":
|
| 152 |
+
if "gap_code4" in st.session_state and st.session_state.statusunknown != st.session_state.gap_code4:
|
| 153 |
+
st.session_state.gap_code4 = st.session_state.statusunknown
|
| 154 |
+
rerun_needed = True
|
| 155 |
+
|
| 156 |
+
# non-conserved on <-> gap 0
|
| 157 |
+
elif source == "gap_code0":
|
| 158 |
+
st.session_state['statusnon-conserved'] = st.session_state.gap_code0
|
| 159 |
+
rerun_needed = True
|
| 160 |
+
elif source == "statusnon-conserved":
|
| 161 |
+
if "gap_code0" in st.session_state and st.session_state['statusnon-conserved'] != st.session_state.gap_code0:
|
| 162 |
+
st.session_state.gap_code0 = st.session_state['statusnon-conserved']
|
| 163 |
+
rerun_needed = True
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
def getButtons(style_options, style_choice, default_gap=None):
|
| 167 |
column = style_options[style_choice]['property']
|
| 168 |
+
opts = [style[0] for style in style_options[style_choice]['stops']]
|
| 169 |
+
default_gap = default_gap or {}
|
| 170 |
+
buttons = {}
|
| 171 |
+
for name in opts:
|
| 172 |
+
key = column + str(name)
|
| 173 |
+
buttons[name] = st.checkbox(f"{name}", value=st.session_state[key], key=key, on_change = sync_checkboxes, args = (key,))
|
| 174 |
+
filter_choice = [key for key, value in buttons.items() if value]
|
| 175 |
+
return {column: filter_choice}
|
| 176 |
+
|
|
|
|
| 177 |
|
| 178 |
|
| 179 |
def getColorVals(style_options, style_choice):
|
|
|
|
| 190 |
for col, val in zip(filter_cols, filter_vals):
|
| 191 |
filters.append(["match", ["get", col], val, True, False])
|
| 192 |
combined_filters = ["all"] + filters
|
| 193 |
+
|
| 194 |
+
if "non-conserved" in list(chain.from_iterable(filter_vals)):
|
| 195 |
+
combined_filters = ["any", combined_filters, ["match", ["get", "status"], ["non-conserved"],True, False]]
|
| 196 |
+
|
| 197 |
style = {
|
| 198 |
"version": 8,
|
| 199 |
"sources": {
|
app/variables.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
# urls for main layer
|
| 2 |
-
ca_parquet = "https://huggingface.co/datasets/boettiger-lab/ca-30x30/resolve/
|
| 3 |
-
ca_pmtiles = "https://huggingface.co/datasets/boettiger-lab/ca-30x30/resolve/
|
|
|
|
| 4 |
|
| 5 |
ca_area_acres = 1.014e8 #acres
|
| 6 |
style_choice = "GAP Status Code"
|
|
@@ -41,6 +42,9 @@ default_gap = {
|
|
| 41 |
0: False,
|
| 42 |
3: False,
|
| 43 |
4: False,
|
|
|
|
|
|
|
|
|
|
| 44 |
}
|
| 45 |
|
| 46 |
# Maplibre styles. (should these be functions?)
|
|
@@ -59,7 +63,8 @@ manager = {
|
|
| 59 |
['Tribal', tribal_color],
|
| 60 |
['Private', private_color],
|
| 61 |
['HOA', hoa_color],
|
| 62 |
-
]
|
|
|
|
| 63 |
}
|
| 64 |
|
| 65 |
easement = {
|
|
@@ -68,7 +73,8 @@ easement = {
|
|
| 68 |
'stops': [
|
| 69 |
['True', private_access_color],
|
| 70 |
['False', public_access_color],
|
| 71 |
-
]
|
|
|
|
| 72 |
}
|
| 73 |
|
| 74 |
year = {
|
|
@@ -77,7 +83,8 @@ year = {
|
|
| 77 |
'stops': [
|
| 78 |
['pre-2024', year2023_color],
|
| 79 |
['2024', year2024_color],
|
| 80 |
-
]
|
|
|
|
| 81 |
}
|
| 82 |
|
| 83 |
access = {
|
|
@@ -88,7 +95,8 @@ access = {
|
|
| 88 |
['No Public Access', private_access_color],
|
| 89 |
['Unknown Access', "#bbbbbb"],
|
| 90 |
['Restricted Access', tribal_color],
|
| 91 |
-
]
|
|
|
|
| 92 |
}
|
| 93 |
|
| 94 |
gap = {
|
|
@@ -97,22 +105,25 @@ gap = {
|
|
| 97 |
'stops': [
|
| 98 |
[1, "#26633d"],
|
| 99 |
[2, "#879647"],
|
| 100 |
-
[3, "#
|
| 101 |
-
[4, "#
|
| 102 |
-
]
|
|
|
|
| 103 |
}
|
| 104 |
|
| 105 |
status = {
|
| 106 |
'property': 'status',
|
| 107 |
'type': 'categorical',
|
| 108 |
'stops': [
|
| 109 |
-
['30x30-conserved', "#
|
| 110 |
-
['other-conserved', "#
|
| 111 |
-
['
|
| 112 |
-
|
|
|
|
| 113 |
}
|
| 114 |
|
| 115 |
|
|
|
|
| 116 |
ecoregion = {
|
| 117 |
'property': 'ecoregion',
|
| 118 |
'type': 'categorical',
|
|
@@ -137,14 +148,14 @@ ecoregion = {
|
|
| 137 |
['Northern California Coast', "#c7c7c7"],
|
| 138 |
['Great Valley (North)', "#dbdb8d"],
|
| 139 |
['Central California Coast', "#9edae5"],
|
| 140 |
-
]
|
|
|
|
| 141 |
}
|
| 142 |
|
| 143 |
-
|
| 144 |
style_options = {
|
| 145 |
"Year": year,
|
| 146 |
-
"GAP Code": gap,
|
| 147 |
"30x30 Status": status,
|
|
|
|
| 148 |
"Ecoregion": ecoregion,
|
| 149 |
"Manager Type": manager,
|
| 150 |
"Easement": easement,
|
|
@@ -252,8 +263,8 @@ svi_style = {
|
|
| 252 |
|
| 253 |
select_column = {
|
| 254 |
"Year": "established",
|
| 255 |
-
"GAP Code": "gap_code",
|
| 256 |
"30x30 Status": "status",
|
|
|
|
| 257 |
"Ecoregion": "ecoregion",
|
| 258 |
"Manager Type": "manager_type",
|
| 259 |
"Easement": "easement",
|
|
|
|
| 1 |
# urls for main layer
|
| 2 |
+
ca_parquet = "https://huggingface.co/datasets/boettiger-lab/ca-30x30/resolve/38af68979644f52ac928c5e41c81ec4d93468eef/ca-30x30.parquet"
|
| 3 |
+
ca_pmtiles = "https://huggingface.co/datasets/boettiger-lab/ca-30x30/resolve/e283bb63ee76dd5acd2d187029a80ab6a011886b/ca-30x30.pmtiles"
|
| 4 |
+
|
| 5 |
|
| 6 |
ca_area_acres = 1.014e8 #acres
|
| 7 |
style_choice = "GAP Status Code"
|
|
|
|
| 42 |
0: False,
|
| 43 |
3: False,
|
| 44 |
4: False,
|
| 45 |
+
"other-conserved":False,
|
| 46 |
+
"unknown":False,
|
| 47 |
+
"non-conserved":False
|
| 48 |
}
|
| 49 |
|
| 50 |
# Maplibre styles. (should these be functions?)
|
|
|
|
| 63 |
['Tribal', tribal_color],
|
| 64 |
['Private', private_color],
|
| 65 |
['HOA', hoa_color],
|
| 66 |
+
],
|
| 67 |
+
'default': white
|
| 68 |
}
|
| 69 |
|
| 70 |
easement = {
|
|
|
|
| 73 |
'stops': [
|
| 74 |
['True', private_access_color],
|
| 75 |
['False', public_access_color],
|
| 76 |
+
],
|
| 77 |
+
'default': white
|
| 78 |
}
|
| 79 |
|
| 80 |
year = {
|
|
|
|
| 83 |
'stops': [
|
| 84 |
['pre-2024', year2023_color],
|
| 85 |
['2024', year2024_color],
|
| 86 |
+
],
|
| 87 |
+
'default': white
|
| 88 |
}
|
| 89 |
|
| 90 |
access = {
|
|
|
|
| 95 |
['No Public Access', private_access_color],
|
| 96 |
['Unknown Access', "#bbbbbb"],
|
| 97 |
['Restricted Access', tribal_color],
|
| 98 |
+
],
|
| 99 |
+
'default': white
|
| 100 |
}
|
| 101 |
|
| 102 |
gap = {
|
|
|
|
| 105 |
'stops': [
|
| 106 |
[1, "#26633d"],
|
| 107 |
[2, "#879647"],
|
| 108 |
+
[3, "#bdcf72"],
|
| 109 |
+
[4, "#6d6e6d"]
|
| 110 |
+
],
|
| 111 |
+
'default': white
|
| 112 |
}
|
| 113 |
|
| 114 |
status = {
|
| 115 |
'property': 'status',
|
| 116 |
'type': 'categorical',
|
| 117 |
'stops': [
|
| 118 |
+
['30x30-conserved', "#56711f"],
|
| 119 |
+
['other-conserved', "#b6ce7a"],
|
| 120 |
+
['unknown', "#e5efdb"],
|
| 121 |
+
['non-conserved', "#e1e1e1"]
|
| 122 |
+
],
|
| 123 |
}
|
| 124 |
|
| 125 |
|
| 126 |
+
|
| 127 |
ecoregion = {
|
| 128 |
'property': 'ecoregion',
|
| 129 |
'type': 'categorical',
|
|
|
|
| 148 |
['Northern California Coast', "#c7c7c7"],
|
| 149 |
['Great Valley (North)', "#dbdb8d"],
|
| 150 |
['Central California Coast', "#9edae5"],
|
| 151 |
+
],
|
| 152 |
+
'default': white
|
| 153 |
}
|
| 154 |
|
|
|
|
| 155 |
style_options = {
|
| 156 |
"Year": year,
|
|
|
|
| 157 |
"30x30 Status": status,
|
| 158 |
+
"GAP Code": gap,
|
| 159 |
"Ecoregion": ecoregion,
|
| 160 |
"Manager Type": manager,
|
| 161 |
"Easement": easement,
|
|
|
|
| 263 |
|
| 264 |
select_column = {
|
| 265 |
"Year": "established",
|
|
|
|
| 266 |
"30x30 Status": "status",
|
| 267 |
+
"GAP Code": "gap_code",
|
| 268 |
"Ecoregion": "ecoregion",
|
| 269 |
"Manager Type": "manager_type",
|
| 270 |
"Easement": "easement",
|
preprocess/preprocess.ipynb
CHANGED
|
@@ -37,8 +37,13 @@
|
|
| 37 |
"ca_boundary_shape = \"../data/ca_shape\"\n",
|
| 38 |
"ca_boundary_parquet = path + \"ca_boundary.parquet\"\n",
|
| 39 |
"\n",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
"# file to save non-conserved areas; costly operation so we save results \n",
|
| 41 |
"ca_nonconserved_parquet = path + \"ca-30x30-nonconserved-500m-simplified.parquet\" \n",
|
|
|
|
| 42 |
"\n",
|
| 43 |
"# temp file used to compute zonal stats: has conserved + non-conserved areas \n",
|
| 44 |
"ca_temp_parquet = path + \"ca-30x30-temp.parquet\" \n",
|
|
@@ -148,6 +153,88 @@
|
|
| 148 |
"nonconserved.execute().to_parquet(ca_nonconserved_parquet)"
|
| 149 |
]
|
| 150 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 151 |
{
|
| 152 |
"cell_type": "markdown",
|
| 153 |
"id": "ce52b1e0-027e-4915-9e7b-e51e946560ed",
|
|
@@ -165,7 +252,7 @@
|
|
| 165 |
"source": [
|
| 166 |
"# match CA Nature schema \n",
|
| 167 |
"nonconserved_clean = (\n",
|
| 168 |
-
" con.read_parquet(
|
| 169 |
" .cast({\"geom\": \"geometry\"})\n",
|
| 170 |
" .mutate(established = ibis.null(), gap_code = 0, name = ibis.literal(\"Non-Conserved Areas\"),\n",
|
| 171 |
" access_type = ibis.null(), manager = ibis.null(), manager_type = ibis.null(),\n",
|
|
@@ -250,7 +337,7 @@
|
|
| 250 |
" .mutate(easement=_.Easement.cast(\"string\").substitute({\"0\": \"False\", \"1\": \"True\"}))\n",
|
| 251 |
" .mutate(status=_.gap_code.cast(\"string\")\n",
|
| 252 |
" .substitute({\"1\": \"30x30-conserved\", \"2\": \"30x30-conserved\", \"3\": \"other-conserved\", \n",
|
| 253 |
-
" \"4\": \"
|
| 254 |
" .select(_.established, _.gap_code, _.status, _.name, _.access_type, _.manager, _.manager_type,\n",
|
| 255 |
" _.ecoregion, _.easement, _.acres, _.id, _.type, _.geom)\n",
|
| 256 |
" .union(nonconserved_clean)\n",
|
|
@@ -379,7 +466,7 @@
|
|
| 379 |
},
|
| 380 |
{
|
| 381 |
"cell_type": "code",
|
| 382 |
-
"execution_count":
|
| 383 |
"id": "aade11d9-87b9-403d-bad1-3069663807a9",
|
| 384 |
"metadata": {},
|
| 385 |
"outputs": [],
|
|
@@ -483,7 +570,7 @@
|
|
| 483 |
"#to use PMTiles, need to convert to geojson\n",
|
| 484 |
"ca_geojson = (con\n",
|
| 485 |
" .read_parquet(ca_parquet)\n",
|
| 486 |
-
" .filter(_.status != 'non-conserved') #omitting the non-conserved to only for pmtiles \n",
|
| 487 |
" )\n",
|
| 488 |
"\n",
|
| 489 |
"#can't go directly from parquet -> pmtiles, need to go parquet -> geojson -> pmtiles \n",
|
|
|
|
| 37 |
"ca_boundary_shape = \"../data/ca_shape\"\n",
|
| 38 |
"ca_boundary_parquet = path + \"ca_boundary.parquet\"\n",
|
| 39 |
"\n",
|
| 40 |
+
"# Ecoregions\n",
|
| 41 |
+
"ca_ecoregions_shape = \"../data/ecoregions/ACE_Ecoregions_BaileyDerived_2022.shp\"\n",
|
| 42 |
+
"ca_ecoregions_parquet = path + \"ace_ecoregions.parquet\"\n",
|
| 43 |
+
"\n",
|
| 44 |
"# file to save non-conserved areas; costly operation so we save results \n",
|
| 45 |
"ca_nonconserved_parquet = path + \"ca-30x30-nonconserved-500m-simplified.parquet\" \n",
|
| 46 |
+
"ca_nonconserved_eco_parquet = path + \"ca-30x30-nonconserved-500m-simplified-eco.parquet\" \n",
|
| 47 |
"\n",
|
| 48 |
"# temp file used to compute zonal stats: has conserved + non-conserved areas \n",
|
| 49 |
"ca_temp_parquet = path + \"ca-30x30-temp.parquet\" \n",
|
|
|
|
| 153 |
"nonconserved.execute().to_parquet(ca_nonconserved_parquet)"
|
| 154 |
]
|
| 155 |
},
|
| 156 |
+
{
|
| 157 |
+
"cell_type": "markdown",
|
| 158 |
+
"id": "845eb0ed-3392-4346-9b7e-959cd97a274f",
|
| 159 |
+
"metadata": {},
|
| 160 |
+
"source": [
|
| 161 |
+
"#### Get ecoregions - convert them to parquet"
|
| 162 |
+
]
|
| 163 |
+
},
|
| 164 |
+
{
|
| 165 |
+
"cell_type": "code",
|
| 166 |
+
"execution_count": null,
|
| 167 |
+
"id": "b43fee2c-b8c4-4076-b87b-089676031165",
|
| 168 |
+
"metadata": {},
|
| 169 |
+
"outputs": [],
|
| 170 |
+
"source": [
|
| 171 |
+
"eco = gpd.read_file(ca_ecoregions_shape)\n",
|
| 172 |
+
"eco.to_parquet(ca_ecoregions_parquet)"
|
| 173 |
+
]
|
| 174 |
+
},
|
| 175 |
+
{
|
| 176 |
+
"cell_type": "markdown",
|
| 177 |
+
"id": "b5689850-80fa-4cc9-87e9-46074b8d9107",
|
| 178 |
+
"metadata": {},
|
| 179 |
+
"source": [
|
| 180 |
+
"#### Compute ecoregion for non-conserved areas"
|
| 181 |
+
]
|
| 182 |
+
},
|
| 183 |
+
{
|
| 184 |
+
"cell_type": "code",
|
| 185 |
+
"execution_count": 2,
|
| 186 |
+
"id": "070bbdde-b141-4a63-8f8a-984dd01fd51a",
|
| 187 |
+
"metadata": {},
|
| 188 |
+
"outputs": [
|
| 189 |
+
{
|
| 190 |
+
"data": {
|
| 191 |
+
"application/vnd.jupyter.widget-view+json": {
|
| 192 |
+
"model_id": "3c217929b7744164a99f6e2314366359",
|
| 193 |
+
"version_major": 2,
|
| 194 |
+
"version_minor": 0
|
| 195 |
+
},
|
| 196 |
+
"text/plain": [
|
| 197 |
+
"FloatProgress(value=0.0, layout=Layout(width='auto'), style=ProgressStyle(bar_color='black'))"
|
| 198 |
+
]
|
| 199 |
+
},
|
| 200 |
+
"metadata": {},
|
| 201 |
+
"output_type": "display_data"
|
| 202 |
+
}
|
| 203 |
+
],
|
| 204 |
+
"source": [
|
| 205 |
+
"con = ibis.duckdb.connect(extensions=[\"spatial\"])\n",
|
| 206 |
+
"\n",
|
| 207 |
+
"eco = con.read_parquet(ca_ecoregions_parquet)\n",
|
| 208 |
+
"non = con.read_parquet(ca_nonconserved_parquet)\n",
|
| 209 |
+
"\n",
|
| 210 |
+
"con.create_table(\"eco\", eco.select(\"ECOREGION_\",\"geometry\"), overwrite = True)\n",
|
| 211 |
+
"con.create_table(\"non\", non, overwrite = True)\n",
|
| 212 |
+
"\n",
|
| 213 |
+
"# split up the non-conserved areas by ecoregions\n",
|
| 214 |
+
"con.con.execute('''\n",
|
| 215 |
+
"CREATE TABLE non_conserved_eco AS\n",
|
| 216 |
+
"SELECT \n",
|
| 217 |
+
" non.*, \n",
|
| 218 |
+
" eco.ECOREGION_ AS ecoregion,\n",
|
| 219 |
+
" ST_Intersection(non.geom, eco.geometry) AS geom -- Split non into ecoregions\n",
|
| 220 |
+
"FROM non\n",
|
| 221 |
+
"JOIN eco \n",
|
| 222 |
+
"ON ST_Intersects(non.geom, eco.geometry)\n",
|
| 223 |
+
"WHERE ST_GeometryType(ST_Intersection(non.geom, eco.geometry)) IN ('POLYGON', 'MULTIPOLYGON');\n",
|
| 224 |
+
"''')\n",
|
| 225 |
+
"\n",
|
| 226 |
+
"\n",
|
| 227 |
+
"# save to parquet file so we don't have to run this again\n",
|
| 228 |
+
"non_eco = (con.table(\"non_conserved_eco\")\n",
|
| 229 |
+
" .drop('geom')\n",
|
| 230 |
+
" .rename(geom = \"geom_1\")\n",
|
| 231 |
+
" .mutate(geom = ST_MakeValid(_.geom)) \n",
|
| 232 |
+
" )\n",
|
| 233 |
+
"\n",
|
| 234 |
+
"non_conserved_eco = non_eco.execute()\n",
|
| 235 |
+
"non_conserved_eco.to_parquet(ca_nonconserved_eco_parquet)"
|
| 236 |
+
]
|
| 237 |
+
},
|
| 238 |
{
|
| 239 |
"cell_type": "markdown",
|
| 240 |
"id": "ce52b1e0-027e-4915-9e7b-e51e946560ed",
|
|
|
|
| 252 |
"source": [
|
| 253 |
"# match CA Nature schema \n",
|
| 254 |
"nonconserved_clean = (\n",
|
| 255 |
+
" con.read_parquet(ca_nonconserved_eco_parquet)\n",
|
| 256 |
" .cast({\"geom\": \"geometry\"})\n",
|
| 257 |
" .mutate(established = ibis.null(), gap_code = 0, name = ibis.literal(\"Non-Conserved Areas\"),\n",
|
| 258 |
" access_type = ibis.null(), manager = ibis.null(), manager_type = ibis.null(),\n",
|
|
|
|
| 337 |
" .mutate(easement=_.Easement.cast(\"string\").substitute({\"0\": \"False\", \"1\": \"True\"}))\n",
|
| 338 |
" .mutate(status=_.gap_code.cast(\"string\")\n",
|
| 339 |
" .substitute({\"1\": \"30x30-conserved\", \"2\": \"30x30-conserved\", \"3\": \"other-conserved\", \n",
|
| 340 |
+
" \"4\": \"unknown\"}))\n",
|
| 341 |
" .select(_.established, _.gap_code, _.status, _.name, _.access_type, _.manager, _.manager_type,\n",
|
| 342 |
" _.ecoregion, _.easement, _.acres, _.id, _.type, _.geom)\n",
|
| 343 |
" .union(nonconserved_clean)\n",
|
|
|
|
| 466 |
},
|
| 467 |
{
|
| 468 |
"cell_type": "code",
|
| 469 |
+
"execution_count": 2,
|
| 470 |
"id": "aade11d9-87b9-403d-bad1-3069663807a9",
|
| 471 |
"metadata": {},
|
| 472 |
"outputs": [],
|
|
|
|
| 570 |
"#to use PMTiles, need to convert to geojson\n",
|
| 571 |
"ca_geojson = (con\n",
|
| 572 |
" .read_parquet(ca_parquet)\n",
|
| 573 |
+
" # .filter(_.status != 'non-conserved') #omitting the non-conserved to only for pmtiles \n",
|
| 574 |
" )\n",
|
| 575 |
"\n",
|
| 576 |
"#can't go directly from parquet -> pmtiles, need to go parquet -> geojson -> pmtiles \n",
|