cassiebuhler commited on
Commit
56784d5
·
1 Parent(s): 78fca1a

wip; ecoregion + filter updates

Browse files

separated gap 3 and 4, ecoregions for non-conserved land, synced 30x30 status/gap filters, and fixed pmtiles filters

Files changed (4) hide show
  1. app/app.py +39 -11
  2. app/utils.py +60 -15
  3. app/variables.py +28 -17
  4. 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
- m.add_legend(legend_dict=legend_d, position='bottom-left')
 
 
 
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 == "GAP Code": # gap code 1 and 2 are on by default
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 = 'bottom-left')
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
- return df, df_tab
 
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
- def getButtons(style_options, style_choice, default_gap=None): #finding the buttons selected to use as filters
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- name: st.checkbox(f"{name}", value=default_gap.get(name, True), key=column + str(name))
130
- for name in opts
131
- }
132
- filter_choice = [key for key, value in buttons.items() if value] # return only selected
133
- d = {}
134
- d[column] = filter_choice
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/8d5d938c04d3206e6bfb04055b5e779c4c28222f/ca-30x30.parquet"
3
- ca_pmtiles = "https://huggingface.co/datasets/boettiger-lab/ca-30x30/resolve/c58913a279d13c414722c4299b0e0867e923946a/ca-30x30.pmtiles"
 
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, "#EE4B2B"],
101
- [4, "#BF40BF"]
102
- ]
 
103
  }
104
 
105
  status = {
106
  'property': 'status',
107
  'type': 'categorical',
108
  'stops': [
109
- ['30x30-conserved', "#26633d"],
110
- ['other-conserved', "#879647"],
111
- ['non-conserved', white]
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(ca_nonconserved_parquet)\n",
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\": \"other-conserved\"}))\n",
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": null,
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",