{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Simple load collection example: ESA WorldCover\n", "\n", "\n", "This notebook is a simple example of how to load the ESA WorldCover dataset using the `load_collection` function from the `openeo` Python package. Here we will load the ESA WorldCover dataset for the year 2021 apply simple UDF and visualize the result." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Authenticated using refresh token.\n" ] } ], "source": [ "import openeo\n", "import json\n", "import folium\n", "import xarray as xr\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "import matplotlib.colors as mcolors\n", "\n", "\n", "# connect to the federated backend\n", "connection = openeo.connect(\"openeo.dataspace.copernicus.eu\").authenticate_oidc()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "List the available collections from the backend and fetch its description." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['SENTINEL3_OLCI_L1B',\n", " 'SENTINEL3_SLSTR',\n", " 'SENTINEL_5P_L2',\n", " 'COPERNICUS_VEGETATION_PHENOLOGY_PRODUCTIVITY_10M_SEASON1',\n", " 'COPERNICUS_VEGETATION_PHENOLOGY_PRODUCTIVITY_10M_SEASON2',\n", " 'COPERNICUS_PLANT_PHENOLOGY_INDEX',\n", " 'ESA_WORLDCOVER_10M_2020_V1',\n", " 'ESA_WORLDCOVER_10M_2021_V2',\n", " 'COPERNICUS_VEGETATION_INDICES',\n", " 'SENTINEL2_L1C',\n", " 'SENTINEL2_L2A',\n", " 'SENTINEL1_GRD',\n", " 'COPERNICUS_30',\n", " 'LANDSAT8_L2',\n", " 'SENTINEL3_SYN_L2_SYN',\n", " 'SENTINEL3_SLSTR_L2_LST',\n", " 'SENTINEL1_GLOBAL_MOSAICS']" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "connection.list_collection_ids()" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " " ], "text/plain": [ "{'assets': {},\n", " 'cube:dimensions': {'bands': {'type': 'bands', 'values': ['MAP']},\n", " 't': {'extent': ['2023-04-27T06:38:05Z', '2024-04-25T06:38:05Z'],\n", " 'type': 'temporal'},\n", " 'x': {'axis': 'x',\n", " 'extent': [-180.0, 180.0],\n", " 'step': 8.3333333333e-05,\n", " 'type': 'spatial'},\n", " 'y': {'axis': 'y',\n", " 'extent': [-90.0, 90.0],\n", " 'step': 8.3333333333e-05,\n", " 'type': 'spatial'}},\n", " 'description': 'WorldCover provides global land cover products for 2021 at 10 m resolution, developed and validated in near-real time based on Sentinel-1 and Sentinel-2 data. ',\n", " 'experimental': True,\n", " 'extent': {'spatial': {'bbox': [[-180.0, -90.0, 180.0, 90.0]]},\n", " 'temporal': {'interval': [['2023-04-27T06:38:05Z',\n", " '2024-04-25T06:38:05Z']]}},\n", " 'id': 'ESA_WORLDCOVER_10M_2021_V2',\n", " 'item_assets': {'MAP': {'description': 'classification band',\n", " 'eo:bands': [{'description': 'Classification', 'name': 'MAP'}],\n", " 'roles': ['data'],\n", " 'title': 'MAP',\n", " 'type': 'image/tiff; application=geotiff'}},\n", " 'keywords': ['VITO',\n", " 'COPERNICUS',\n", " 'ESA',\n", " 'Land Cover',\n", " 'Sentinel-1',\n", " 'Sentinel-2',\n", " 'Environment',\n", " 'WorldCover'],\n", " 'license': 'CC-BY-4.0',\n", " 'links': [{'href': 'https://esa-worldcover.org/',\n", " 'rel': 'alternate',\n", " 'title': 'Product website'},\n", " {'href': 'https://services.terrascope.be/catalogue/description?collection=urn:eop:VITO:ESA_WorldCover_10m_2021_V2',\n", " 'rel': 'alternate',\n", " 'title': 'EO OpenSearch catalog'},\n", " {'href': 'https://viewer.esa-worldcover.org/worldcover',\n", " 'rel': 'alternate',\n", " 'title': 'Product Viewer'},\n", " {'href': 'https://services.terrascope.be/wmts/v2',\n", " 'rel': 'wmts',\n", " 'wmts:layer': 'WORLDCOVER_2021_MAP'},\n", " {'href': 'https://openeo.dataspace.copernicus.eu/openeo/1.2/collections',\n", " 'rel': 'root'},\n", " {'href': 'https://openeo.dataspace.copernicus.eu/openeo/1.2/collections',\n", " 'rel': 'parent'},\n", " {'href': 'https://openeo.dataspace.copernicus.eu/openeo/1.2/collections/ESA_WORLDCOVER_10M_2021_V2',\n", " 'rel': 'self'}],\n", " 'providers': [{'name': 'VITO',\n", " 'roles': ['producer'],\n", " 'url': 'https://remotesensing.vito.be/'},\n", " {'name': 'ESA', 'roles': ['licensor'], 'url': 'https://esa.int/'}],\n", " 'stac_extensions': ['https://stac-extensions.github.io/item-assets/v1.0.0/schema.json',\n", " 'https://stac-extensions.github.io/raster/v1.1.0/schema.json',\n", " 'https://stac-extensions.github.io/classification/v1.0.0/schema.json'],\n", " 'stac_version': '1.0.0',\n", " 'summaries': {'eo:bands': [{'gsd': 10, 'name': 'MAP', 'type': 'uint8'}]},\n", " 'title': 'Global landcover at 10m resolution for 2021.',\n", " 'type': 'Collection'}" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "connection.describe_collection(\"ESA_WORLDCOVER_10M_2021_V2\")" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "# define aoi and temporal extent\n", "def read_json(filename: str) -> dict:\n", " with open(filename) as input:\n", " field = json.load(input)\n", " return field\n", "\n", "\n", "dates = [\"2021-01-01\", \"2021-12-31\"]\n", "aoi = read_json(\"aoi/Antwerp.geojson\")" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Make this Notebook Trusted to load map: File -> Trust Notebook
" ], "text/plain": [ "" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "m = folium.Map([51.18, 4.5], zoom_start=11)\n", "folium.GeoJson(aoi).add_to(m)\n", "m" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "# load data from the CDSE backend\n", "cube = connection.load_collection(\n", " \"ESA_WORLDCOVER_10M_2021_V2\", temporal_extent=dates, spatial_extent=aoi\n", ")" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "# let us apply a simple rescaling udf\n", "\n", "udf = openeo.UDF(\n", " \"\"\"\n", "import xarray\n", "\n", "def apply_datacube(cube: xarray.DataArray, context: dict) -> xarray.DataArray:\n", " cube.values = 0.8 * cube.values\n", " return cube\n", "\"\"\"\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Users can also load the UDF from a python file directly, instead of defining it as a string. For more information, please refer to the [openEO UDF documentation](https://open-eo.github.io/openeo-python-client/udf.html)." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "rescaled_cube = cube.apply(process=udf)" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rescaled_cube" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0:00:00 Job 'j-2502101653094c4694b9c4f438184002': send 'start'\n", "0:00:13 Job 'j-2502101653094c4694b9c4f438184002': created (progress 0%)\n", "0:00:18 Job 'j-2502101653094c4694b9c4f438184002': created (progress 0%)\n", "0:00:25 Job 'j-2502101653094c4694b9c4f438184002': created (progress 0%)\n", "0:00:33 Job 'j-2502101653094c4694b9c4f438184002': created (progress 0%)\n", "0:00:43 Job 'j-2502101653094c4694b9c4f438184002': created (progress 0%)\n", "0:00:55 Job 'j-2502101653094c4694b9c4f438184002': created (progress 0%)\n", "0:01:10 Job 'j-2502101653094c4694b9c4f438184002': running (progress N/A)\n", "0:01:29 Job 'j-2502101653094c4694b9c4f438184002': running (progress N/A)\n", "0:01:53 Job 'j-2502101653094c4694b9c4f438184002': running (progress N/A)\n", "0:02:23 Job 'j-2502101653094c4694b9c4f438184002': finished (progress 100%)\n" ] }, { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# job configuration\n", "\n", "job_options = {\n", " \"executor-memory\": \"2G\",\n", " \"executor-memoryOverhead\": \"3G\",\n", " \"python-memory\": \"3G\",\n", "}\n", "# running a batch job\n", "\n", "rescaled_cube.execute_batch(\n", " title=\"ESA_WORLDCOVER_10M_2021_V2\",\n", " outputfile=\"ESAWorldCover.nc\",\n", " job_options=job_options,\n", ")" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
Make this Notebook Trusted to load map: File -> Trust Notebook
" ], "text/plain": [ "" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# visualize the result\n", "\n", "ds = xr.open_dataset(\"ESAWorldCover.nc\")\n", "data = ds[[\"MAP\"]].to_array(dim=\"bands\").squeeze()\n", "# Replace zeros with NaN\n", "arr = data.values\n", "arr[arr == 0] = np.nan\n", "\n", "# Generate lat/lon grid\n", "lon, lat = np.meshgrid(\n", " data.x.values.astype(np.float64), data.y.values.astype(np.float64)\n", ")\n", "\n", "# Normalize data using a thematic color scale\n", "cmap = plt.get_cmap(\"hsv\") # Choose a thematic colormap\n", "norm = mcolors.Normalize(vmin=np.nanmin(arr), vmax=np.nanmax(arr)) # Scale values\n", "colored_data = cmap(norm(arr)) # Convert data to RGBA colors\n", "\n", "# Ensure correct shape (H, W, 4)\n", "colored_data = (colored_data[:, :, :4] * 255).astype(\n", " np.uint8\n", ") # Convert to uint8 format\n", "\n", "# Create Folium map\n", "m = folium.Map(location=[lat.mean(), lon.mean()], zoom_start=12)\n", "\n", "folium.raster_layers.ImageOverlay(\n", " image=colored_data,\n", " bounds=[[lat.min(), lon.min()], [lat.max(), lon.max()]],\n", " mercator_project=True,\n", " opacity=0.7,\n", ").add_to(m)\n", "\n", "m" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.12" } }, "nbformat": 4, "nbformat_minor": 4 }