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",
|