cassiebuhler commited on
Commit
ba4e718
1 Parent(s): 66134f4

the maps work, still working on the stats

Browse files
Files changed (1) hide show
  1. app.py +263 -0
app.py CHANGED
@@ -0,0 +1,263 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import streamlit.components.v1 as components
3
+ import base64
4
+ import leafmap.maplibregl as leafmap
5
+ import altair as alt
6
+ import ibis
7
+ from ibis import _
8
+ import ibis.selectors as s
9
+
10
+ from typing import Optional
11
+ def to_streamlit(
12
+ self,
13
+ width: Optional[int] = None,
14
+ height: Optional[int] = 600,
15
+ scrolling: Optional[bool] = False,
16
+ **kwargs,
17
+ ):
18
+
19
+ try:
20
+ import streamlit.components.v1 as components
21
+ import base64
22
+
23
+ raw_html = self.to_html().encode("utf-8")
24
+ raw_html = base64.b64encode(raw_html).decode()
25
+ return components.iframe(
26
+ f"data:text/html;base64,{raw_html}",
27
+ width=width,
28
+ height=height,
29
+ scrolling=scrolling,
30
+ **kwargs,
31
+ )
32
+
33
+ except Exception as e:
34
+ raise Exception(e)
35
+
36
+
37
+
38
+ ca_pmtiles = "https://data.source.coop/cboettig/ca30x30/ca_areas.pmtiles"
39
+ # ca_pmtiles = "ca_areas.pmtiles"
40
+
41
+ # ca_parquet = "https://data.source.coop/cboettig/ca30x30/ca_areas.parquet"
42
+ ca_parquet = "ca_areas.parquet"
43
+
44
+ ca_area_acres = 1.014e8 #acres
45
+
46
+ style_choice = "GAP Status Code"
47
+ # style_choice = "Easement"
48
+ # style_choice = "Year"
49
+
50
+ con = ibis.duckdb.connect()
51
+ ca = (con
52
+ .read_parquet(ca_parquet)
53
+ .cast({"SHAPE": "geometry"})
54
+ .mutate(
55
+ area=_.SHAPE.area())
56
+ # .head()
57
+ )
58
+
59
+ # print(ca.schema())
60
+
61
+ # ca = ca.filter(ca.Release_Year == 2024)
62
+
63
+ private_color = "#DE881E" # orange #"#850101" # red
64
+ tribal_color = "#BF40BF" # purple
65
+ mixed_color = "#005a00" # green
66
+ public_color = "#3388ff" # blue
67
+ year2023_color = "#26542C" # green
68
+ year2024_color = "#F3AB3D" # orange
69
+
70
+
71
+
72
+
73
+ def summary_table(column, colors):
74
+ df = (ca
75
+ # .rename(area = "area_square_meters")
76
+ .group_by(column)
77
+ .aggregate(hectares_protected = (_.area.sum() / 10000).round(),
78
+ percent_protected = 100 * _.Acres.sum() / ca_area_acres
79
+ )
80
+ .mutate(percent_protected = _.percent_protected.round(1),
81
+ )
82
+ .inner_join(colors, column)
83
+ )
84
+ df = df.to_pandas()
85
+ df[column] = df[column].astype(str)
86
+ return df
87
+
88
+
89
+ def area_plot(df, column):
90
+ base = alt.Chart(df).encode(
91
+ alt.Theta("percent_protected:Q").stack(True),
92
+ )
93
+ pie = ( base
94
+ .mark_arc(innerRadius= 40, outerRadius=100)
95
+ .encode(alt.Color("color:N").scale(None).legend(None),
96
+ tooltip=['percent_protected', 'hectares_protected', column])
97
+ )
98
+ text = ( base
99
+ .mark_text(radius=80, size=14, color="white")
100
+ .encode(text = column + ":N")
101
+ )
102
+ plot = pie # pie + text
103
+ return plot.properties(width="container", height=300)
104
+
105
+
106
+
107
+ def pad_style(paint, alpha):
108
+ return {
109
+ "version": 8,
110
+ "sources": {
111
+ "ca": {
112
+ "type": "vector",
113
+ "url": "pmtiles://" + ca_pmtiles,
114
+ }},
115
+ "layers": [{
116
+ "id": "layer1",
117
+ "source": "ca",
118
+ "source-layer": "CA_Cons_Areas_parentlyr_Merge_ecofix",
119
+ "type": "fill",
120
+ "paint": {
121
+ "fill-color": paint,
122
+ "fill-opacity": alpha
123
+ }
124
+ }]}
125
+
126
+ manager = {
127
+ 'property': 'cpad_MNG_AG_LEV',
128
+ 'type': 'categorical',
129
+ 'stops': [
130
+ ['Federal', "darkblue"],
131
+ ['State', public_color],
132
+ ['Non Profit', "lightblue"],
133
+ ['Special District', "darkgreen"],
134
+ ['', "grey"],
135
+ ['Joint', "green"],
136
+ ['Tribal', tribal_color],
137
+ ['Private', "darkred"],
138
+ ['City', "pink"],
139
+ ['County', "blue"],
140
+ ['Home Owners Association', "lightgreen"]
141
+ ]
142
+ }
143
+
144
+
145
+
146
+
147
+ easement = {
148
+ 'property': 'Easement',
149
+ 'type': 'categorical',
150
+ 'stops': [
151
+ [0, public_color],
152
+ [1, private_color]
153
+ ]
154
+ }
155
+
156
+
157
+ year = {
158
+ 'property': 'Release_Year',
159
+ 'type': 'categorical',
160
+ 'stops': [
161
+ [2023, year2023_color],
162
+ [2024, year2024_color]
163
+ ]
164
+ }
165
+
166
+
167
+ access = {
168
+ 'property': 'ACCESS_TYP',
169
+ 'type': 'categorical',
170
+ 'stops': [
171
+ ['Open Access', public_color],
172
+ ['No Public Access', private_color],
173
+ ['Unknown Access', "grey"],
174
+ ['Restricted Access', tribal_color]
175
+ ]
176
+ }
177
+
178
+
179
+
180
+ gap = {
181
+ 'property': 'reGAP',
182
+ 'type': 'categorical',
183
+ 'stops': [
184
+ [1, "#26633d"],
185
+ [2, "#879647"],
186
+ [3, "#BBBBBB"],
187
+ [4, "#F8F8F8"]
188
+ ]
189
+ }
190
+
191
+
192
+ style_options = {
193
+ "GAP Status Code": gap,
194
+ "Management Agency": manager,
195
+ "Easement": easement,
196
+ "Public Access": access,
197
+ "Year": year
198
+ }
199
+
200
+
201
+
202
+ st.set_page_config(layout="wide", page_title="CA Protected Areas Explorer", page_icon=":globe:")
203
+
204
+ '''
205
+ # CA 30X30 Prototype
206
+
207
+ '''
208
+
209
+ m = leafmap.Map(style="positron")
210
+
211
+ with st.sidebar:
212
+
213
+ if st.toggle("Protected Areas", True):
214
+
215
+ style_choice = st.radio("Color by:", style_options)
216
+ alpha = st.slider("transparency", 0.0, 1.0, 0.5)
217
+ style = pad_style(style_options[style_choice], alpha)
218
+ m.add_pmtiles(ca_pmtiles, style=style, visible=True, opacity=alpha, tooltip=True)
219
+
220
+
221
+ select_column = {
222
+ "GAP Status Code": "reGAP",
223
+ "Management Agency": "cpad_MNG_AG_LEV",
224
+ "Easement": "Easement",
225
+ "Year": "Release_Year",
226
+ "Public Access": "ACCESS_TYP"}
227
+
228
+
229
+
230
+
231
+ column = select_column[style_choice]
232
+
233
+ select_colors = {
234
+ "Management Agency": manager["stops"],
235
+ "Easement": easement["stops"],
236
+ "Public Access": access["stops"],
237
+ "Year": year["stops"],
238
+ "GAP Status Code": gap["stops"]}
239
+
240
+ colors = (ibis
241
+ .memtable(select_colors[style_choice], columns = [column, "color"])
242
+ .to_pandas()
243
+ )
244
+
245
+ main = st.container()
246
+
247
+ with main:
248
+ map_col, stats_col = st.columns([2,1])
249
+
250
+ with map_col:
251
+ to_streamlit(m, height=700)
252
+ df = summary_table(column, colors)
253
+ total_percent = df.percent_protected.sum().round(1)
254
+ with stats_col:
255
+ with st.container():
256
+ f"{total_percent}% CA Covered"
257
+ st.altair_chart(area_plot(df, column), use_container_width=True)
258
+
259
+
260
+
261
+ st.divider()
262
+ footer = st.container()
263
+