Jonathan Marokhovsky commited on
Commit
4f36249
1 Parent(s): c140c33

Got area selection calculations working

Browse files
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="Get ROI")
87
  output = widgets.Output()
88
 
89
  def on_button_click(_):
90
  if self.user_roi is not None:
91
- with output:
92
- output.clear_output()
93
- print(map_utils.calculate_area(self.user_roi))
 
 
 
 
 
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
- # Map.element( # type: ignore
149
- l_map.element( # type: ignore
 
 
 
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