|
6 | 6 | "metadata": {}, |
7 | 7 | "source": [ |
8 | 8 | "# Upscaling Service - Proof of Concept\n", |
9 | | - "This notebooks showcases a demo of the APEx Upscaling Service by demonstrating the capabilities of the [APEx Dispatcher API](https://github.com/ESA-APEx/apex_dispatch_api). In this notebook we will perform a small upscaling exercise for one of the services in the [APEx Algoritm Services Catalogue](https://algorithm-catalogue.apex.esa.int/), specfically the [Forest Fire Mapping](https://algorithm-catalogue.apex.esa.int/apps/parcel_delineation#execution-information). We will split up an area of interest in a 20x20km grid and execute this through this upscaling task through the APEx Dispatch API." |
| 9 | + "This notebooks showcases a demo of the APEx Upscaling Service by demonstrating the capabilities of the [APEx Dispatcher API](https://github.com/ESA-APEx/apex_dispatch_api). In this notebook we will perform a small upscaling exercise for one of the services in the [APEx Algoritm Services Catalogue](https://algorithm-catalogue.apex.esa.int/), specfically the [Wind Turbine Detection](https://algorithm-catalogue.apex.esa.int/apps/wind_turbine_detection#execution-information). We will split up an area of interest in a 20x20km grid and execute this through this upscaling task through the APEx Dispatch API." |
10 | 10 | ] |
11 | 11 | }, |
12 | 12 | { |
|
58 | 58 | "metadata": {}, |
59 | 59 | "outputs": [], |
60 | 60 | "source": [ |
61 | | - "application = \"https://raw.githubusercontent.com/ESA-APEx/apex_algorithms/refs/heads/main/algorithm_catalog/vito/random_forest_firemapping/openeo_udp/random_forest_firemapping.json\"\n", |
62 | | - "endpoint = \"https://openeofed.dataspace.copernicus.eu\"" |
| 61 | + "application = \"https://raw.githubusercontent.com/ESA-APEx/apex_algorithms/main/algorithm_catalog/dhi/wind_turbine/openeo_udp/wind_turbine.json\"\n", |
| 62 | + "endpoint = \"https://openeo.vito.be\"" |
63 | 63 | ] |
64 | 64 | }, |
65 | 65 | { |
66 | 66 | "cell_type": "code", |
67 | | - "execution_count": 4, |
| 67 | + "execution_count": 5, |
68 | 68 | "id": "c83aa9d5", |
69 | 69 | "metadata": {}, |
70 | 70 | "outputs": [], |
71 | 71 | "source": [ |
72 | | - "area_of_interest = {\n", |
| 72 | + "spatial_extent = {\n", |
73 | 73 | " \"coordinates\": [\n", |
74 | 74 | " [\n", |
75 | 75 | " [\n", |
76 | | - " -7.5326905572890155,\n", |
77 | | - " 42.70954706745698\n", |
| 76 | + " 14.370712654502597,\n", |
| 77 | + " 47.27563273620049\n", |
78 | 78 | " ],\n", |
79 | 79 | " [\n", |
80 | | - " -7.5326905572890155,\n", |
81 | | - " 42.023106310303206\n", |
| 80 | + " 14.370712654502597,\n", |
| 81 | + " 47.26583764118868\n", |
82 | 82 | " ],\n", |
83 | 83 | " [\n", |
84 | | - " -6.25664018841303,\n", |
85 | | - " 42.023106310303206\n", |
| 84 | + " 14.405670947432327,\n", |
| 85 | + " 47.26583764118868\n", |
86 | 86 | " ],\n", |
87 | 87 | " [\n", |
88 | | - " -6.25664018841303,\n", |
89 | | - " 42.70954706745698\n", |
| 88 | + " 14.405670947432327,\n", |
| 89 | + " 47.27563273620049\n", |
90 | 90 | " ],\n", |
91 | 91 | " [\n", |
92 | | - " -7.5326905572890155,\n", |
93 | | - " 42.70954706745698\n", |
| 92 | + " 14.370712654502597,\n", |
| 93 | + " 47.27563273620049\n", |
94 | 94 | " ]\n", |
95 | 95 | " ]\n", |
96 | 96 | " ],\n", |
97 | 97 | " \"type\": \"Polygon\"\n", |
98 | 98 | " }\n", |
99 | | - "time_of_interest = [\"2025-08-01\", \"2025-08-25\"]" |
| 99 | + "year = 2024" |
| 100 | + ] |
| 101 | + }, |
| 102 | + { |
| 103 | + "cell_type": "code", |
| 104 | + "execution_count": 19, |
| 105 | + "id": "53d0800c-a6b3-4a64-a7a2-f27689adc7d7", |
| 106 | + "metadata": {}, |
| 107 | + "outputs": [], |
| 108 | + "source": [ |
| 109 | + "# Map related settings\n", |
| 110 | + "center = shape(spatial_extent).centroid\n", |
| 111 | + "zoom = 14" |
100 | 112 | ] |
101 | 113 | }, |
102 | 114 | { |
|
110 | 122 | }, |
111 | 123 | { |
112 | 124 | "cell_type": "code", |
113 | | - "execution_count": 11, |
| 125 | + "execution_count": 6, |
114 | 126 | "id": "e0618338", |
115 | 127 | "metadata": {}, |
116 | 128 | "outputs": [ |
117 | 129 | { |
118 | 130 | "name": "stdout", |
119 | 131 | "output_type": "stream", |
120 | 132 | "text": [ |
121 | | - "Processing 48 tiles for area of interest\n" |
| 133 | + "Processing 1 tiles for area of interest\n" |
122 | 134 | ] |
123 | 135 | } |
124 | 136 | ], |
125 | 137 | "source": [ |
126 | 138 | "tiles = requests.post(f\"http://{dispatch_api}/tiles\", json={\n", |
127 | 139 | " \"grid\": \"20x20km\",\n", |
128 | | - " \"aoi\": area_of_interest\n", |
| 140 | + " \"aoi\": spatial_extent\n", |
129 | 141 | "}).json()\n", |
130 | 142 | "print(f\"Processing {len(tiles['geometries'])} tiles for area of interest\")" |
131 | 143 | ] |
132 | 144 | }, |
133 | 145 | { |
134 | 146 | "cell_type": "code", |
135 | | - "execution_count": 12, |
| 147 | + "execution_count": 20, |
136 | 148 | "id": "6de8d686", |
137 | 149 | "metadata": {}, |
138 | 150 | "outputs": [ |
139 | 151 | { |
140 | 152 | "data": { |
141 | 153 | "application/vnd.jupyter.widget-view+json": { |
142 | | - "model_id": "b32498d96d9644398a441f9b7d1f19a0", |
| 154 | + "model_id": "766ab5981ee449048f19eae60ad8236c", |
143 | 155 | "version_major": 2, |
144 | 156 | "version_minor": 0 |
145 | 157 | }, |
146 | 158 | "text/plain": [ |
147 | | - "Map(center=[42.366326688880086, -6.894665372851023], controls=(ZoomControl(options=['position', 'zoom_in_text'…" |
| 159 | + "Map(center=[47.27073518869459, 14.388191800967462], controls=(ZoomControl(options=['position', 'zoom_in_text',…" |
148 | 160 | ] |
149 | 161 | }, |
150 | | - "execution_count": 12, |
| 162 | + "execution_count": 20, |
151 | 163 | "metadata": {}, |
152 | 164 | "output_type": "execute_result" |
153 | 165 | } |
154 | 166 | ], |
155 | 167 | "source": [ |
156 | 168 | "# Create a map centered at the approximate center of the area of interest\n", |
157 | | - "center = shape(area_of_interest).centroid\n", |
158 | | - "m = Map(center=[center.y, center.x], zoom=8)\n", |
| 169 | + "m = Map(center=[center.y, center.x], zoom=zoom)\n", |
159 | 170 | " \n", |
160 | 171 | "# Add the tiles (GeometryCollection) to the map\n", |
161 | 172 | "geo_json = GeoJSON(data=tiles)\n", |
|
177 | 188 | }, |
178 | 189 | { |
179 | 190 | "cell_type": "code", |
180 | | - "execution_count": 13, |
| 191 | + "execution_count": 38, |
181 | 192 | "id": "c5ca3fdd-7559-4fc7-8318-61b5dc59475f", |
182 | 193 | "metadata": {}, |
183 | 194 | "outputs": [ |
184 | 195 | { |
185 | 196 | "data": { |
186 | 197 | "text/plain": [ |
187 | | - "{'id': 6,\n", |
188 | | - " 'title': 'Forest Fire Detection',\n", |
| 198 | + "{'id': 13,\n", |
| 199 | + " 'title': 'Wind Turbine Detection',\n", |
189 | 200 | " 'label': 'openeo',\n", |
190 | 201 | " 'status': 'created'}" |
191 | 202 | ] |
192 | 203 | }, |
193 | | - "execution_count": 13, |
| 204 | + "execution_count": 38, |
194 | 205 | "metadata": {}, |
195 | 206 | "output_type": "execute_result" |
196 | 207 | } |
197 | 208 | ], |
198 | 209 | "source": [ |
199 | 210 | "upscaling_task = requests.post(f\"http://{dispatch_api}/upscale_tasks\", json={\n", |
200 | | - " \"title\": \"Forest Fire Detection\",\n", |
| 211 | + " \"title\": \"Wind Turbine Detection\",\n", |
201 | 212 | " \"label\": \"openeo\",\n", |
202 | 213 | " \"service\": {\n", |
203 | 214 | " \"endpoint\": endpoint,\n", |
204 | 215 | " \"application\": application\n", |
205 | 216 | " },\n", |
206 | 217 | " \"parameters\": {\n", |
207 | | - " \"temporal_extent\": time_of_interest\n", |
| 218 | + " \"year\": year\n", |
208 | 219 | " },\n", |
209 | 220 | " \"dimension\": {\n", |
210 | 221 | " \"name\": \"spatial_extent\",\n", |
|
226 | 237 | }, |
227 | 238 | { |
228 | 239 | "cell_type": "code", |
229 | | - "execution_count": 14, |
| 240 | + "execution_count": 39, |
230 | 241 | "id": "02e5c413-d110-4110-be59-c86e5226edc5", |
231 | 242 | "metadata": {}, |
232 | 243 | "outputs": [], |
|
254 | 265 | }, |
255 | 266 | { |
256 | 267 | "cell_type": "code", |
257 | | - "execution_count": 18, |
| 268 | + "execution_count": 40, |
258 | 269 | "id": "ac428293-7cd4-49a8-9bfa-4e0dc8f4d2cc", |
259 | 270 | "metadata": {}, |
260 | 271 | "outputs": [ |
261 | 272 | { |
262 | 273 | "data": { |
263 | 274 | "application/vnd.jupyter.widget-view+json": { |
264 | | - "model_id": "8c57dbf49c574581aa8b4efe312479e2", |
| 275 | + "model_id": "b2e2721b5cfb4e08a4416813f1f961d1", |
265 | 276 | "version_major": 2, |
266 | 277 | "version_minor": 0 |
267 | 278 | }, |
268 | 279 | "text/plain": [ |
269 | | - "Map(center=[42.251628548555004, -6.37490034623255], controls=(ZoomControl(options=['position', 'zoom_in_text',…" |
| 280 | + "Map(center=[47.27073518869459, 14.388191800967462], controls=(ZoomControl(options=['position', 'zoom_in_text',…" |
270 | 281 | ] |
271 | 282 | }, |
272 | 283 | "metadata": {}, |
273 | 284 | "output_type": "display_data" |
274 | 285 | }, |
275 | 286 | { |
276 | | - "ename": "ConnectionClosedError", |
277 | | - "evalue": "received 1012 (service restart); then sent 1012 (service restart)", |
278 | | - "output_type": "error", |
279 | | - "traceback": [ |
280 | | - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", |
281 | | - "\u001b[0;31mConnectionResetError\u001b[0m Traceback (most recent call last)", |
282 | | - "File \u001b[0;32m~/.pyenv/versions/3.10.12/lib/python3.10/asyncio/selector_events.py:862\u001b[0m, in \u001b[0;36m_SelectorSocketTransport._read_ready__data_received\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 861\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 862\u001b[0m data \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_sock\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrecv\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmax_size\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 863\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m (\u001b[38;5;167;01mBlockingIOError\u001b[39;00m, \u001b[38;5;167;01mInterruptedError\u001b[39;00m):\n", |
283 | | - "\u001b[0;31mConnectionResetError\u001b[0m: [Errno 54] Connection reset by peer", |
284 | | - "\nThe above exception was the direct cause of the following exception:\n", |
285 | | - "\u001b[0;31mConnectionClosedError\u001b[0m Traceback (most recent call last)", |
286 | | - "Cell \u001b[0;32mIn[18], line 73\u001b[0m\n\u001b[1;32m 70\u001b[0m \u001b[38;5;28;01mbreak\u001b[39;00m\n\u001b[1;32m 72\u001b[0m \u001b[38;5;66;03m# Run the websocket listener in the notebook\u001b[39;00m\n\u001b[0;32m---> 73\u001b[0m \u001b[38;5;28;01mawait\u001b[39;00m listen_for_updates()\n", |
287 | | - "Cell \u001b[0;32mIn[18], line 42\u001b[0m, in \u001b[0;36mlisten_for_updates\u001b[0;34m()\u001b[0m\n\u001b[1;32m 40\u001b[0m \u001b[38;5;28;01masync\u001b[39;00m \u001b[38;5;28;01mwith\u001b[39;00m websockets\u001b[38;5;241m.\u001b[39mconnect(ws_url) \u001b[38;5;28;01mas\u001b[39;00m websocket:\n\u001b[1;32m 41\u001b[0m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;28;01mTrue\u001b[39;00m:\n\u001b[0;32m---> 42\u001b[0m message \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mawait\u001b[39;00m websocket\u001b[38;5;241m.\u001b[39mrecv()\n\u001b[1;32m 43\u001b[0m message \u001b[38;5;241m=\u001b[39m json\u001b[38;5;241m.\u001b[39mloads(message)\n\u001b[1;32m 44\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m message\u001b[38;5;241m.\u001b[39mget(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mdata\u001b[39m\u001b[38;5;124m\"\u001b[39m):\n", |
288 | | - "File \u001b[0;32m~/.pyenv/versions/3.10.12/lib/python3.10/site-packages/websockets/asyncio/connection.py:322\u001b[0m, in \u001b[0;36mConnection.recv\u001b[0;34m(self, decode)\u001b[0m\n\u001b[1;32m 318\u001b[0m \u001b[38;5;66;03m# fallthrough\u001b[39;00m\n\u001b[1;32m 319\u001b[0m \n\u001b[1;32m 320\u001b[0m \u001b[38;5;66;03m# Wait for the protocol state to be CLOSED before accessing close_exc.\u001b[39;00m\n\u001b[1;32m 321\u001b[0m \u001b[38;5;28;01mawait\u001b[39;00m asyncio\u001b[38;5;241m.\u001b[39mshield(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mconnection_lost_waiter)\n\u001b[0;32m--> 322\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mprotocol\u001b[38;5;241m.\u001b[39mclose_exc \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mself\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mrecv_exc\u001b[39;00m\n", |
289 | | - "\u001b[0;31mConnectionClosedError\u001b[0m: received 1012 (service restart); then sent 1012 (service restart)" |
| 287 | + "name": "stdout", |
| 288 | + "output_type": "stream", |
| 289 | + "text": [ |
| 290 | + "Job finished with status failed\n" |
| 291 | + ] |
| 292 | + }, |
| 293 | + { |
| 294 | + "name": "stderr", |
| 295 | + "output_type": "stream", |
| 296 | + "text": [ |
| 297 | + "/var/folders/50/09_2zmx12zj6ks4fdl4y9wgc0000gn/T/ipykernel_92745/582752889.py:70: RuntimeWarning: coroutine 'Connection.close' was never awaited\n", |
| 298 | + " websocket.close()\n", |
| 299 | + "RuntimeWarning: Enable tracemalloc to get the object allocation traceback\n" |
290 | 300 | ] |
291 | 301 | } |
292 | 302 | ], |
293 | 303 | "source": [ |
294 | | - "m = Map(center=[42.251628548555004, -6.37490034623255], zoom=8)\n", |
| 304 | + "m = Map(center=[center.y, center.x], zoom=zoom)\n", |
295 | 305 | "geo_json = GeoJSON(\n", |
296 | 306 | " data={\n", |
297 | 307 | " \"type\": \"FeatureCollection\",\n", |
|
344 | 354 | " if job_status == \"finished\" and job_id not in processed_jobs:\n", |
345 | 355 | " processed_jobs.add(job_id)\n", |
346 | 356 | " await show_results(job_id)\n", |
347 | | - " else:\n", |
| 357 | + " elif job_status != \"finished\":\n", |
348 | 358 | " features.append({\n", |
349 | 359 | " \"type\": \"Feature\",\n", |
350 | 360 | " \"geometry\": job[\"parameters\"][\"spatial_extent\"],\n", |
|
359 | 369 | " }\n", |
360 | 370 | " geo_json.style_callback = job_style\n", |
361 | 371 | " if message[\"data\"][\"status\"] in [\"finished\", \"canceled\", \"failed\"]:\n", |
| 372 | + " print(f\"Job finished with status {message['data']['status']}\")\n", |
362 | 373 | " websocket.close()\n", |
363 | 374 | " break\n", |
364 | 375 | "\n", |
|
0 commit comments