Spaces:
Sleeping
Sleeping
so it begins...
Browse files- app.py +121 -0
- requirements.txt +10 -0
app.py
ADDED
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# -*- coding: utf-8 -*-
|
2 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
3 |
+
# you may not use this file except in compliance with the License.
|
4 |
+
# You may obtain a copy of the License at
|
5 |
+
#
|
6 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
7 |
+
#
|
8 |
+
# Unless required by applicable law or agreed to in writing, software
|
9 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
10 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
11 |
+
# See the License for the specific language governing permissions and
|
12 |
+
# limitations under the License.
|
13 |
+
|
14 |
+
import leafmap.foliumap as leafmap
|
15 |
+
import rioxarray
|
16 |
+
import geopandas as gpd
|
17 |
+
import streamlit as st
|
18 |
+
import altair as alt
|
19 |
+
import ibis
|
20 |
+
from ibis import _
|
21 |
+
import ibis.selectors as s
|
22 |
+
from streamlit_folium import st_folium
|
23 |
+
import json
|
24 |
+
|
25 |
+
def extract_geom(gdf, cog):
|
26 |
+
x = (rioxarray.
|
27 |
+
open_rasterio('/vsicurl/'+cog, masked=True).
|
28 |
+
rio.clip(gdf.geometry.values, gdf.crs, from_disk=True)
|
29 |
+
)
|
30 |
+
return x
|
31 |
+
|
32 |
+
def read_polygon(polygon):
|
33 |
+
geojson_str = json.dumps(polygon)
|
34 |
+
gdf = gpd.read_file(geojson_str, driver='GeoJSON')
|
35 |
+
gdf.set_crs('epsg:4326')
|
36 |
+
return gdf
|
37 |
+
|
38 |
+
def area_hectares(gdf):
|
39 |
+
area = gdf.to_crs("EPSG:9822").area / 10000.
|
40 |
+
return area
|
41 |
+
|
42 |
+
|
43 |
+
# +
|
44 |
+
st.set_page_config(layout="wide", page_title="Paradise Valley", page_icon="⚡")
|
45 |
+
|
46 |
+
st.title("Land Use change in Paradise Valley")
|
47 |
+
|
48 |
+
DESCRIPTION='''
|
49 |
+
Pan and zoom to the desired location on the map. Then, use the map tools to draw a polygon (pentagon tool), bounding box (square tool) or other shape anywhere on the map.
|
50 |
+
(use esc key to exit drawing mode). Map will display acres of land converted.
|
51 |
+
'''
|
52 |
+
|
53 |
+
built = "https://huggingface.co/datasets/boettiger-lab/yellowstone/resolve/main/diffs_cog.tif"
|
54 |
+
|
55 |
+
|
56 |
+
m = leafmap.Map(center=[35, -100], zoom=3)
|
57 |
+
|
58 |
+
## Map controls sidebar
|
59 |
+
with st.sidebar:
|
60 |
+
st.markdown(DESCRIPTION)
|
61 |
+
|
62 |
+
cog_layers = {
|
63 |
+
"Expanded Built Land, 2017-2023": built,
|
64 |
+
}
|
65 |
+
|
66 |
+
selection = st.radio("Data", cog_layers)
|
67 |
+
cog = cog_layers[selection]
|
68 |
+
m.add_cog_layer(cog, palette="reds", name=selection,
|
69 |
+
transparent_bg=True, opacity = 0.8,
|
70 |
+
zoom_to_layer=False)
|
71 |
+
|
72 |
+
"### python code for map layer:"
|
73 |
+
"adjust options or add additional layers using leafmap"
|
74 |
+
code = st.text_area(
|
75 |
+
label = "code:",
|
76 |
+
value = code_ex,
|
77 |
+
height = 400)
|
78 |
+
|
79 |
+
# run whatever python code is in the python box, just for fun
|
80 |
+
eval(compile(code, "<string>", "exec"))
|
81 |
+
st_data = m.to_streamlit(height=400, bidirectional=True)
|
82 |
+
|
83 |
+
polygon = st_data["last_active_drawing"]
|
84 |
+
# Here we actually compute the total carbon in the requested polygon
|
85 |
+
if polygon is not None:
|
86 |
+
gdf = read_polygon(polygon)
|
87 |
+
x = extract_geom(gdf, cog).fillna(0)
|
88 |
+
count = x.count()
|
89 |
+
area = round(float(area_hectares(gdf)))
|
90 |
+
carbon_total = round(float(x.mean()) * area) # no, mean does not include zeros
|
91 |
+
col2, col3 = st.columns(3)
|
92 |
+
col2.metric(label=f"Area", value=f"{area:,} Hectares")
|
93 |
+
col3.metric(label=f"pixels", value=f"{count:,}")
|
94 |
+
|
95 |
+
|
96 |
+
# pixel sums instead of means
|
97 |
+
# value = round(float(x.sum()))
|
98 |
+
# if(selection in ["Vulnerable Carbon (2018)",
|
99 |
+
# "Manageable Carbon (2018)",
|
100 |
+
# "Irrecoverable Carbon (2018)"]):
|
101 |
+
# value = value * 9 # 300m pixels, each pixel is 9 hectres
|
102 |
+
|
103 |
+
|
104 |
+
st.divider()
|
105 |
+
|
106 |
+
'''
|
107 |
+
Note: this is just a proof-of-principle demonstration of these tools, and these calculations have not been validated.
|
108 |
+
|
109 |
+
## Credits
|
110 |
+
|
111 |
+
### Data sources
|
112 |
+
|
113 |
+
- <https://planetarycomputer.microsoft.com/dataset/io-lulc-annual-v02>
|
114 |
+
|
115 |
+
### Software stack
|
116 |
+
|
117 |
+
- Streamlit (python) app hosted on free-tier HuggingFace spaces ([source code](https://huggingface.co/spaces/boettiger-lab/leafmap/blob/main/app.py)).
|
118 |
+
- Cloud-optimized geotifs hosted on [Source.Coop](https://source.coop)
|
119 |
+
- Mapping with Leafmap, calculations with rasterio
|
120 |
+
|
121 |
+
'''
|
requirements.txt
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
pandas
|
2 |
+
pydeck
|
3 |
+
pmtiles
|
4 |
+
streamlit
|
5 |
+
leafmap
|
6 |
+
ibis-framework[duckdb]
|
7 |
+
altair
|
8 |
+
rioxarray
|
9 |
+
geopandas
|
10 |
+
streamlit-folium
|