From 09b4eaecd0a3e814fa7f158b0ce3510a6f8bee59 Mon Sep 17 00:00:00 2001 From: Alex Leith Date: Mon, 18 Dec 2023 14:23:41 +1100 Subject: [PATCH] Working config for geomad --- .gitignore | 1 + Makefile | 62 +- README.md | 7 +- docker-compose.yaml | 82 +- notebooks/DownloadFromPC.ipynb | 69 + notebooks/Landsat9_Basic.ipynb | 148 ++ notebooks/Sentinel1_Basic.ipynb | 580 +------- notebooks/Sentinel1_SurfaceWater.ipynb | 165 +++ ows/Dockerfile | 13 + ows_config/README.md | 45 + ows_config/__init__.py | 0 ows_config/aerials/__init__.py | 0 ows_config/aerials/ows_aerials_cfg.py | 62 + ows_config/common/__init__.py | 0 ows_config/common/ows_legend_cfg.py | 27 + ows_config/common/ows_reslim_cfg.py | 75 + ows_config/common/ows_util_tools.py | 13 + ows_config/elevation/__init__.py | 0 ows_config/elevation/ows_nasadem_cfg.py | 100 ++ ows_config/land_cover/__init__.py | 0 ows_config/land_cover/ows_io_lulc_cfg.py | 151 ++ ows_config/pacific_ows.py | 173 +++ ows_config/radar_backscatter/__init__.py | 0 ows_config/radar_backscatter/ows_s1_cfg.py | 89 ++ ows_config/surface_reflectance/__init__.py | 0 ows_config/surface_reflectance/band_sr_cfg.py | 51 + .../surface_reflectance/ows_landsat_cfg.py | 133 ++ ows_config/surface_reflectance/ows_s2_cfg.py | 28 + .../surface_reflectance/style_sr_cfg.py | 1231 +++++++++++++++++ products.csv | 1 + products/dep_ls_geomad.odc-product.yaml | 78 ++ 31 files changed, 2799 insertions(+), 585 deletions(-) create mode 100644 .gitignore create mode 100644 notebooks/DownloadFromPC.ipynb create mode 100644 notebooks/Landsat9_Basic.ipynb create mode 100644 notebooks/Sentinel1_SurfaceWater.ipynb create mode 100644 ows/Dockerfile create mode 100644 ows_config/README.md create mode 100644 ows_config/__init__.py create mode 100644 ows_config/aerials/__init__.py create mode 100644 ows_config/aerials/ows_aerials_cfg.py create mode 100644 ows_config/common/__init__.py create mode 100644 ows_config/common/ows_legend_cfg.py create mode 100644 ows_config/common/ows_reslim_cfg.py create mode 100644 ows_config/common/ows_util_tools.py create mode 100644 ows_config/elevation/__init__.py create mode 100644 ows_config/elevation/ows_nasadem_cfg.py create mode 100644 ows_config/land_cover/__init__.py create mode 100644 ows_config/land_cover/ows_io_lulc_cfg.py create mode 100644 ows_config/pacific_ows.py create mode 100644 ows_config/radar_backscatter/__init__.py create mode 100644 ows_config/radar_backscatter/ows_s1_cfg.py create mode 100644 ows_config/surface_reflectance/__init__.py create mode 100644 ows_config/surface_reflectance/band_sr_cfg.py create mode 100644 ows_config/surface_reflectance/ows_landsat_cfg.py create mode 100644 ows_config/surface_reflectance/ows_s2_cfg.py create mode 100644 ows_config/surface_reflectance/style_sr_cfg.py create mode 100644 products/dep_ls_geomad.odc-product.yaml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/Makefile b/Makefile index 3d28ddd..4ff39c0 100644 --- a/Makefile +++ b/Makefile @@ -3,9 +3,47 @@ # BBOX over Samoa and Tonga BBOX := -180.0,-20.0,-170.0,-10.0 +# Bigger BBOX over most of the Pacific +BIGBBOX := 135.0,-30.0,180.0,15.0 +BIGBBOX2 := -180,-30.0,-120.0,15.0 + up: docker compose up +# Init the DB + +datacube-init: + docker-compose exec explorer \ + datacube system init --no-init-users + +# Explorer + +explorer-init: + docker-compose exec explorer \ + cubedash-gen --init + +explorer-geupdaten: + docker-compose exec explorer \ + cubedash-gen --all + +# OWS + +ows-shell: + docker-compose exec ows bash + +ows-init: + docker-compose exec ows \ + datacube-ows-update --schema --role odc_admin + +ows-update: + docker-compose exec ows \ + bash -c " \ + datacube-ows-update --views && \ + datacube-ows-update \ + " + +# Indexing + products: dc-sync-products products.csv @@ -15,13 +53,21 @@ index-esri-lc: stac-to-dc \ --catalog-href=https://planetarycomputer.microsoft.com/api/stac/v1/ \ --collections=io-lulc-9-class \ - --bbox=${BBOX} + --bbox=${BIGBBOX} + stac-to-dc \ + --catalog-href=https://planetarycomputer.microsoft.com/api/stac/v1/ \ + --collections=io-lulc-9-class \ + --bbox=${BIGBBOX2} index-nasadem: stac-to-dc \ --catalog-href=https://planetarycomputer.microsoft.com/api/stac/v1/ \ --collections=nasadem \ - --bbox=${BBOX} + --bbox=${BIGBBOX} + stac-to-dc \ + --catalog-href=https://planetarycomputer.microsoft.com/api/stac/v1/ \ + --collections=nasadem \ + --bbox=${BIGBBOX2} index-sentinel-1: stac-to-dc \ @@ -30,6 +76,18 @@ index-sentinel-1: --datetime='2023-01-01/2023-12-31' \ --collections='sentinel-1-rtc' +index-sentinel-1-pacific: + stac-to-dc \ + --catalog-href='https://planetarycomputer.microsoft.com/api/stac/v1/' \ + --bbox='$(BIGBBOX)' \ + --datetime='2023-01-01/2023-12-31' \ + --collections='sentinel-1-rtc' + stac-to-dc \ + --catalog-href='https://planetarycomputer.microsoft.com/api/stac/v1/' \ + --bbox='$(BIGBBOX2)' \ + --datetime='2023-01-01/2023-12-31' \ + --collections='sentinel-1-rtc' + index-sentinel-2: stac-to-dc \ --catalog-href='https://planetarycomputer.microsoft.com/api/stac/v1/' \ diff --git a/README.md b/README.md index 55d9042..5f03c64 100644 --- a/README.md +++ b/README.md @@ -10,19 +10,16 @@ need to have environment variables exported for connectivity to the Postgres database too, which runs in Docker. * Export environment variables: + ``` bash export DB_HOSTNAME=localhost export DB_USERNAME=pacific export DB_PASSWORD=secretpassword export DB_DATABASE=odc ``` + * Start the postgres DB: `make up` or `docker compose up` * Add products: `make products` or `dc-sync-products products.csv` * Index data individually or do it all with `make index-all` * Now you can use any of the products. There's only one example notebook for now, for [Sentinel-1](notebooks/Sentinel1_Basic.ipynb). - - -## Next steps - -Todo: get OWS and Explorer working as well a little Terria to visualise OWS diff --git a/docker-compose.yaml b/docker-compose.yaml index 162756f..d923b10 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,10 +1,78 @@ services: - postgis: - image: mdillon/postgis:9.6 - restart: always + # postgis: + # image: mdillon/postgis:9.6 + # restart: always + # environment: + # POSTGRES_USER: pacific + # POSTGRES_PASSWORD: secretpassword + # POSTGRES_DB: odc + # ports: + # - "5432:5432" + # terria: + # image: alexgleith/terriamap + # # environment: + # # - VIRTUAL_HOST=terria.vcap.me + # # expose: + # # - "3001" + # ports: + # - "3001:3001" + ows: + network_mode: host + build: + context: ows + dockerfile: Dockerfile + volumes: + - ./ows_config:/env/config/ows_config environment: - POSTGRES_USER: pacific - POSTGRES_PASSWORD: secretpassword - POSTGRES_DB: odc + - DB_HOSTNAME=host.docker.internal + - DB_USERNAME=odc_admin + - DB_PASSWORD=${DB_PASSWORD} + - DB_DATABASE=odc + - PYTHONPATH=/env/config + - WMS_CONFIG_PATH=/env/config/ows_config/pacific_ows.py + - DATACUBE_OWS_CFG=ows_config.pacific_ows.ows_cfg + # - PC_SDK_SUBSCRIPTION_KEY=${PC_SDK_SUBSCRIPTION_KEY} + # - VIRTUAL_HOST=ows.localtest.me ports: - - "5432:5432" + - "80:8000" + # explorer: + # image: opendatacube/explorer:latest + # environment: + # - DB_HOSTNAME=postgis + # - DB_USERNAME=pacific + # - DB_PASSWORD=secretpassword + # - DB_DATABASE=odc + # # - CUBEDASH_SETTINGS=/code/settings.env.py + # # - VIRTUAL_HOST=explorer.localtest.me + # # expose: + # # - "8080" + # ports: + # - "8080:8080" + # # volumes: + # # - ./explorer/settings.env.py:/code/settings.env.py + # command: + # - "gunicorn" + # - "-b" + # - "0.0.0.0:8080" + # - "-w" + # - "3" + # - "--threads=2" + # - "-k" + # - "gthread" + # - "--timeout" + # - "90" + # - "--config" + # - "python:cubedash.gunicorn_config" + # - "cubedash:app" + # nginx-proxy: + # image: nginxproxy/nginx-proxy + # container_name: nginx-proxy + # ports: + # - "80:80" + # # - "443:443" + # volumes: + # # - conf:/etc/nginx/conf.d + # # - vhost:/etc/nginx/vhost.d + # # - html:/usr/share/nginx/html + # # - certs:/etc/nginx/certs:ro + # - /var/run/docker.sock:/tmp/docker.sock:ro diff --git a/notebooks/DownloadFromPC.ipynb b/notebooks/DownloadFromPC.ipynb new file mode 100644 index 0000000..aa66e37 --- /dev/null +++ b/notebooks/DownloadFromPC.ipynb @@ -0,0 +1,69 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import pystac\n", + "from planetary_computer import sign_url, sign_item\n", + "from pathlib import Path\n", + "import requests" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "item = pystac.Item.from_file(\"https://planetarycomputer.microsoft.com/api/stac/v1/collections/sentinel-2-l2a/items/S2A_MSIL2A_20230623T214811_R043_T02LMK_20230624T044756\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "signed = sign_item(item)\n", + "\n", + "folder = Path(\"files\") / item.id\n", + "folder.mkdir(exist_ok=True)\n", + "\n", + "for asset in signed.assets.values():\n", + " # local_file is the final part of the URL, e.g. \"S2A_MSIL2A_20230623T214811_R043_T02LMK_20230624T044756.tif\"\n", + " local_file = folder / Path(asset.href).name.split(\"?\")[0]\n", + " print(f\"Downloading {asset.href} to {local_file}\")\n", + "\n", + " # Download the file using requests\n", + " with open(local_file, \"wb\") as f:\n", + " f.write(requests.get(asset.href).content)\n", + "\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.11.6" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebooks/Landsat9_Basic.ipynb b/notebooks/Landsat9_Basic.ipynb new file mode 100644 index 0000000..03abc54 --- /dev/null +++ b/notebooks/Landsat9_Basic.ipynb @@ -0,0 +1,148 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import datacube\n", + "from planetary_computer import sign_url\n", + "from dea_tools.plotting import rgb" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dc = datacube.Datacube(app='ls9-example')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "datasets = dc.find_datasets(product=\"ls9_c2l2_sr\", region_code=\"069069\", time=\"20230505\")\n", + "\n", + "print(f\"Found {len(datasets)} datasets\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "data = dc.load(\n", + " datasets=datasets,\n", + " measurements=[\"red\", \"green\", \"blue\"],\n", + " output_crs=\"EPSG:3832\",\n", + " resolution=(-30, 30),\n", + " lon=(-172.825, -172.125),\n", + " lat=(-13.85, -13.4),\n", + " patch_url=sign_url,\n", + " skip_broken_datasets=True\n", + ")\n", + "data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "data = data.where(data != 0)\n", + "scaled = data * 2.75e-5 - 0.2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rgb(scaled, bands=[\"red\", \"green\", \"blue\"], size=10)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import odc.ui\n", + "import ipyleaflet\n", + "from ipywidgets import widgets as w\n", + "\n", + "polygons, bbox = odc.ui.dss_to_geojson(data.red, bbox=True)\n", + "\n", + "\n", + "zoom = odc.ui.zoom_from_bbox(bbox)\n", + "center = (bbox.bottom + bbox.top) * 0.5, (bbox.right + bbox.left) * 0.5\n", + "\n", + "m = ipyleaflet.Map(\n", + " center=center,\n", + " zoom=zoom,\n", + " scroll_wheel_zoom=True, # Allow zoom with the mouse scroll wheel\n", + " layout=w.Layout(\n", + " width='600px', # Set Width of the map to 600 pixels, examples: \"100%\", \"5em\", \"300px\"\n", + " height='600px', # Set height of the map\n", + " ))\n", + "\n", + "# Add full-screen and layer visibility controls\n", + "m.add_control(ipyleaflet.FullScreenControl())\n", + "m.add_control(ipyleaflet.LayersControl())\n", + "\n", + "img_layer = odc.ui.mk_image_overlay(\n", + " scaled.isel(time=0),\n", + " clamp=(0, 0.2),\n", + " # clamp=(2700, 20000), # 3000 -- brightest pixel level\n", + " fmt='png',\n", + " bands=[\"red\", \"green\", \"blue\"],\n", + ") # \"jpeg\" is another option\n", + "\n", + "# Add image layer to a map we created earlier\n", + "m.add_layer(img_layer)\n", + "\n", + "slider = w.FloatSlider(min=0, max=1, value=1, # Opacity is valid in [0,1] range\n", + " orientation='horizontal', # Vertical slider is what we want\n", + " readout=False, # No need to show exact value\n", + " layout=w.Layout(width='6em')) # Fine tune display layout: make it thinner\n", + "\n", + "# Connect slider value to opacity property of the Image Layer\n", + "w.jslink((slider, 'value'), \n", + " (img_layer, 'opacity') )\n", + "m.add_control(ipyleaflet.WidgetControl(widget=slider))\n", + "\n", + "display(m)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.11.6" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebooks/Sentinel1_Basic.ipynb b/notebooks/Sentinel1_Basic.ipynb index 13843fc..6443967 100644 --- a/notebooks/Sentinel1_Basic.ipynb +++ b/notebooks/Sentinel1_Basic.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -12,7 +12,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -21,17 +21,9 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Found 184 datasets\n" - ] - } - ], + "outputs": [], "source": [ "datasets = dc.find_datasets(product=\"sentinel_1_rtc\")\n", "\n", @@ -40,432 +32,9 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "
<xarray.Dataset>\n",
-       "Dimensions:      (time: 1, y: 2351, x: 2967)\n",
-       "Coordinates:\n",
-       "  * time         (time) datetime64[ns] 2023-01-05T06:08:15.303917\n",
-       "  * y            (y) float64 -1.753e+06 -1.753e+06 ... -1.988e+06 -1.988e+06\n",
-       "  * x            (x) float64 3.793e+06 3.793e+06 3.793e+06 ... 4.09e+06 4.09e+06\n",
-       "    spatial_ref  int32 3832\n",
-       "Data variables:\n",
-       "    vv           (time, y, x) float32 nan nan nan nan nan ... nan nan nan nan\n",
-       "    vh           (time, y, x) float32 nan nan nan nan nan ... nan nan nan nan\n",
-       "Attributes:\n",
-       "    crs:           EPSG:3832\n",
-       "    grid_mapping:  spatial_ref
" - ], - "text/plain": [ - "\n", - "Dimensions: (time: 1, y: 2351, x: 2967)\n", - "Coordinates:\n", - " * time (time) datetime64[ns] 2023-01-05T06:08:15.303917\n", - " * y (y) float64 -1.753e+06 -1.753e+06 ... -1.988e+06 -1.988e+06\n", - " * x (x) float64 3.793e+06 3.793e+06 3.793e+06 ... 4.09e+06 4.09e+06\n", - " spatial_ref int32 3832\n", - "Data variables:\n", - " vv (time, y, x) float32 nan nan nan nan nan ... nan nan nan nan\n", - " vh (time, y, x) float32 nan nan nan nan nan ... nan nan nan nan\n", - "Attributes:\n", - " crs: EPSG:3832\n", - " grid_mapping: spatial_ref" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "data = dc.load(datasets=datasets[0:1], output_crs=\"EPSG:3832\", resolution=(-100, 100), patch_url=sign_url)\n", "data = data.where(data.vv != -32768)\n", @@ -475,140 +44,9 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
Make this Notebook Trusted to load map: File -> Trust Notebook
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "import folium\n", "import odc.geo.xr\n", @@ -651,7 +89,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.4" + "version": "3.11.6" }, "orig_nbformat": 4 }, diff --git a/notebooks/Sentinel1_SurfaceWater.ipynb b/notebooks/Sentinel1_SurfaceWater.ipynb new file mode 100644 index 0000000..6e72d6e --- /dev/null +++ b/notebooks/Sentinel1_SurfaceWater.ipynb @@ -0,0 +1,165 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import datacube\n", + "from planetary_computer import sign_url\n", + "import holoviews as hv\n", + "\n", + "from dea_tools.plotting import rgb\n", + "\n", + "import hvplot.xarray\n", + "hv.extension('bokeh')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "dc = datacube.Datacube()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Find data over Fiji\n", + "lon = [177, 179]\n", + "lat = [-18.5, -17]\n", + "datasets = dc.find_datasets(product=\"sentinel_1_rtc\", lon=lon, lat=lat)\n", + "\n", + "print(f\"Found {len(datasets)} datasets\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "data = dc.load(datasets=datasets[0:1], output_crs=\"EPSG:3832\", resolution=(-25, 25), patch_url=sign_url, lon=lon, lat=lat)\n", + "data = data.where(data.vv != -32768)\n", + "\n", + "data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Remove the time dimension\n", + "data = data.squeeze(\"time\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "window_size = 3\n", + "\n", + "# Add vv/vh\n", + "data[\"vv_vh\"] = data.vv / data.vh\n", + "\n", + "# Smooth the dataset with a rolling mean\n", + "data[\"vv_smoothed\"] = data.vv.rolling(x=window_size, y=window_size, center=True).mean()\n", + "data[\"vh_smoothed\"] = data.vh.rolling(x=window_size, y=window_size, center=True).mean()\n", + "data[\"vv_vh_smoothed\"] = data.vv_vh.rolling(x=window_size, y=window_size, center=True).mean()\n", + "\n", + "median = data[[\"vv_smoothed\", \"vh_smoothed\", \"vv_vh_smoothed\"]].median()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "rgb(data[[\"vv_smoothed\", \"vh_smoothed\", \"vv_vh_smoothed\"]] / median, bands=[\"vv_smoothed\", \"vh_smoothed\", \"vv_vh_smoothed\"], size=10)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "data.vv_smoothed.hvplot.hist(bins=100, bin_range=(0, 0.5), title=\"VV Histogram\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "data[\"water\"] = data.vv_smoothed < 0.04\n", + "data[\"water\"] = data.water.where(data.water)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import folium\n", + "import odc.geo.xr\n", + "\n", + "m = folium.Map(control_scale=True, tiles=None)\n", + "\n", + "data.vv_smoothed.odc.add_to(m, opacity=1.0, name=\"vv_smoothed\", vmin=0, vmax=0.5)\n", + "data.vv.odc.add_to(m, opacity=1.0, name=\"vv\", vmin=0, vmax=0.5)\n", + "data.water.odc.add_to(m, opacity=1.0, name=\"water\", vmin=0, vmax=1, cmap=\"Blues\")\n", + "\n", + "# Zoom map\n", + "m.fit_bounds(data.odc.map_bounds())\n", + "\n", + "tile = folium.TileLayer(\n", + " tiles = 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',\n", + " attr = 'Esri',\n", + " name = 'Esri Satellite',\n", + " overlay = False,\n", + " control = True\n", + ").add_to(m)\n", + "folium.TileLayer('openstreetmap').add_to(m)\n", + "\n", + "folium.LayerControl().add_to(m)\n", + "display(m)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.11.4" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/ows/Dockerfile b/ows/Dockerfile new file mode 100644 index 0000000..a303615 --- /dev/null +++ b/ows/Dockerfile @@ -0,0 +1,13 @@ +FROM opendatacube/ows:1.8.36 + +USER root + +RUN apt-get update && \ + apt-get install -y python3-pip &&\ + apt-get autoclean && \ + apt-get autoremove && \ + rm -rf /var/lib/{apt,dpkg,cache,log} + +RUN pip3 install planetary_computer && pip3 freeze + +USER ows diff --git a/ows_config/README.md b/ows_config/README.md new file mode 100644 index 0000000..a82cf5c --- /dev/null +++ b/ows_config/README.md @@ -0,0 +1,45 @@ +# OWS CFG refactored + +Goal: Split mega single file ows_cfg.py into multiple manageable/maintainable config files. + +## Structure + +### root config + +This file will act as an assembler which include all the layers required for the ows service. `maynooth_ows.py` + +### Resuable + +- legend definition +- resource limit definition +- custom functions + +### deployment Groups + +- ??? + +### Best practices + +#### individual style + +Each style definition for layers is to be declared following naming convention `style_*` + +#### Grouping of styles + +Some layers share common style definitions and in the configuration file they should be declared following naming convention `styles_*` + +#### bands + +Each product's bands definition is to be declared following naming convention `bands_*` + +#### abstract + +If several layers have long abstract move them to `abstract_*_cfg.py` and declared following naming convention `abstract_*` + +### Resource Limit + +Any unique resource limiting configuration should be declared in `ows_reslim_cfg.py` and be declared following naming convention `reslim_*` + +### common legend definition + +Reusable legend definition should be declared in `ows_legend_cfg.py` and be declared following naming convention `legend_*` diff --git a/ows_config/__init__.py b/ows_config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ows_config/aerials/__init__.py b/ows_config/aerials/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ows_config/aerials/ows_aerials_cfg.py b/ows_config/aerials/ows_aerials_cfg.py new file mode 100644 index 0000000..023ca9b --- /dev/null +++ b/ows_config/aerials/ows_aerials_cfg.py @@ -0,0 +1,62 @@ +layer = { + "title": "Aerial Imagery RGB", + "name": "aerial_rgb", + "abstract": """ +TODO +""", + "product_name": "airborne_rgb", + "time_resolution": "day", + "bands": { + "red": [], + "green": [], + "blue": [] + }, + "resource_limits": { + "wms": { + "zoomed_out_fill_colour": [150, 180, 200, 160], + "min_zoom_factor": 35.0, + }, + "wcs": {} + }, + "image_processing": { + "extent_mask_func": "datacube_ows.ogc_utils.mask_by_val", + "always_fetch_bands": [], + "manual_merge": False + }, + "native_crs": "EPSG:3857", + "native_resolution": [0.1677696917139964439, -0.1677696917139964439], + "wcs": {}, + "styling": { + "default_style": "simple_rgb", + "styles": [ + { + "name": "simple_rgb", + "title": "Simple RGB", + "abstract": "Simple true-colour image, using the red, green and blue bands", + "components": {"red": {"red": 1.0}, "green": {"green": 1.0}, "blue": {"blue": 1.0}}, + "scale_range": [0.0, 255.0], + }, + { + "name": "red", + "title": "Red", + "abstract": "Red band", + "components": {"red": {"red": 1.0}, "green": {"red": 1.0}, "blue": {"red": 1.0}}, + "scale_range": [0, 255], + }, + { + "name": "green", + "title": "Green", + "abstract": "Green band", + "components": {"red": {"green": 1.0}, "green": {"green": 1.0}, "blue": {"green": 1.0}}, + "scale_range": [0, 255], + }, + { + "name": "blue", + "title": "Blue", + "abstract": "Blue band", + "components": {"red": {"blue": 1.0}, "green": {"blue": 1.0}, "blue": {"blue": 1.0}}, + "scale_range": [0, 255], + } + ], + }, +} diff --git a/ows_config/common/__init__.py b/ows_config/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ows_config/common/ows_legend_cfg.py b/ows_config/common/ows_legend_cfg.py new file mode 100644 index 0000000..e6c7a24 --- /dev/null +++ b/ows_config/common/ows_legend_cfg.py @@ -0,0 +1,27 @@ +# Reusable Chunks 3. Legends +legend_idx_0_1_5ticks = { + "begin": "0.0", + "end": "1.0", + "ticks_every": "0.2", + "units": "unitless", +} + +legend_idx_percentage_by_10 = { + "begin": "0.0", + "end": "1.0", + "ticks_every": "0.1", + "units": "%", + "tick_labels": { + "0.0": {"label": "0"}, + "0.1": {"label": "10"}, + "0.2": {"label": "20"}, + "0.3": {"label": "30"}, + "0.4": {"label": "40"}, + "0.5": {"label": "50"}, + "0.6": {"label": "60"}, + "0.7": {"label": "70"}, + "0.8": {"label": "80"}, + "0.9": {"label": "90"}, + "1.0": {"label": "100"}, + }, +} diff --git a/ows_config/common/ows_reslim_cfg.py b/ows_config/common/ows_reslim_cfg.py new file mode 100644 index 0000000..d69b835 --- /dev/null +++ b/ows_config/common/ows_reslim_cfg.py @@ -0,0 +1,75 @@ +# Defines cache control on GetMap requests +dataset_cache_rules = [ + { + "min_datasets": 5, + "max_age": 60 * 60 * 24, + }, + { + "min_datasets": 9, + "max_age": 60 * 60 * 24 * 7, + }, + { + "min_datasets": 17, + "max_age": 60 * 60 * 24 * 30, + }, + { + "min_datasets": 65, + "max_age": 60 * 60 * 24 * 120, + }, +] + +# Resolution Limits +reslim_landsat = { + "wms": { + "zoomed_out_fill_colour": [150, 180, 200, 160], + "min_zoom_factor": 35.0, + "dataset_cache_rules": dataset_cache_rules, + }, + "wcs": { + # "max_datasets": 16, # Defaults to no dataset limit + }, +} + +reslim_sentinel2 = { + "wms": { + "zoomed_out_fill_colour": [150, 180, 200, 160], + "min_zoom_factor": 20.0, # defaults to 300! + "dataset_cache_rules": dataset_cache_rules, + }, + "wcs": { + "max_datasets": 64, # Defaults to no dataset limit + }, +} + +reslim_zoom9 = { + "wms": { + "zoomed_out_fill_colour": [150, 180, 200, 160], + "min_zoom_factor": 2000.0, + "dataset_cache_rules": dataset_cache_rules, + }, + "wcs": { + "max_datasets": 64, # Defaults to no dataset limit + }, +} + +reslim_nasadem = { + "wms": { + "zoomed_out_fill_colour": [150, 180, 200, 160], + "min_zoom_factor": 10.0, + "dataset_cache_rules": dataset_cache_rules, + }, + "wcs": { + # "max_datasets": 16, # Defaults to no dataset limit + }, +} + +reslim_continental = { + "wms": { + "zoomed_out_fill_colour": [150, 180, 200, 160], + "min_zoom_factor": 10.0, + "dataset_cache_rules": dataset_cache_rules, + }, + "wcs": { + "max_datasets": 32, # Defaults to no dataset limit + }, +} diff --git a/ows_config/common/ows_util_tools.py b/ows_config/common/ows_util_tools.py new file mode 100644 index 0000000..301dd5c --- /dev/null +++ b/ows_config/common/ows_util_tools.py @@ -0,0 +1,13 @@ +import numpy # pylint: disable=import-error + + +def mask_by_nan(data, band): + return ~numpy.isnan(data[band]) + + +def mask_by_emad_nan(data, band_mapper=None): + if band_mapper: + emad = band_mapper("emad") + else: + emad = "emad" + return ~numpy.isnan(data[emad]) diff --git a/ows_config/elevation/__init__.py b/ows_config/elevation/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ows_config/elevation/ows_nasadem_cfg.py b/ows_config/elevation/ows_nasadem_cfg.py new file mode 100644 index 0000000..b2ececf --- /dev/null +++ b/ows_config/elevation/ows_nasadem_cfg.py @@ -0,0 +1,100 @@ +from ows_config.common.ows_reslim_cfg import reslim_nasadem + +bands_elevation = { + "elevation": [], +} + +style_greyscale = { + "name": "greyscale", + "title": "Greyscale", + "abstract": "Greyscale elevation", + "needed_bands": ["elevation"], + "index_function": { + "function": "datacube_ows.band_utils.single_band", + "mapped_bands": True, + "kwargs": { + "band": "elevation", + }, + }, + "color_ramp": [ + {"value": -0, "color": "#383838", "alpha": 0.0}, + {"value": 0.1, "color": "#383838"}, + {"value": 250, "color": "#5e5e5e"}, + {"value": 500, "color": "#858585"}, + {"value": 1000, "color": "#adadad"}, + {"value": 2000, "color": "#d4d4d4"}, + {"value": 4000, "color": "#fafafa"}, + ], + "legend": { + "title": "Elevation ", + "begin": "0", + "end": "4000", + "ticks_every": 400, + "units": "m", + "tick_labels": { + "4000": {"prefix": ">"}, + }, + }, +} + +style_colours = { + "name": "colours", + "title": "Coloured", + "abstract": "Coloured elevation", + "needed_bands": ["elevation"], + "index_function": { + "function": "datacube_ows.band_utils.single_band", + "mapped_bands": True, + "kwargs": { + "band": "elevation", + }, + }, + "color_ramp": [ + {"value": -0, "color": "#e1f2ff", "alpha": 0.0}, + {"value": 0.1, "color": "#41c23c"}, + {"value": 150, "color": "#f9a80e"}, + {"value": 300, "color": "#cb5f3e"}, + {"value": 400, "color": "#9d387d"}, + {"value": 500, "color": "#ba6daa"}, + {"value": 900, "color": "#d7a2d6"}, + {"value": 1200, "color": "#e6c8e6"}, + {"value": 4000, "color": "#ffecf9"}, + ], + "legend": { + "title": "Elevation ", + "begin": "0", + "end": "4000", + "ticks_every": 400, + "units": "m", + "tick_labels": { + "4000": {"prefix": ">"}, + }, + }, +} + +layer = { + "title": "NASADEM", + "name": "nasadem", + "abstract": """ +Todo... +""", + "product_name": "nasadem", + "time_resolution": "summary", + "bands": bands_elevation, + "resource_limits": reslim_nasadem, + "image_processing": { + "extent_mask_func": "datacube_ows.ogc_utils.mask_by_val", + "always_fetch_bands": [], + "manual_merge": False, + }, + "native_crs": "EPSG:4326", + "native_resolution": [ + 0.000277777777780, + -0.000277777777780, + ], + "wcs": {}, + "styling": { + "default_style": "greyscale", + "styles": [style_greyscale, style_colours], + }, +} diff --git a/ows_config/land_cover/__init__.py b/ows_config/land_cover/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ows_config/land_cover/ows_io_lulc_cfg.py b/ows_config/land_cover/ows_io_lulc_cfg.py new file mode 100644 index 0000000..3e53ec4 --- /dev/null +++ b/ows_config/land_cover/ows_io_lulc_cfg.py @@ -0,0 +1,151 @@ +from ows_config.common.ows_reslim_cfg import reslim_continental + +bands_esri = { + "data": [], +} + +style_colours = { + "name": "style_colours", + "title": "Coloured", + "abstract": "Land cover colours", + "needed_bands": ["data"], + "index_function": { + "function": "datacube_ows.band_utils.single_band", + "mapped_bands": True, + "kwargs": { + "band": "data", + }, + }, + "value_map": { + "data": [ + { + "title": "", + "abstract": "", + "values": [0], + "alpha": 0.0, + "color": "#707070", + }, + { + "title": "Water", + "abstract": "", + "values": [1], + "color": "#419BDF", + }, + { + "title": "Trees", + "abstract": "", + "values": [2], + "color": "#397D49", + }, + { + "title": "Grass", + "abstract": "", + "values": [3], + "color": "#88B053", + }, + { + "title": "Flooded Vegetation", + "abstract": "", + "values": [4], + "color": "#7A87C6", + }, + { + "title": "Crops", + "abstract": "", + "values": [5], + "color": "#E49635", + }, + { + "title": "Scrub/Shrub", + "abstract": "", + "values": [6], + "color": "#DFC35A", + }, + { + "title": "Built Area", + "abstract": "", + "values": [7], + "color": "#C4281B", + }, + { + "title": "Bare Ground", + "abstract": "", + "values": [8], + "color": "#A59B8F", + }, + { + "title": "Snow/Ice", + "abstract": "", + "values": [9], + "color": "#A8EBFF", + }, + { + "title": "Clouds", + "abstract": "", + "values": [10], + "color": "#616161", + }, + { + "title": "Rangeland", + "abstract": "", + "values": [11], + "color": "#E3E2C3", + }, + ] + }, + "legend": {"width": 3.0, "height": 3.0}, +} + +layer = { + "title": "ESRI Land Use/Land Cover", + "name": "esri_lulc", + "abstract": """ +Todo... +""", + "product_name": "io_lulc", + "time_resolution": "summary", + "bands": bands_esri, + "resource_limits": reslim_continental, + "image_processing": { + "extent_mask_func": "datacube_ows.ogc_utils.mask_by_val", + "always_fetch_bands": [], + "manual_merge": False, + }, + "native_crs": "EPSG:3857", + "native_resolution": [ + 10.0, + -10.0, + ], + "wcs": {}, + "styling": { + "default_style": "style_colours", + "styles": [style_colours], + }, +} + +layer_9_class = { + "title": "ESRI Land Use/Land Cover 9 Class", + "name": "esri_lulc_9_class", + "abstract": """ +Todo... +""", + "product_name": "io_lulc_9_class", + "time_resolution": "summary", + "bands": bands_esri, + "resource_limits": reslim_continental, + "image_processing": { + "extent_mask_func": "datacube_ows.ogc_utils.mask_by_val", + "always_fetch_bands": [], + "manual_merge": False, + }, + "native_crs": "EPSG:3857", + "native_resolution": [ + 10.0, + -10.0, + ], + "wcs": {}, + "styling": { + "default_style": "style_colours", + "styles": [style_colours], + }, +} diff --git a/ows_config/pacific_ows.py b/ows_config/pacific_ows.py new file mode 100644 index 0000000..e8753e5 --- /dev/null +++ b/ows_config/pacific_ows.py @@ -0,0 +1,173 @@ +ows_cfg = { + "global": { + # Master config for all services and products. + "response_headers": { + "Access-Control-Allow-Origin": "*", # CORS header + }, + "services": { + "wms": True, + "wcs": True, + "wmts": True, + }, + "published_CRSs": { + "EPSG:3857": { # Web Mercator + "geographic": False, + "horizontal_coord": "x", + "vertical_coord": "y", + }, + "EPSG:3577": { # GDA-94, Australian Albers. Not sure why, but it's required! + "geographic": False, + "horizontal_coord": "x", + "vertical_coord": "y", + }, + "EPSG:4326": {"geographic": True, "vertical_coord_first": True}, # WGS-84 + "EPSG:3832": { # Some Pacific projection + "geographic": False, + "horizontal_coord": "x", + "vertical_coord": "y", + }, + }, + "allowed_urls": [ + "http://localhost:80", + ], + # Metadata to go straight into GetCapabilities documents + "title": "Digital Earth Pacific Web Services", + "abstract": """TODO...""", + "info_url": "", + "keywords": [ + "Digital Earth Pacific" + ], + "contact_info": { + "person": "TODO", + "organisation": "Digital Earth Pacific", + "position": "", + "address": { + "type": "postal", + "address": "TODO", + "city": "TODO", + "state": "TODO", + "postcode": "TODO", + "country": "New Caledonia", + }, + "telephone": "TODO", + "fax": "", + "email": "TODO", + }, + "fees": "", + "access_constraints": "TODO" + }, # END OF global SECTION + "wms": { + # Config for WMS service, for all products/layers + # "s3_aws_zone": "us-west-2", + "max_width": 512, + "max_height": 512, + }, # END OF wms SECTION + "wcs": { + # Config for WCS service, for all products/coverages + "default_geographic_CRS": "EPSG:4326", + "formats": { + "GeoTIFF": { + # "renderer": "datacube_ows.wcs_utils.get_tiff", + "renderers": { + "1": "datacube_ows.wcs1_utils.get_tiff", + "2": "datacube_ows.wcs2_utils.get_tiff", + }, + "mime": "image/geotiff", + "extension": "tif", + "multi-time": False, + }, + "netCDF": { + # "renderer": "datacube_ows.wcs_utils.get_netcdf", + "renderers": { + "1": "datacube_ows.wcs1_utils.get_netcdf", + "2": "datacube_ows.wcs2_utils.get_netcdf", + }, + "mime": "application/x-netcdf", + "extension": "nc", + "multi-time": True, + }, + }, + "native_format": "GeoTIFF", + }, # END OF wcs SECTION + "layers": [ + { + "title": "Digital Earth Pacific OWS", + "abstract": "TODO", + "layers": [ + # Hierarchical list of layers + { + "title": "Earth Observation", + "abstract": """Earth Observation""", + "layers": [ + # { + # "title": "Surface Reflectance", + # "abstract": """Surface Reflectance""", + # "layers": [ + # { + # "include": "ows_config.surface_reflectance.ows_s2_cfg.layer", + # "type": "python", + # }, + # { + # "include": "ows_config.surface_reflectance.ows_landsat_cfg.layer_ls5", + # "type": "python", + # }, + # { + # "include": "ows_config.surface_reflectance.ows_landsat_cfg.layer_ls7", + # "type": "python", + # }, + # { + # "include": "ows_config.surface_reflectance.ows_landsat_cfg.layer_ls8", + # "type": "python", + # }, + # { + # "include": "ows_config.surface_reflectance.ows_landsat_cfg.layer_ls9", + # "type": "python", + # }, + # ], + # }, + # { + # "title": "Radar backscatter", + # "abstract": """Radar backscatter""", + # "layers": [ + # { + # "include": "ows_config.radar_backscatter.ows_s1_cfg.layer", + # "type": "python", + # }, + # ] + # }, + { + "title": "Annual Summary Products", + "abstract": """Annual Summary Products""", + "layers": [ + { + "include": "ows_config.surface_reflectance.ows_landsat_cfg.dep_ls_geomad", + "type": "python", + }, + ] + } + ], + }, + # { + # "title": "Elevation", + # "abstract": """Digital elevation model""", + # "layers": [ + # { + # "include": "ows_config.elevation.ows_nasadem_cfg.layer", + # "type": "python", + # }, + # ], + # }, + # { + # "title": "Land Cover", + # "abstract": """Land Cover""", + # "layers": [ + # { + # "include": "ows_config.land_cover.ows_io_lulc_cfg.layer_9_class", + # "type": "python", + # }, + # ], + # } + ], + } + ], +} diff --git a/ows_config/radar_backscatter/__init__.py b/ows_config/radar_backscatter/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ows_config/radar_backscatter/ows_s1_cfg.py b/ows_config/radar_backscatter/ows_s1_cfg.py new file mode 100644 index 0000000..83d586a --- /dev/null +++ b/ows_config/radar_backscatter/ows_s1_cfg.py @@ -0,0 +1,89 @@ +# From https://raw.githubusercontent.com/digitalearthafrica/config/master/services/ows_refactored/radar_backscatter/ows_sentinel1_cfg.py +from ows_config.common.ows_reslim_cfg import reslim_landsat + +bands_s1 = {"vv": [], "vh": []} + + +style_s1_vv = { + "name": "vv", + "title": "VV", + "abstract": "VV band", + "additional_bands": [], + "components": { + "red": {"vv": 1.0, "scale_range": [0.0, 0.28]}, + "green": {"vv": 1.0, "scale_range": [0.0, 0.28]}, + "blue": {"vv": 1.0, "scale_range": [0.0, 0.28]}, + } +} + +style_s1_vh = { + "name": "vh", + "title": "VH", + "abstract": "VH band", + "additional_bands": [], + "components": { + "red": {"vh": 1.0, "scale_range": [0.0, 0.06]}, + "green": {"vh": 1.0, "scale_range": [0.0, 0.06]}, + "blue": {"vh": 1.0, "scale_range": [0.0, 0.06]}, + } +} + +style_s1_vh_over_vv = { + "name": "vv_vh_vh_over_vv", + "title": "VV, VH and VH/VV", + "abstract": "False colour representation of VV, VH and VH/VV for R, G and B respectively", + "additional_bands": [], + "components": { + "red": {"vv": 1.0, "scale_range": [0.0, 0.28]}, + "green": {"vh": 1.0, "scale_range": [0.0, 0.06]}, + "blue": { + "function": "datacube_ows.band_utils.band_quotient", + "mapped_bands": True, + "kwargs": {"band1": "vh", "band2": "vv", "scale_from": [0.0, 0.49]}, + }, + } +} + +style_s1_rvi = { + "name": "rvi", + "title": "Radar Vegetation Index", + "abstract": "Dual-pol radar vegetation index for Sentinel-1", + "index_expression": "4*vh/(vv + vh)", + "mpl_ramp": "YlGnBu_r", + "range": [0.0, 1.0], + "legend": { + "begin": "0.0", + "end": "1.0", + "decimal_places": 1, + "ticks": ["0.0", "0.2", "0.4", "0.6", "0.8", "1.0"], + } +} + + +layer = { + "title": "Sentinel-1 RTC", + "name": "sentinel_1_rtc", + "abstract": """ +todo...""", + "product_name": "sentinel_1_rtc", + "bands": bands_s1, + "dynamic": True, + "resource_limits": reslim_landsat, + "image_processing": { + "extent_mask_func": "datacube_ows.ogc_utils.mask_by_val", + "always_fetch_bands": [], + "manual_merge": False, + }, + "native_crs": "EPSG:4326", + "native_resolution": [0.0002, -0.0002], + "styling": { + "default_style": "vv_vh_vh_over_vv", + "styles": [ + style_s1_vh_over_vv, + style_s1_vv, + style_s1_vh, + style_s1_rvi, + ], + }, + "patch_url_function": "planetary_computer.sign_url", +} diff --git a/ows_config/surface_reflectance/__init__.py b/ows_config/surface_reflectance/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ows_config/surface_reflectance/band_sr_cfg.py b/ows_config/surface_reflectance/band_sr_cfg.py new file mode 100644 index 0000000..aa3aba2 --- /dev/null +++ b/ows_config/surface_reflectance/band_sr_cfg.py @@ -0,0 +1,51 @@ +bands_sentinel2 = { + "B01": ["coastal_aerosol"], + "B02": ["blue"], + "B03": ["green"], + "B04": ["red"], + "B05": ["red_edge_1"], + "B06": ["red_edge_2"], + "B07": ["red_edge_3"], + "B08": ["nir", "nir_1"], + "B8A": ["nir_narrow", "nir_2"], + "B09": ["water_vapour"], + "B11": ["swir_1", "swir_16"], + "B12": ["swir_2", "swir_22"], + "AOT": ["aerosol_optical_thickness"], + "WVP": ["scene_average_water_vapour"], + "SCL": ["mask", "qa"], +} +bands_ls5_7_sr = { + "SR_B1": ["blue"], + "SR_B2": ["green"], + "SR_B3": ["red"], + "SR_B4": ["nir"], + "SR_B5": ["swir_1"], + "SR_B7": ["swir_2"], + "QA_PIXEL": ["pq"], + "SR_ATMOS_OPACITY": ["atmospheric_opacity"], +} + +bands_ls8_9_sr = { + "SR_B2": ["blue"], + "SR_B3": ["green"], + "SR_B4": ["red"], + "SR_B5": ["nir"], + "SR_B6": ["swir_1"], + "SR_B7": ["swir_2"], + "qa_pixel": ["pq"], + "SR_QA_AEROSOL": ["aerosol_level"], +} + +bands_ls_geomad = { + "blue": ["band_2"], + "green": ["band_3"], + "red": ["band_4"], + "nir08": ["nir", "band_5"], + "swir16": ["swir1", "swir_1", "band_6"], + "swir22": ["swir2", "swir_2", "band_7"], + "smad": ["sdev"], + "emad": ["edev"], + "bcmad": ["bcdev"], + "count": [], +} diff --git a/ows_config/surface_reflectance/ows_landsat_cfg.py b/ows_config/surface_reflectance/ows_landsat_cfg.py new file mode 100644 index 0000000..3958ca1 --- /dev/null +++ b/ows_config/surface_reflectance/ows_landsat_cfg.py @@ -0,0 +1,133 @@ +from ows_config.common.ows_reslim_cfg import reslim_continental, reslim_landsat +from ows_config.surface_reflectance.band_sr_cfg import ( + bands_ls5_7_sr, + bands_ls8_9_sr, + bands_ls_geomad, +) +from ows_config.surface_reflectance.style_sr_cfg import ( + styles_landsat_5_7, + styles_landsat_8_9, + styles_ls_geomad, +) + +dep_ls_geomad = { + "title": "Annual GeoMAD (Landsat 8 & 9)", + "name": "dep_ls_geomad", + "abstract": """ +TODO +""", + "product_name": "dep_ls_geomad", + # "low_res_product_name": "gm_ls8_ls9_annual_lowres", # todo... + "bands": bands_ls_geomad, + "dynamic": False, + "resource_limits": reslim_continental, + "time_resolution": "summary", + "image_processing": { + "extent_mask_func": "ows_config.common.ows_util_tools.mask_by_emad_nan", + "always_fetch_bands": ["emad"], + "manual_merge": False, + "apply_solar_corrections": False, + }, + "native_crs": "EPSG:3832", + "native_resolution": [30.0, -30.0], + "styling": { + "default_style": "simple_rgb", + "styles": styles_ls_geomad, + } +} + +layer_ls5 = { + "title": "Landsat 5 C2L2", + "name": "ls5_c2l2_sr", + "abstract": """ +TODO""", + "product_name": "ls5_c2l2_sr", + "bands": bands_ls5_7_sr, + "dynamic": True, + "resource_limits": reslim_landsat, + "image_processing": { + "extent_mask_func": "datacube_ows.ogc_utils.mask_by_val", + "always_fetch_bands": [], + "manual_merge": False, # True + "apply_solar_corrections": False, + }, + "native_crs": "EPSG:3857", + "native_resolution": [30.0, -30.0], + "styling": { + "default_style": "simple_rgb", + "styles": styles_landsat_5_7, + }, + "patch_url_function": "planetary_computer.sign_url", +} + +layer_ls7 = { + "title": "Landsat 7 C2L2", + "name": "ls7_c2l2_sr", + "abstract": """ +TODO""", + "product_name": "ls7_c2l2_sr", + "bands": bands_ls5_7_sr, + "dynamic": True, + "resource_limits": reslim_landsat, + "image_processing": { + "extent_mask_func": "datacube_ows.ogc_utils.mask_by_val", + "always_fetch_bands": [], + "manual_merge": False, # True + "apply_solar_corrections": False, + }, + "native_crs": "EPSG:3857", + "native_resolution": [30.0, -30.0], + "styling": { + "default_style": "simple_rgb", + "styles": styles_landsat_5_7, + }, + "patch_url_function": "planetary_computer.sign_url", +} + +layer_ls8 = { + "title": "Landsat 8 C2L2", + "name": "ls8_c2l2_sr", + "abstract": """ +TODO""", + "product_name": "ls8_c2l2_sr", + "bands": bands_ls8_9_sr, + "dynamic": True, + "resource_limits": reslim_landsat, + "image_processing": { + "extent_mask_func": "datacube_ows.ogc_utils.mask_by_val", + "always_fetch_bands": [], + "manual_merge": False, # True + "apply_solar_corrections": False, + }, + "native_crs": "EPSG:3857", + "native_resolution": [30.0, -30.0], + "styling": { + "default_style": "simple_rgb", + "styles": styles_landsat_8_9, + }, + "patch_url_function": "planetary_computer.sign_url", +} + +layer_ls9 = { + "title": "Landsat 9 C2L2", + "name": "ls9_c2l2_sr", + "abstract": """ +TODO""", + "product_name": "ls9_c2l2_sr", + "bands": bands_ls8_9_sr, + "dynamic": True, + "resource_limits": reslim_landsat, + "image_processing": { + "extent_mask_func": "datacube_ows.ogc_utils.mask_by_val", + "always_fetch_bands": [], + "manual_merge": False, # True + "apply_solar_corrections": False, + }, + "native_crs": "EPSG:3857", + "native_resolution": [30.0, -30.0], + "styling": { + "default_style": "simple_rgb", + "styles": styles_landsat_8_9, + }, + "patch_url_function": "planetary_computer.sign_url", +} diff --git a/ows_config/surface_reflectance/ows_s2_cfg.py b/ows_config/surface_reflectance/ows_s2_cfg.py new file mode 100644 index 0000000..aabbf19 --- /dev/null +++ b/ows_config/surface_reflectance/ows_s2_cfg.py @@ -0,0 +1,28 @@ +from ows_config.common.ows_reslim_cfg import reslim_sentinel2 +from ows_config.surface_reflectance.band_sr_cfg import bands_sentinel2 +from ows_config.surface_reflectance.style_sr_cfg import styles_s2_list + +layer = { + "title": "Sentinel-2 L2A", + "name": "s2_l2a", + "abstract": """ +TODO +""", + "product_name": "s2_l2a", + "bands": bands_sentinel2, + "dynamic": True, + "resource_limits": reslim_sentinel2, + "image_processing": { + "extent_mask_func": "datacube_ows.ogc_utils.mask_by_val", + "always_fetch_bands": [], + "manual_merge": False, # True + "apply_solar_corrections": False, + }, + "native_crs": "EPSG:3857", + "native_resolution": [10.0, -10.0], + "styling": { + "default_style": "simple_rgb", + "styles": styles_s2_list, + }, + "patch_url_function": "planetary_computer.sign_url", +} diff --git a/ows_config/surface_reflectance/style_sr_cfg.py b/ows_config/surface_reflectance/style_sr_cfg.py new file mode 100644 index 0000000..f6f213e --- /dev/null +++ b/ows_config/surface_reflectance/style_sr_cfg.py @@ -0,0 +1,1231 @@ +from ows_config.common.ows_legend_cfg import legend_idx_0_1_5ticks + +style_gals_irg = { + "name": "infrared_green", + "title": "False colour - Green, SWIR, NIR", + "abstract": "False Colour image with SWIR1->Red, NIR->Green, and Green->Blue", + "components": { + "red": {"swir_1": 1.0}, + "green": {"nir": 1.0}, + "blue": {"green": 1.0}, + }, + "scale_range": [7272.0, 18181.0], +} + +style_s2_irg = { + "name": "infrared_green", + "title": "False colour - Green, SWIR, NIR", + "abstract": "False Colour image with SWIR1->Red, NIR->Green, and Green->Blue", + "components": { + "red": {"swir_1": 1.0}, + "green": {"nir": 1.0}, + "blue": {"green": 1.0}, + }, + "scale_range": [0, 3000], +} + +style_ls_irg = { + "name": "infrared_green", + "title": "False colour - Green, SWIR, NIR", + "abstract": "False Colour image with SWIR1->Red, NIR->Green, and Green->Blue", + "components": { + "red": {"swir1": 1.0}, + "green": {"nir": 1.0}, + "blue": {"green": 1.0}, + }, + "scale_range": [0.0, 3000.0], +} + +style_ls_ndvi = { + "name": "ndvi", + "title": "NDVI - Red, NIR", + "abstract": "Normalised Difference Vegetation Index - a derived index that correlates well with the existence of vegetation", + "index_function": { + "function": "datacube_ows.band_utils.norm_diff", + "mapped_bands": True, + "kwargs": {"band1": "nir", "band2": "red"}, + }, + "needed_bands": ["red", "nir"], + "color_ramp": [ + {"value": -0.0, "color": "#8F3F20", "alpha": 0.0}, + {"value": 0.0, "color": "#8F3F20", "alpha": 1.0}, + {"value": 0.1, "color": "#A35F18"}, + {"value": 0.2, "color": "#B88512"}, + {"value": 0.3, "color": "#CEAC0E"}, + {"value": 0.4, "color": "#E5D609"}, + {"value": 0.5, "color": "#FFFF0C"}, + {"value": 0.6, "color": "#C3DE09"}, + {"value": 0.7, "color": "#88B808"}, + {"value": 0.8, "color": "#529400"}, + {"value": 0.9, "color": "#237100"}, + {"value": 1.0, "color": "#114D04"}, + ], + "legend": legend_idx_0_1_5ticks, + "multi_date": [ + { + "allowed_count_range": [2, 2], + "animate": False, + "preserve_user_date_order": True, + "aggregator_function": { + "function": "datacube_ows.band_utils.multi_date_delta", + }, + "mpl_ramp": "RdYlBu", + "range": [-1.0, 1.0], + "legend": { + "begin": "-1.0", + "end": "1.0", + "ticks": [ + "-1.0", + "0.0", + "1.0", + ] + }, + "feature_info_label": "ndvi_delta", + }, + {"allowed_count_range": [3, 4], "animate": True}, + ], +} + +style_ls_ndwi = { + "name": "ndwi", + "title": "NDWI - Green, NIR", + "abstract": "Normalised Difference Water Index - a derived index that correlates well with the existence of water (McFeeters 1996)", + "index_function": { + "function": "datacube_ows.band_utils.norm_diff", + "mapped_bands": True, + "kwargs": {"band1": "green", "band2": "nir"}, + }, + "needed_bands": ["green", "nir"], + "color_ramp": [ + {"value": -0.1, "color": "#f7fbff", "alpha": 0.0}, + {"value": 0.0, "color": "#d8e7f5"}, + {"value": 0.1, "color": "#b0d2e8"}, + {"value": 0.2, "color": "#73b3d8"}, + {"value": 0.3, "color": "#3e8ec4"}, + {"value": 0.4, "color": "#1563aa"}, + {"value": 0.5, "color": "#08306b"}, + ], + "legend": { + "begin": "0.0", + "end": "0.5", + "decimal_places": 1, + "ticks": ["0.0", "0.2", "0.4", "0.5"], + "tick_labels": { + "0.0": {"prefix": "<"}, + "0.2": {"label": "0.2"}, + "0.4": {"label": "0.4"}, + "0.5": {"prefix": ">"}, + }, + }, + "multi_date": [ + { + "allowed_count_range": [2, 2], + "animate": False, + "preserve_user_date_order": True, + "aggregator_function": { + "function": "datacube_ows.band_utils.multi_date_delta", + }, + "mpl_ramp": "RdYlBu", + "range": [-1.0, 1.0], + "legend": { + "begin": "-1.0", + "end": "1.0", + "ticks": [ + "-1.0", + "0.0", + "1.0", + ] + }, + "feature_info_label": "ndwi_delta", + }, + {"allowed_count_range": [3, 4], "animate": True}, + ], +} + +style_gals_mndwi = { + "name": "mndwi", + "title": "MNDWI - Green, SWIR", + "abstract": "Modified Normalised Difference Water Index - a derived index that correlates well with the existence of water (Xu 2006)", + "index_function": { + "function": "datacube_ows.band_utils.norm_diff", + "mapped_bands": True, + "kwargs": {"band1": "green", "band2": "swir_1"}, + }, + "needed_bands": ["green", "swir_1"], + "color_ramp": [ + {"value": -0.1, "color": "#f7fbff", "alpha": 0.0}, + {"value": 0.0, "color": "#d8e7f5"}, + {"value": 0.2, "color": "#b0d2e8"}, + {"value": 0.4, "color": "#73b3d8"}, + {"value": 0.6, "color": "#3e8ec4"}, + {"value": 0.8, "color": "#1563aa"}, + {"value": 1.0, "color": "#08306b"}, + ], + "legend": legend_idx_0_1_5ticks, +} + +style_ls_mndwi = { + "name": "mndwi", + "title": "MNDWI - Green, SWIR", + "abstract": "Modified Normalised Difference Water Index - a derived index that correlates " + "well with the existence of water (Xu 2006)", + "index_function": { + "function": "datacube_ows.band_utils.norm_diff", + "mapped_bands": True, + "kwargs": {"band1": "green", "band2": "swir1"}, + }, + "needed_bands": ["green", "swir1"], + "color_ramp": [ + {"value": -0.1, "color": "#f7fbff", "alpha": 0.0}, + {"value": 0.0, "color": "#d8e7f5"}, + {"value": 0.2, "color": "#b0d2e8"}, + {"value": 0.4, "color": "#73b3d8"}, + {"value": 0.6, "color": "#3e8ec4"}, + {"value": 0.8, "color": "#1563aa"}, + {"value": 1.0, "color": "#08306b"}, + ], + "legend": legend_idx_0_1_5ticks, +} + +style_gals_pure_blue = { + "name": "blue", + "title": "Blue - 480", + "abstract": "Blue band, centered on 480nm", + "components": {"red": {"blue": 1.0}, "green": {"blue": 1.0}, "blue": {"blue": 1.0}}, + "scale_range": [7272.0, 18181.0], +} + +style_ls_pure_blue = { + "name": "blue", + "title": "Blue - 480", + "abstract": "Blue band, centered on 480nm", + "components": {"red": {"blue": 1.0}, "green": {"blue": 1.0}, "blue": {"blue": 1.0}}, + "scale_range": [0.0, 3000.0], +} + +style_sentinel_pure_blue = { + "name": "blue", + "title": "Blue - 490", + "abstract": "Blue band, centered on 490nm", + "components": {"red": {"blue": 1.0}, "green": {"blue": 1.0}, "blue": {"blue": 1.0}}, + "scale_range": [0.0, 3000.0], +} + +style_gals_pure_green = { + "name": "green", + "title": "Green - 560", + "abstract": "Green band, centered on 560nm", + "components": { + "red": {"green": 1.0}, + "green": {"green": 1.0}, + "blue": {"green": 1.0}, + }, + "scale_range": [7272.0, 18181.0], +} + +style_ls_pure_green = { + "name": "green", + "title": "Green - 560", + "abstract": "Green band, centered on 560nm", + "components": { + "red": {"green": 1.0}, + "green": {"green": 1.0}, + "blue": {"green": 1.0}, + }, + "scale_range": [0.0, 3000.0], +} + +style_ls_simple_rgb = { + "name": "simple_rgb", + "title": "Simple RGB", + "abstract": "Simple true-colour image, using the red, green and blue bands", + "components": {"red": {"red": 1.0}, "green": {"green": 1.0}, "blue": {"blue": 1.0}}, + "scale_range": [0.0, 3000.0], +} + +style_gm_simple_rgb = { + "name": "simple_rgb", + "title": "Geomedian - Red, Green, Blue", + "abstract": "Simple true-colour image, using the red, green and blue bands", + "components": {"red": {"red": 1.0}, "green": {"green": 1.0}, "blue": {"blue": 1.0}}, + "scale_range": [0.0, 3000.0], + "multi_date": [{"allowed_count_range": [2, 4], "animate": True}], +} + +style_gals_simple_rgb = { + "name": "simple_rgb", + "title": "Simple RGB", + "abstract": "Simple true-colour image, using the red, green and blue bands", + "components": {"red": {"red": 1.0}, "green": {"green": 1.0}, "blue": {"blue": 1.0}}, + # The raw band value range to be compressed to an 8 bit range for the output image tiles. + # Band values outside this range are clipped to 0 or 255 as appropriate. + "scale_range": [7272.0, 18181.0], +} + + +style_gals_pure_red = { + "name": "red", + "title": "Red - 660", + "abstract": "Red band, centered on 660nm", + "components": {"red": {"red": 1.0}, "green": {"red": 1.0}, "blue": {"red": 1.0}}, + "scale_range": [7272.0, 18181.0], +} + +style_ls_pure_red = { + "name": "red", + "title": "Red - 660", + "abstract": "Red band, centered on 660nm", + "components": {"red": {"red": 1.0}, "green": {"red": 1.0}, "blue": {"red": 1.0}}, + "scale_range": [0.0, 3000.0], +} + +style_gals_pure_nir = { + "name": "nir", + "title": "Near Infrared (NIR) - 840", + "abstract": "Near infra-red band, centered on 840nm", + "components": {"red": {"nir": 1.0}, "green": {"nir": 1.0}, "blue": {"nir": 1.0}}, + "scale_range": [7272.0, 18181.0], +} + +style_ls_pure_nir = { + "name": "nir", + "title": "Near Infrared (NIR) - 840", + "abstract": "Near infra-red band, centered on 840nm", + "components": {"red": {"nir": 1.0}, "green": {"nir": 1.0}, "blue": {"nir": 1.0}}, + "scale_range": [0.0, 3000.0], +} + +style_sentinel_pure_nir = { + "name": "nir", + "title": "Near Infrared (NIR) - 870", + "abstract": "Near infra-red band, centered on 870nm", + "components": {"red": {"nir": 1.0}, "green": {"nir": 1.0}, "blue": {"nir": 1.0}}, + "scale_range": [0.0, 3000.0], +} + +style_gals_pure_swir1 = { + "name": "swir_1", + "title": "Shortwave Infrared (SWIR) - 1610", + "abstract": "Short wave infra-red band 1, centered on 1610nm", + "components": { + "red": {"swir_1": 1.0}, + "green": {"swir_1": 1.0}, + "blue": {"swir_1": 1.0}, + }, + "scale_range": [7272.0, 18181.0], +} + + +style_s2_pure_swir1 = { + "name": "swir_1", + "title": "Shortwave Infrared (SWIR) - 1610", + "abstract": "Short wave infra-red band 1, centered on 1610nm", + "components": {"red": {"B11": 1.0}, "green": {"B11": 1.0}, "blue": {"B11": 1.0}}, + "scale_range": [0.0, 3000.0], +} + +style_ls_pure_swir1 = { + "name": "swir1", + "title": "Shortwave Infrared (SWIR) - 1650", + "abstract": "Short wave infra-red band 1, centered on 1650nm", + "components": { + "red": {"swir1": 1.0}, + "green": {"swir1": 1.0}, + "blue": {"swir1": 1.0}, + }, + "scale_range": [0.0, 3000.0], +} + +style_sentinel_pure_swir1 = { + "name": "swir1", + "title": "Shortwave Infrared (SWIR) - 1610", + "abstract": "Short wave infra-red band 1, centered on 1610nm", + "components": { + "red": {"swir1": 1.0}, + "green": {"swir1": 1.0}, + "blue": {"swir1": 1.0}, + }, + "scale_range": [0.0, 3000.0], +} + +style_gals_pure_swir2 = { + "name": "swir_2", + "title": "Shortwave Infrared (SWIR) - 2200", + "abstract": "Short wave infra-red band 2, centered on 2200nm", + "components": { + "red": {"swir_2": 1.0}, + "green": {"swir_2": 1.0}, + "blue": {"swir_2": 1.0}, + }, + "scale_range": [7272.0, 18181.0], +} + +style_s2_pure_swir2 = { + "name": "swir_2", + "title": "Shortwave Infrared (SWIR) - 2200", + "abstract": "Short wave infra-red band 2, centered on 2200nm", + "components": { + "red": {"swir_2": 1.0}, + "green": {"swir_2": 1.0}, + "blue": {"swir_2": 1.0}, + }, + "scale_range": [0, 3000.0], +} + +style_ls_pure_swir2 = { + "name": "swir2", + "title": "Shortwave Infrared (SWIR) - 2220", + "abstract": "Short wave infra-red band 2, centered on 2220nm", + "components": { + "red": {"swir2": 1.0}, + "green": {"swir2": 1.0}, + "blue": {"swir2": 1.0}, + }, + "scale_range": [0.0, 3000.0], +} + +style_s2_ndci = { + "name": "ndci", + "title": "NDCI - Red Edge, Red", + "abstract": "Normalised Difference Chlorophyll Index - a derived index that correlates well with the existence of chlorophyll", + "index_function": { + "function": "datacube_ows.band_utils.sentinel2_ndci", + "mapped_bands": True, + "kwargs": { + "b_red_edge": "red_edge_1", + "b_red": "red", + "b_green": "green", + "b_swir": "swir_2", + }, + }, + "needed_bands": ["red_edge_1", "red", "green", "swir_2"], + "color_ramp": [ + {"value": -0.1, "color": "#1696FF"}, + {"value": -0.1, "color": "#1696FF"}, + {"value": 0.0, "color": "#00FFDF"}, + { + "value": 0.1, + "color": "#FFF50E", + }, + {"value": 0.2, "color": "#FFB50A"}, + { + "value": 0.4, + "color": "#FF530D", + }, + {"value": 0.5, "color": "#FF0000"}, + ], + "legend": { + "begin": "-0.1", + "end": "0.5", + "decimal_places": 1, + "ticks": ["-0.1", "0.0", "0.2", "0.5"], + "tick_labels": { + "-0.1": {"prefix": "<"}, + "0.5": {"prefix": ">"}, + }, + }, +} + +style_s2_pure_aerosol = { + "name": "aerosol", + "title": "Narrow Blue - 440", + "abstract": "Coastal Aerosol or Narrow Blue band, approximately 435nm to 450nm", + "components": { + "red": {"coastal_aerosol": 1.0}, + "green": {"coastal_aerosol": 1.0}, + "blue": {"coastal_aerosol": 1.0}, + }, + "scale_range": [0.0, 3000.0], +} + + +style_s2_pure_redge_1 = { + "name": "red_edge_1", + "title": "Vegetation Red Edge - 710", + "abstract": "Near infra-red band, centred on 710nm", + "components": { + "red": {"red_edge_1": 1.0}, + "green": {"red_edge_1": 1.0}, + "blue": {"red_edge_1": 1.0}, + }, + "scale_range": [0.0, 3000.0], +} + + +style_s2_pure_redge_2 = { + "name": "red_edge_2", + "title": "Vegetation Red Edge - 740", + "abstract": "Near infra-red band, centred on 740nm", + "components": { + "red": {"red_edge_2": 1.0}, + "green": {"red_edge_2": 1.0}, + "blue": {"red_edge_2": 1.0}, + }, + "scale_range": [0.0, 3000.0], +} + + +style_s2_pure_redge_3 = { + "name": "red_edge_3", + "title": "Vegetation Red Edge - 780", + "abstract": "Near infra-red band, centred on 780nm", + "components": { + "red": {"red_edge_3": 1.0}, + "green": {"red_edge_3": 1.0}, + "blue": {"red_edge_3": 1.0}, + }, + "scale_range": [0.0, 3000.0], +} + +style_s2_pure_narrow_nir = { + "name": "narrow_nir", + "title": "Narrow Near Infrared - 870", + "abstract": "Near infra-red band, centred on 865nm", + "components": {"red": {"nir": 1.0}, "green": {"nir": 1.0}, "blue": {"nir": 1.0}}, + "scale_range": [0.0, 3000.0], +} + +style_sentinel_pure_swir2 = { + "name": "swir2", + "title": "Shortwave Infrared (SWIR) - 2200", + "abstract": "Short wave infra-red band 2, centered on 2200nm", + "components": { + "red": {"swir2": 1.0}, + "green": {"swir2": 1.0}, + "blue": {"swir2": 1.0}, + }, + "scale_range": [0.0, 3000.0], +} + +style_s2_scl = { + "name": "scl", + "title": "Scene Classification", + "abstract": "Sentinel-2 Scene Classification Layer", + "value_map": { + "SCL": [ + { + "title": "", + "abstract": "", + "values": [0], + "alpha": 0.0, + "color": "#707070", + }, + { + "title": "Saturated or Defective", + "abstract": "", + "values": [1], + "color": "#ff0004", + }, + { + "title": "Dark Areas", + "abstract": "", + "values": [2], + "color": "#C4281B", + }, + { + "title": "Cloud Shadow", + "abstract": "", + "values": [3], + "color": "#A59B8F", + }, + { + "title": "Vegetation", + "abstract": "", + "values": [4], + "color": "#88B053", + }, + { + "title": "Bare Soil", + "abstract": "", + "values": [5], + "color": "#DFC35A", + }, + { + "title": "Water", + "abstract": "", + "values": [6], + "color": "#419BDF", + }, + { + "title": "Unclassified", + "abstract": "", + "values": [7], + "color": "#75001b", + }, + { + "title": "Medium Probability Cloud", + "abstract": "", + "values": [8], + "color": "#d0d0d0", + }, + { + "title": "High Probability Cloud", + "abstract": "", + "values": [9], + "color": "#616161", + }, + { + "title": "Cirrus", + "abstract": "", + "values": [10], + "color": "#c3e7f0", + }, + { + "title": "Snow or Ice", + "abstract": "", + "values": [11], + "color": "#A8EBFF", + }, + ] + }, + "legend": {"width": 3.0, "height": 3.0}, +} + + +style_sentinel_count = { + "name": "count", + "title": "Included observation count", + "abstract": "Count of observations included in geomedian/MAD calculations", + "index_function": { + "function": "datacube_ows.band_utils.single_band", + "mapped_bands": True, + "kwargs": { + "band": "count", + }, + }, + "needed_bands": ["count"], + "include_in_feature_info": False, + "color_ramp": [ + {"value": 0, "color": "#666666", "alpha": 0}, + { + # purely for legend display + # we should not get fractional + # values in this styles + "value": 0.2, + "color": "#890000", + "alpha": 1, + }, + {"value": 20, "color": "#990000"}, + {"value": 30, "color": "#E38400"}, + {"value": 40, "color": "#E3DF00"}, + {"value": 50, "color": "#A6E300"}, + {"value": 60, "color": "#00E32D"}, + {"value": 70, "color": "#00E3C8"}, + {"value": 80, "color": "#0097E3"}, + {"value": 90, "color": "#005FE3"}, + {"value": 100, "color": "#000FE3"}, + {"value": 110, "color": "#000EA9"}, + {"value": 120, "color": "#5700E3"}, + ], + "legend": { + "begin": "0", + "end": "120", + "decimal_places": 0, + "ticks_every": 20, + "tick_labels": { + "120": {"prefix": ">"}, + }, + }, +} + +style_gm_count = { + "name": "count", + "title": "Clear observation count", + "abstract": "Count of observations included in geomedian/MAD calculations", + "index_function": { + "function": "datacube_ows.band_utils.single_band", + "mapped_bands": True, + "kwargs": { + "band": "count", + }, + }, + "needed_bands": ["count"], + "include_in_feature_info": False, + "color_ramp": [ + {"value": 0, "color": "#666666", "alpha": 0}, + { + # purely for legend display + # we should not get fractional + # values in this styles + "value": 0.2, + "color": "#890000", + "alpha": 1, + }, + {"value": 20, "color": "#990000"}, + {"value": 30, "color": "#E38400"}, + {"value": 40, "color": "#E3DF00"}, + {"value": 50, "color": "#A6E300"}, + {"value": 60, "color": "#00E32D"}, + {"value": 70, "color": "#00E3C8"}, + {"value": 80, "color": "#0097E3"}, + {"value": 90, "color": "#005FE3"}, + {"value": 100, "color": "#000FE3"}, + {"value": 110, "color": "#000EA9"}, + {"value": 120, "color": "#5700E3"}, + ], + "legend": { + "begin": "0", + "end": "120", + "decimal_places": 0, + "ticks_every": 20, + "tick_labels": { + "120": {"prefix": ">"}, + }, + }, +} + +styles_ls8c_list = [ + style_gals_simple_rgb, + style_gals_irg, + style_gals_pure_blue, + style_gals_pure_green, + style_gals_pure_red, + style_gals_pure_nir, + style_gals_pure_swir1, + style_gals_pure_swir2, +] + +styles_s2_list = [ + style_ls_simple_rgb, + style_s2_irg, + style_ls_ndvi, + # style_ls_ndwi, + style_gals_mndwi, + style_s2_ndci, + style_s2_pure_aerosol, + style_sentinel_pure_blue, + style_ls_pure_green, + style_ls_pure_red, + style_s2_pure_redge_1, + style_s2_pure_redge_2, + style_s2_pure_redge_3, + style_ls_pure_nir, + style_s2_pure_narrow_nir, + style_s2_pure_swir1, + style_s2_pure_swir2, + style_s2_scl, +] + +styles_sr_list = [ + style_ls_simple_rgb, + style_ls_irg, + style_ls_ndvi, + style_ls_ndwi, + style_ls_mndwi, + style_ls_pure_blue, + style_ls_pure_green, + style_ls_pure_red, + style_sentinel_pure_nir, + style_sentinel_pure_swir1, + style_sentinel_pure_swir2, +] + +# styles for Landsat C2 +# Band wavelengths have been removed as they differ +# between LS5, 7 and 8 +# Scale range is set as per Collection 1, but scaled to +# 65455 instead of 10000 + +style_lsc2_sr_simple_rgb = { + "name": "simple_rgb", + "title": "True colour - RGB", + "abstract": "True-colour image, using the red, green and blue bands", + "components": { + "red": {"red": 1.0}, + "green": {"green": 1.0}, + "blue": {"blue": 1.0} + }, + "scale_range": [7272.0, 18181.0], +} + +style_lsc2_sr_irg = { + "name": "infrared_green", + "title": "False colour - SWIR, NIR, Green", + "abstract": "False colour image with SWIR1->Red, NIR->Green, and Green->Blue", + "components": { + "red": {"swir_1": 1.0}, + "green": {"nir": 1.0}, + "blue": {"green": 1.0}, + }, + "scale_range": [7272.0, 18181.0], +} + +style_lsc2_sr_ndvi = { + "name": "ndvi", + "title": "NDVI - Red, NIR", + "abstract": "Normalised Difference Vegetation Index - a derived index that correlates well with the existence of vegetation", + "index_expression": "(nir*1.0-red)/(nir+red-14545.4545)", + "color_ramp": [ + {"value": -0.0, "color": "#8F3F20", "alpha": 0.0}, + {"value": 0.0, "color": "#8F3F20", "alpha": 1.0}, + {"value": 0.1, "color": "#A35F18"}, + {"value": 0.2, "color": "#B88512"}, + {"value": 0.3, "color": "#CEAC0E"}, + {"value": 0.4, "color": "#E5D609"}, + {"value": 0.5, "color": "#FFFF0C"}, + {"value": 0.6, "color": "#C3DE09"}, + {"value": 0.7, "color": "#88B808"}, + {"value": 0.8, "color": "#529400"}, + {"value": 0.9, "color": "#237100"}, + {"value": 1.0, "color": "#114D04"}, + ], + "legend": legend_idx_0_1_5ticks, +} + +style_lsc2_sr_ndwi = { + "name": "ndwi", + "title": "NDWI - Green, NIR", + "abstract": "Normalised Difference Water Index - a derived index that correlates well with the existence of water (McFeeters 1996)", + "index_expression": "(green*1.0-nir)/(green+nir-14545.4545)", + "color_ramp": [ + {"value": -0.1, "color": "#f7fbff", "alpha": 0.0}, + {"value": 0.0, "color": "#d8e7f5", "legend": {"prefix": "<"}}, + {"value": 0.1, "color": "#b0d2e8"}, + {"value": 0.2, "color": "#73b3d8", "legend": {}}, + {"value": 0.3, "color": "#3e8ec4"}, + {"value": 0.4, "color": "#1563aa", "legend": {}}, + {"value": 0.5, "color": "#08306b", "legend": {"prefix": ">"}}, + ], + "legend": { + "begin": "0.0", + "end": "0.5", + "decimal_places": 1, + "ticks": ["0.0", "0.2", "0.4", "0.5"], + "tick_labels": { + "0.0": {"prefix": "<"}, + "0.2": {"label": "0.2"}, + "0.4": {"label": "0.4"}, + "0.5": {"prefix": ">"}, + }, + }, +} + +style_lsc2_sr_mndwi = { + "name": "mndwi", + "title": "MNDWI - Green, SWIR", + "abstract": "Modified Normalised Difference Water Index - a derived index that correlates " + "well with the existence of water (Xu 2006)", + "index_expression": "(green*1.0-swir_1)/(green+swir_1-14545.4545)", + "color_ramp": [ + {"value": -0.1, "color": "#f7fbff", "alpha": 0.0}, + {"value": 0.0, "color": "#d8e7f5"}, + {"value": 0.2, "color": "#b0d2e8"}, + {"value": 0.4, "color": "#73b3d8"}, + {"value": 0.6, "color": "#3e8ec4"}, + {"value": 0.8, "color": "#1563aa"}, + {"value": 1.0, "color": "#08306b"}, + ], + "legend": legend_idx_0_1_5ticks, +} + +style_lsc2_sr_pure_blue = { + "name": "blue", + "title": "Blue", + "abstract": "Blue band", + "components": {"red": {"blue": 1.0}, "green": {"blue": 1.0}, "blue": {"blue": 1.0}}, + "scale_range": [7272.0, 18181.0], +} + +style_lsc2_sr_pure_green = { + "name": "green", + "title": "Green", + "abstract": "Green band", + "components": { + "red": {"green": 1.0}, + "green": {"green": 1.0}, + "blue": {"green": 1.0}, + }, + "scale_range": [7272.0, 18181.0], +} + +style_lsc2_sr_pure_red = { + "name": "red", + "title": "Red", + "abstract": "Red band", + "components": { + "red": {"red": 1.0}, + "green": {"red": 1.0}, + "blue": {"red": 1.0}, + }, + "scale_range": [7272.0, 18181.0], +} + +style_lsc2_sr_pure_nir = { + "name": "nir", + "title": "Near Infrared (NIR)", + "abstract": "Near infra-red band", + "components": { + "red": {"nir": 1.0}, + "green": {"nir": 1.0}, + "blue": {"nir": 1.0} + }, + "scale_range": [7272.0, 18181.0], +} + +style_lsc2_sr_swir_1 = { + "name": "swir_1", + "title": "Shortwave Infrared 1 (SWIR1)", + "abstract": "Shortwave infrared band 1", + "components": { + "red": {"swir_1": 1.0}, + "green": {"swir_1": 1.0}, + "blue": {"swir_1": 1.0}, + }, + "scale_range": [7272.0, 18181.0], +} + +style_lsc2_sr_swir_2 = { + "name": "swir_2", + "title": "Shortwave Infrared 2 (SWIR2)", + "abstract": "Shortwave infrared band 2", + "components": { + "red": {"swir_2": 1.0}, + "green": {"swir_2": 1.0}, + "blue": {"swir_2": 1.0}, + }, + "scale_range": [7272.0, 18181.0], +} + +style_lsc2_pq = { + "name": "pixel_quality", + "title": "Pixel Quality", + "abstract": "Pixel Quality", + "value_map": { + "pq": [ + { + "title": "", + "abstract": "", + "flags": {"nodata": True}, + "alpha": 0.0, + "color": "#707070", + }, + { + "title": "Water", + "abstract": "", + "flags": {"water": "water"}, + "color": "#4F81BD", + }, + { + "title": "Cloud", + "abstract": "", + "flags": {"cloud": "high_confidence"}, + "color": "#c2c1c0", + }, + { + "title": "Dilated cloud", + "abstract": "", + "flags": {"dilated_cloud": "dilated"}, + "color": "#707070", + }, + { + "title": "Cloud Shadow", + "abstract": "", + "flags": {"cloud_shadow": "high_confidence"}, + "color": "#4b4b37", + }, + { + "title": "Snow", + "abstract": "", + "flags": {"snow": "high_confidence"}, + "color": "Beige", + }, + { + "title": "Land", + "abstract": "", + "flags": {"water": "land_or_cloud"}, + "color": "#96966e", + }, + ] + } +} + +style_ls8c2_pq = { + "name": "pixel_quality", + "title": "Pixel Quality", + "abstract": "Pixel Quality", + "value_map": { + "pq": [ + { + "title": "", + "abstract": "", + "flags": {"nodata": True}, + "alpha": 0.0, + "color": "#707070", + }, + { + "title": "Water", + "abstract": "", + "flags": {"water": "water"}, + "color": "#4F81BD", + }, + { + "title": "Cloud", + "abstract": "", + "flags": {"cloud": "high_confidence"}, + "color": "#c2c1c0", + }, + { + "title": "Dilated cloud", + "abstract": "", + "flags": {"dilated_cloud": "dilated"}, + "color": "#707070", + }, + { + "title": "Cirrus", + "abstract": "", + "flags": {"cirrus": "high_confidence"}, + "color": "#708090", + }, + { + "title": "Cloud Shadow", + "abstract": "", + "flags": {"cloud_shadow": "high_confidence"}, + "color": "#4b4b37", + }, + { + "title": "Snow", + "abstract": "", + "flags": {"snow": "high_confidence"}, + "color": "Beige", + }, + { + "title": "Land", + "abstract": "", + "flags": {"water": "land_or_cloud"}, + "color": "#96966e", + }, + ] + } +} + +styles_landsat_5_7 = [ + style_lsc2_sr_simple_rgb, + style_lsc2_sr_irg, + style_lsc2_sr_ndvi, + style_lsc2_sr_mndwi, + style_lsc2_sr_pure_blue, + style_lsc2_sr_pure_green, + style_lsc2_sr_pure_red, + style_lsc2_sr_pure_nir, + style_lsc2_sr_swir_1, + style_lsc2_sr_swir_2, + style_lsc2_pq, +] + + +styles_landsat_8_9 = [ + style_lsc2_sr_simple_rgb, + style_lsc2_sr_irg, + style_lsc2_sr_ndvi, + style_lsc2_sr_mndwi, + style_lsc2_sr_pure_blue, + style_lsc2_sr_pure_green, + style_lsc2_sr_pure_red, + style_lsc2_sr_pure_nir, + style_lsc2_sr_swir_1, + style_lsc2_sr_swir_2, + style_ls8c2_pq, +] + +# styles tmad +sdev_scaling = [0.020, 0.18] +edev_scaling = [6.2, 7.3] +bcdev_scaling = [0.025, 0.13] + +style_tmad_sdev_std = { + "name": "arcsec_sdev", + "title": "Spectral MAD (SMAD)", + "abstract": "Good for cropland and forest", + "index_function": { + "function": "datacube_ows.band_utils.single_band_arcsec", + "mapped_bands": True, + "kwargs": {"band": "sdev", "scale_from": sdev_scaling, "scale_to": [0.0, 4.0]}, + }, + "needed_bands": ["sdev"], + "mpl_ramp": "coolwarm", + "range": [0.0, 4.0], + "legend": { + "start": "0.0", + "end": "4.0", + "ticks": ["0.0", "4.0"], + "tick_labels": { + "0.0": {"label": "Low\nSMAD"}, + "4.0": {"label": "High\nSMAD"}, + }, + }, +} + +style_tmad_edev_std = { + "name": "log_edev", + "title": "Euclidean MAD (EMAD)", + "abstract": "Good for cropland and forest", + "index_function": { + "function": "datacube_ows.band_utils.single_band_offset_log", + "mapped_bands": True, + "kwargs": {"band": "edev", "scale_from": edev_scaling, "scale_to": [0.0, 4.0]}, + }, + "needed_bands": ["edev"], + "mpl_ramp": "coolwarm", + "range": [0.0, 4.0], + "legend": { + "start": "0.0", + "end": "4.0", + "ticks": ["0.0", "4.0"], + "tick_labels": { + "0.0": {"label": "Low\nEMAD"}, + "4.0": {"label": "High\nEMAD"}, + }, + }, +} + + +style_tmad_bcdev_std = { + "name": "log_bcdev", + "title": "Bray-Curtis MAD (BCMAD)", + "abstract": "Good for cropland and forest", + "index_function": { + "function": "datacube_ows.band_utils.single_band_offset_log", + "mapped_bands": True, + "kwargs": { + "band": "bcdev", + "scale_from": bcdev_scaling, + "scale_to": [0.0, 4.0], + }, + }, + "needed_bands": ["bcdev"], + "mpl_ramp": "coolwarm", + "range": [0.0, 4.0], + "legend": { + "start": "0.0", + "end": "4.0", + "ticks": ["0.0", "4.0"], + "tick_labels": { + "0.0": {"label": "Low\nBCMAD"}, + "4.0": {"label": "High\nBCMAD"}, + }, + }, +} + +style_tmad_rgb_std = { + "name": "tmad_rgb_std", + "title": "MADs - SMAD, EMAD, BCMAD", + "abstract": "Good for cropland and forest", + "components": { + "red": { + "function": "datacube_ows.band_utils.single_band_arcsec", + "mapped_bands": True, + "kwargs": { + "band": "sdev", + "scale_from": sdev_scaling, + }, + }, + "green": { + "function": "datacube_ows.band_utils.single_band_offset_log", + "mapped_bands": True, + "kwargs": { + "band": "edev", + "scale_from": edev_scaling, + }, + }, + "blue": { + "function": "datacube_ows.band_utils.single_band_offset_log", + "mapped_bands": True, + "kwargs": { + "band": "bcdev", + "scale_from": bcdev_scaling, + }, + }, + }, + "additional_bands": ["sdev", "bcdev", "edev"], +} + +style_tmad_rgb_sens = { + "inherits": style_tmad_rgb_std, + "name": "tmad_rgb_sens", + "title": "MADs (desert) - SMAD, EMAD, BCMAD", + "abstract": "Good for arid land and desert", + "components": { + "red": { + "kwargs": { + "scale_from": [0.0005, 0.11], + } + }, + "green": { + "kwargs": { + "scale_from": [5.9, 6.9], + } + }, + "blue": { + "kwargs": { + "scale_from": [0.008, 0.07], + } + }, + }, +} + + +style_gm_ls_count = { + "name": "count", + "title": "Clear observation count", + "abstract": "Count of observations included in Geomedian/MAD calculations", + "index_function": { + "function": "datacube_ows.band_utils.single_band", + "mapped_bands": True, + "kwargs": { + "band": "count", + }, + }, + "needed_bands": ["count"], + "include_in_feature_info": False, + "color_ramp": [ + # set transparency for no data (0 counts) + {"value": 0, "color": "#666666", "alpha": 0}, + { + # purely for legend display + # simulates transparency at 0 + # we should not get fractional + # values in this styles + "value": 0.2, + "color": "#FFFFFF", + "alpha": 1, + }, + {"value": 5, "color": "#edf8b1"}, + {"value": 10, "color": "#c6e9b4"}, + {"value": 15, "color": "#7ecdbb"}, + {"value": 20, "color": "#40b5c4"}, + {"value": 25, "color": "#1d90c0"}, + {"value": 30, "color": "#225da8"}, + {"value": 35, "color": "#243392"}, + {"value": 40, "color": "#081d58"}, + ], + "legend": { + "begin": "0", + "end": "40", + "decimal_places": 0, + "ticks_every": 5, + "tick_labels": { + "40": {"suffix": "<"}, + }, + }, +} + + +styles_ls_geomad = [ + style_lsc2_sr_simple_rgb, + style_lsc2_sr_irg, + style_tmad_rgb_std, + style_tmad_rgb_sens, + style_ls_ndvi, + style_ls_ndwi, + style_gals_mndwi, + style_ls_pure_blue, + style_ls_pure_green, + style_ls_pure_red, + style_ls_pure_nir, + style_ls_pure_swir1, + style_ls_pure_swir2, + style_tmad_sdev_std, + style_tmad_edev_std, + style_tmad_bcdev_std, + style_gm_ls_count, +] diff --git a/products.csv b/products.csv index 41d3b98..cd7f91b 100644 --- a/products.csv +++ b/products.csv @@ -4,3 +4,4 @@ s2_l2a,https://raw.githubusercontent.com/opendatacube/datacube-dataset-config/ma nasadem,https://raw.githubusercontent.com/opendatacube/datacube-dataset-config/main/products/nasadem.odc-product.yaml sentinel_1_rtc,https://raw.githubusercontent.com/opendatacube/datacube-dataset-config/main/products/s1_rtc.odc-product.yaml ls5_c2l2_sr;ls7_c2l2_sr;ls8_c2l2_sr;ls9_c2l2_sr,https://raw.githubusercontent.com/opendatacube/datacube-dataset-config/main/products/lsX_c2l2_sr.odc-product.yaml +dep_ls_geomad,products/dep_ls_geomad.odc-product.yaml diff --git a/products/dep_ls_geomad.odc-product.yaml b/products/dep_ls_geomad.odc-product.yaml new file mode 100644 index 0000000..40fa10f --- /dev/null +++ b/products/dep_ls_geomad.odc-product.yaml @@ -0,0 +1,78 @@ +--- +name: dep_ls_geomad +description: Landsat Annual Geometric Median and Median + Absolute Deviations +metadata_type: eo3 + +license: CC-BY-4.0 + +metadata: + product: + name: dep_ls_geomad + +storage: + crs: EPSG:3832 + resolution: + x: 30 + y: -30 + +measurements: + - name: blue + dtype: "uint16" + units: "1" + nodata: 0 + aliases: [band_2] + + - name: green + dtype: "uint16" + units: "1" + nodata: 0 + aliases: [band_3] + + - name: red + dtype: "uint16" + units: "1" + nodata: 0 + aliases: [band_4] + + - name: nir08 + dtype: "uint16" + units: "1" + nodata: 0 + aliases: [band_5, nir] + + - name: swir16 + dtype: "uint16" + units: "1" + nodata: 0 + aliases: [band_6, swir_1] + + - name: swir22 + dtype: "uint16" + units: "1" + nodata: 0 + aliases: [band_7, swir_2] + + - name: "smad" + aliases: [sdev, SDEV] + units: "1" + dtype: float32 + nodata: .nan + + - name: "emad" + aliases: [edev, EDEV] + units: "1" + dtype: float32 + nodata: .nan + + - name: "bcmad" + aliases: [bcdev, BCDEV] + units: "1" + dtype: float32 + nodata: .nan + + - name: "count" + aliases: [] + units: "1" + dtype: uint16 + nodata: 0