Spaces:
Runtime error
Runtime error
Jonathan Marokhovsky
commited on
Commit
•
4f36249
1
Parent(s):
c140c33
Got area selection calculations working
Browse files- requirements.txt +2 -0
- toy_app/pages/03_cog.py +30 -63
- toy_app/src/map_utils.py +22 -0
requirements.txt
CHANGED
@@ -4,3 +4,5 @@ geojson==3.1.0
|
|
4 |
ipyleaflet==0.18.2
|
5 |
ipywidgets==8.1.2
|
6 |
leafmap==0.31.9
|
|
|
|
|
|
4 |
ipyleaflet==0.18.2
|
5 |
ipywidgets==8.1.2
|
6 |
leafmap==0.31.9
|
7 |
+
rioxarray
|
8 |
+
geopandas
|
toy_app/pages/03_cog.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1 |
from ipyleaflet import Polygon, WidgetControl
|
2 |
from ipywidgets import widgets
|
|
|
3 |
import leafmap
|
4 |
import logging
|
5 |
import solara
|
@@ -17,6 +18,11 @@ log = logging.getLogger(__name__)
|
|
17 |
libya_fire_url = (
|
18 |
"https://github.com/opengeos/data/releases/download/raster/Libya-2023-07-01.tif"
|
19 |
)
|
|
|
|
|
|
|
|
|
|
|
20 |
libya_fire_name = "Libya Fire"
|
21 |
libya_layer_visible = solara.reactive("True")
|
22 |
test_poly = {
|
@@ -42,6 +48,8 @@ test_poly = {
|
|
42 |
],
|
43 |
},
|
44 |
}
|
|
|
|
|
45 |
|
46 |
|
47 |
class Map(leafmap.Map):
|
@@ -64,6 +72,13 @@ class Map(leafmap.Map):
|
|
64 |
self.add_cog_layer(
|
65 |
url=libya_fire_url, name=libya_fire_name
|
66 |
) # Just display the cog for now, the layer toggle isn't working as expected
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
67 |
# self.toggle_libya_layer()
|
68 |
# user_poly = map_utils.get_user_polygon(self)
|
69 |
# if user_poly:
|
@@ -71,82 +86,39 @@ class Map(leafmap.Map):
|
|
71 |
# self.add_click_detector_widget()
|
72 |
self.add_button()
|
73 |
|
74 |
-
def add_click_detector_widget(self, position="bottomright"):
|
75 |
-
output = widgets.Output()
|
76 |
-
control = WidgetControl(widget=output, position=position)
|
77 |
-
self.add(control)
|
78 |
-
|
79 |
-
def update_user_click(**kwargs):
|
80 |
-
with output:
|
81 |
-
print(kwargs)
|
82 |
-
|
83 |
-
self.on_interaction(update_user_click)
|
84 |
-
|
85 |
def add_button(self):
|
86 |
-
button = widgets.Button(description="
|
87 |
output = widgets.Output()
|
88 |
|
89 |
def on_button_click(_):
|
90 |
if self.user_roi is not None:
|
91 |
-
|
92 |
-
|
93 |
-
|
|
|
|
|
|
|
|
|
|
|
94 |
|
95 |
button.on_click(on_button_click)
|
96 |
|
97 |
widget = widgets.VBox([button, output])
|
98 |
self.add_widget(widget)
|
99 |
|
100 |
-
def toggle_libya_layer(self):
|
101 |
-
if libya_layer_visible.value == "True":
|
102 |
-
# The layer should be visible, if you don't find it, add it
|
103 |
-
log.warning("Layer should be visible, checking it needs to be added")
|
104 |
-
for layer in self.layers:
|
105 |
-
l_name = layer.name
|
106 |
-
if l_name == libya_fire_name:
|
107 |
-
return
|
108 |
-
else:
|
109 |
-
log.warning("Layer not found, adding new cog layer")
|
110 |
-
self.add_cog_layer(url=libya_fire_url, name=libya_fire_name)
|
111 |
-
else:
|
112 |
-
# The layer should not be visible, if you find it, remove it
|
113 |
-
log.warning("Layer should not be visible, checking to see if it's there")
|
114 |
-
for layer in self.layers:
|
115 |
-
l_name = layer.name
|
116 |
-
if l_name != libya_fire_name:
|
117 |
-
continue
|
118 |
-
log.warning("Layer found, removing it")
|
119 |
-
self.remove_layer(l_name)
|
120 |
-
|
121 |
|
122 |
zoom = solara.reactive(2)
|
123 |
center = solara.reactive((20, 0))
|
124 |
-
drawing = solara.reactive(Polygon())
|
125 |
|
126 |
|
127 |
@component
|
128 |
def Page():
|
129 |
-
l_map = Map()
|
130 |
-
|
131 |
-
# def toggle_map():
|
132 |
-
# log.warning("Attempting to toggle the layer")
|
133 |
-
# if libya_layer_visible.value == "True":
|
134 |
-
# libya_layer_visible.set("False")
|
135 |
-
# else:
|
136 |
-
# libya_layer_visible.set("True")
|
137 |
-
# log.warning(f"New layer visibility is set to {libya_layer_visible.value}")
|
138 |
-
def calc_area():
|
139 |
-
log.info("Trying to calculate the area")
|
140 |
-
# user_polygon = l_map.user_roi
|
141 |
-
user_polygon = drawing.value
|
142 |
-
log.warning(f"The polygon is: {user_polygon}")
|
143 |
-
log.warning(f"The user_roi is: {l_map.user_rois}")
|
144 |
-
|
145 |
-
print("Not yet implemented")
|
146 |
-
|
147 |
with solara.Column(style={"min_width": "500px"}):
|
148 |
-
|
149 |
-
|
|
|
|
|
|
|
150 |
zoom=zoom.value,
|
151 |
on_zoom=zoom.set,
|
152 |
center=center.value,
|
@@ -154,14 +126,9 @@ def Page():
|
|
154 |
scroll_wheel_zoom=True,
|
155 |
toolbar_ctrl=False,
|
156 |
draw_control=True,
|
157 |
-
on_draw=drawing.set,
|
158 |
-
# data_ctrl=False,
|
159 |
height="780px",
|
160 |
)
|
161 |
with solara.Row():
|
162 |
solara.Text(f"Center: {center.value}")
|
163 |
solara.Text(f"Zoom: {zoom.value}")
|
164 |
-
|
165 |
-
with solara.Row():
|
166 |
-
solara.Button(label="Calculate Polygon Area", on_click=calc_area)
|
167 |
-
# solara.Button(label="Toggle Fire Image", on_click=toggle_map) # Disable toggle for now since it's not working as expected
|
|
|
1 |
from ipyleaflet import Polygon, WidgetControl
|
2 |
from ipywidgets import widgets
|
3 |
+
from ipywidgets.widgets.widget import jsondumps
|
4 |
import leafmap
|
5 |
import logging
|
6 |
import solara
|
|
|
18 |
libya_fire_url = (
|
19 |
"https://github.com/opengeos/data/releases/download/raster/Libya-2023-07-01.tif"
|
20 |
)
|
21 |
+
richness = "https://data.source.coop/cboettig/mobi/species-richness-all/SpeciesRichness_All.tif"
|
22 |
+
deforest = "https://data.source.coop/vizzuality/lg-land-carbon-data/deforest_carbon_100m_cog.tif"
|
23 |
+
irrecoverable = (
|
24 |
+
"https://data.source.coop/cboettig/carbon/cogs/irrecoverable_c_total_2018.tif"
|
25 |
+
)
|
26 |
libya_fire_name = "Libya Fire"
|
27 |
libya_layer_visible = solara.reactive("True")
|
28 |
test_poly = {
|
|
|
48 |
],
|
49 |
},
|
50 |
}
|
51 |
+
geometry = solara.reactive(jsondumps(test_poly))
|
52 |
+
lost_carbon_count = solara.reactive(0)
|
53 |
|
54 |
|
55 |
class Map(leafmap.Map):
|
|
|
72 |
self.add_cog_layer(
|
73 |
url=libya_fire_url, name=libya_fire_name
|
74 |
) # Just display the cog for now, the layer toggle isn't working as expected
|
75 |
+
self.add_cog_layer(
|
76 |
+
url=irrecoverable,
|
77 |
+
name="irrecoverable c",
|
78 |
+
palette="reds",
|
79 |
+
zoom_to_layer=False,
|
80 |
+
opacity=0.8,
|
81 |
+
)
|
82 |
# self.toggle_libya_layer()
|
83 |
# user_poly = map_utils.get_user_polygon(self)
|
84 |
# if user_poly:
|
|
|
86 |
# self.add_click_detector_widget()
|
87 |
self.add_button()
|
88 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
89 |
def add_button(self):
|
90 |
+
button = widgets.Button(description="Calculate")
|
91 |
output = widgets.Output()
|
92 |
|
93 |
def on_button_click(_):
|
94 |
if self.user_roi is not None:
|
95 |
+
geometry.set(self.user_roi)
|
96 |
+
selected_polygon = map_utils.read_polygon(self.user_roi)
|
97 |
+
selected_on_carbon = map_utils.extract_geom(
|
98 |
+
selected_polygon, irrecoverable
|
99 |
+
).fillna(0)
|
100 |
+
area = round(float(map_utils.area_hectares(selected_polygon)))
|
101 |
+
carbon_total = round(float(selected_on_carbon.mean()) * area)
|
102 |
+
lost_carbon_count.set(carbon_total)
|
103 |
|
104 |
button.on_click(on_button_click)
|
105 |
|
106 |
widget = widgets.VBox([button, output])
|
107 |
self.add_widget(widget)
|
108 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
109 |
|
110 |
zoom = solara.reactive(2)
|
111 |
center = solara.reactive((20, 0))
|
|
|
112 |
|
113 |
|
114 |
@component
|
115 |
def Page():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
116 |
with solara.Column(style={"min_width": "500px"}):
|
117 |
+
with solara.Row():
|
118 |
+
solara.Text(
|
119 |
+
f"Average Carbon Lost within the selected area in 2018: {lost_carbon_count.value:,} Tonnes"
|
120 |
+
)
|
121 |
+
Map.element( # type: ignore
|
122 |
zoom=zoom.value,
|
123 |
on_zoom=zoom.set,
|
124 |
center=center.value,
|
|
|
126 |
scroll_wheel_zoom=True,
|
127 |
toolbar_ctrl=False,
|
128 |
draw_control=True,
|
|
|
|
|
129 |
height="780px",
|
130 |
)
|
131 |
with solara.Row():
|
132 |
solara.Text(f"Center: {center.value}")
|
133 |
solara.Text(f"Zoom: {zoom.value}")
|
134 |
+
solara.Text(f"UserPoly: {geometry.value}")
|
|
|
|
|
|
toy_app/src/map_utils.py
CHANGED
@@ -7,6 +7,9 @@ import typing
|
|
7 |
import leafmap
|
8 |
import geojson
|
9 |
from area import area as area
|
|
|
|
|
|
|
10 |
|
11 |
|
12 |
log = logging.getLogger(__name__)
|
@@ -21,3 +24,22 @@ def calculate_area(poly: geojson.GeoJSON) -> float:
|
|
21 |
raise TypeError("The given GeoJSON did not contain a Polygon as was expected")
|
22 |
|
23 |
return output
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
import leafmap
|
8 |
import geojson
|
9 |
from area import area as area
|
10 |
+
import rioxarray
|
11 |
+
import json
|
12 |
+
import geopandas as gpd
|
13 |
|
14 |
|
15 |
log = logging.getLogger(__name__)
|
|
|
24 |
raise TypeError("The given GeoJSON did not contain a Polygon as was expected")
|
25 |
|
26 |
return output
|
27 |
+
|
28 |
+
|
29 |
+
def extract_geom(gdf, cog):
|
30 |
+
x = rioxarray.open_rasterio("/vsicurl/" + cog, masked=True).rio.clip(
|
31 |
+
gdf.geometry.values, gdf.crs, from_disk=True
|
32 |
+
)
|
33 |
+
return x
|
34 |
+
|
35 |
+
|
36 |
+
def read_polygon(polygon):
|
37 |
+
geojson_str = json.dumps(polygon)
|
38 |
+
gdf = gpd.read_file(geojson_str, driver="GeoJSON")
|
39 |
+
gdf.set_crs("epsg:4326")
|
40 |
+
return gdf
|
41 |
+
|
42 |
+
|
43 |
+
def area_hectares(gdf):
|
44 |
+
area = gdf.to_crs("EPSG:9822").area / 10000.0
|
45 |
+
return area
|