diff --git a/SUPPORT.md b/SUPPORT.md
index 613bc15b..63e4baa8 100644
--- a/SUPPORT.md
+++ b/SUPPORT.md
@@ -2,8 +2,8 @@
## How to file issues and get help
-This project uses GitHub Issues to track bugs and feature requests. Please search the existing
-issues before filing new issues to avoid duplicates. For new issues, file your bug or
+This project uses GitHub Issues to track bugs and feature requests. Please search the existing
+issues before filing new issues to avoid duplicates. For new issues, file your bug or
feature request as a new Issue.
For help and questions about using this project, please reach out to the team at farmvibes at microsoft.com.
@@ -11,6 +11,7 @@ For help and questions about using this project, please reach out to the team at
## Troubleshooting
A list of common issues and their resolution can be found in the [troubleshooting documentation](https://microsoft.github.io/farmvibes-ai/docfiles/markdown/TROUBLESHOOTING.html).
+We also provide a current list of [known issues on our GitHub](https://github.com/microsoft/farmvibes-ai/labels/known%20issues) that are actively being worked on.
## Microsoft Support Policy
diff --git a/docs/source/conf.py b/docs/source/conf.py
index cd777be6..2fe34b1c 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -22,6 +22,7 @@
"sphinxcontrib.mermaid",
"myst_parser",
"sphinx_autodoc_typehints",
+ "sphinxcontrib.openapi",
]
autosummary_generate = True
diff --git a/docs/source/docfiles/code/vibe_core_client/client.md b/docs/source/docfiles/code/vibe_core_client/client.md
index 90a26a8e..b2286cf1 100644
--- a/docs/source/docfiles/code/vibe_core_client/client.md
+++ b/docs/source/docfiles/code/vibe_core_client/client.md
@@ -4,6 +4,7 @@
.. automodule:: vibe_core.client
:members:
:show-inheritance:
+ :private-members: _form_payload
.. autosummary::
:toctree: _autosummary
diff --git a/docs/source/docfiles/markdown/NOTEBOOK_LIST.md b/docs/source/docfiles/markdown/NOTEBOOK_LIST.md
index 3e4ad74a..caa6879d 100644
--- a/docs/source/docfiles/markdown/NOTEBOOK_LIST.md
+++ b/docs/source/docfiles/markdown/NOTEBOOK_LIST.md
@@ -61,6 +61,19 @@ We organize available notebooks in the following topics:
- [`Crop land segmentation (4/4) - inference` 📓](https://github.com/microsoft/farmvibes-ai/blob/main/notebooks/crop_segmentation/04_inference.ipynb)
+
+
+ Deforestation
+
+- [`Detecting Forest Changes` 📓](https://github.com/microsoft/farmvibes-ai/blob/main/notebooks/forest/forest_change_detection.ipynb)
+
+- [`Download ALOS forest extent maps` 📓](https://github.com/microsoft/farmvibes-ai/blob/main/notebooks/forest/download_alos_forest_map.ipynb)
+
+- [`Download Glad Forest Map` 📓](https://github.com/microsoft/farmvibes-ai/blob/main/notebooks/forest/download_glad_forest_map.ipynb)
+
+- [`Download Global Forest Change (Hansen) maps.` 📓](https://github.com/microsoft/farmvibes-ai/blob/main/notebooks/forest/download_hansen_forest_map.ipynb)
+
+
Index Computation
@@ -151,6 +164,14 @@ We organize available notebooks in the following topics:
- [`Crop land segmentation (4/4) - inference` 📓](https://github.com/microsoft/farmvibes-ai/blob/main/notebooks/crop_segmentation/04_inference.ipynb)
+- [`Detecting Forest Changes` 📓](https://github.com/microsoft/farmvibes-ai/blob/main/notebooks/forest/forest_change_detection.ipynb)
+
+- [`Download ALOS forest extent maps` 📓](https://github.com/microsoft/farmvibes-ai/blob/main/notebooks/forest/download_alos_forest_map.ipynb)
+
+- [`Download Glad Forest Map` 📓](https://github.com/microsoft/farmvibes-ai/blob/main/notebooks/forest/download_glad_forest_map.ipynb)
+
+- [`Download Global Forest Change (Hansen) maps.` 📓](https://github.com/microsoft/farmvibes-ai/blob/main/notebooks/forest/download_hansen_forest_map.ipynb)
+
- [`Field boundary segmentation (SAM exploration)` 📓](https://github.com/microsoft/farmvibes-ai/blob/main/notebooks/segment_anything/sam_exploration.ipynb)
- [`Field-level Irrigation Classification` 📓](https://github.com/microsoft/farmvibes-ai/blob/main/notebooks/irrigation/field_level_irrigation_classification.ipynb)
@@ -239,6 +260,14 @@ We organize available notebooks in the following topics:
- [`Carbon sequestration evaluation with Microsoft Azure Data Manager for Agriculture (ADMAg) and COMET-Farm API` 📓](https://github.com/microsoft/farmvibes-ai/blob/main/notebooks/admag/azure_data_manager_for_agriculture_and_comet_farm_api_example.ipynb)
+- [`Detecting Forest Changes` 📓](https://github.com/microsoft/farmvibes-ai/blob/main/notebooks/forest/forest_change_detection.ipynb)
+
+- [`Download ALOS forest extent maps` 📓](https://github.com/microsoft/farmvibes-ai/blob/main/notebooks/forest/download_alos_forest_map.ipynb)
+
+- [`Download Glad Forest Map` 📓](https://github.com/microsoft/farmvibes-ai/blob/main/notebooks/forest/download_glad_forest_map.ipynb)
+
+- [`Download Global Forest Change (Hansen) maps.` 📓](https://github.com/microsoft/farmvibes-ai/blob/main/notebooks/forest/download_hansen_forest_map.ipynb)
+
- [`Green House Gas fluxes` 📓](https://github.com/microsoft/farmvibes-ai/blob/main/notebooks/ghg_fluxes/ghg_fluxes.ipynb)
- [`Nutrient Heatmap Estimation - Classification` 📓](https://github.com/microsoft/farmvibes-ai/blob/main/notebooks/heatmaps/nutrients_using_classification.ipynb)
@@ -341,6 +370,14 @@ We organize available notebooks in the following topics:
- [`Crop land segmentation (4/4) - inference` 📓](https://github.com/microsoft/farmvibes-ai/blob/main/notebooks/crop_segmentation/04_inference.ipynb) : Infer crop land segmentation for new regions with a trained model.
+- [`Detecting Forest Changes` 📓](https://github.com/microsoft/farmvibes-ai/blob/main/notebooks/forest/forest_change_detection.ipynb) : Helps users to detect forest changes
+
+- [`Download ALOS forest extent maps` 📓](https://github.com/microsoft/farmvibes-ai/blob/main/notebooks/forest/download_alos_forest_map.ipynb) : This notebook downloads the ALOS (Advanced Land Observing Satellite) forest extent maps
+
+- [`Download Glad Forest Map` 📓](https://github.com/microsoft/farmvibes-ai/blob/main/notebooks/forest/download_glad_forest_map.ipynb) : This notebook downloads the Global Land Analysis (GLAD) forest extent maps.
+
+- [`Download Global Forest Change (Hansen) maps.` 📓](https://github.com/microsoft/farmvibes-ai/blob/main/notebooks/forest/download_hansen_forest_map.ipynb) : This notebook contains functions to download and process the Global Forest Change (Hansen) maps.
+
- [`Field boundary segmentation (SAM exploration)` 📓](https://github.com/microsoft/farmvibes-ai/blob/main/notebooks/segment_anything/sam_exploration.ipynb) : Segment Anything Model exploration over FarmVibes.AI data to segment crop field boundaries.
- [`Field-level Irrigation Classification` 📓](https://github.com/microsoft/farmvibes-ai/blob/main/notebooks/irrigation/field_level_irrigation_classification.ipynb) : Estimate an irrigation probability map over crop fields segmented with Segment Anything Model.
diff --git a/docs/source/docfiles/markdown/QUICKSTART.md b/docs/source/docfiles/markdown/QUICKSTART.md
index 0747f31f..6963a861 100644
--- a/docs/source/docfiles/markdown/QUICKSTART.md
+++ b/docs/source/docfiles/markdown/QUICKSTART.md
@@ -111,3 +111,6 @@ that FarmVibes.AI and the python client are working properly.
For more information on how to execute workflows, please take a look at our
[client guide](./CLIENT.md). For information on any issues running the cluster, including on
how to re-start it after a machine reboot, take a look at our [troubleshoot guide](./TROUBLESHOOTING.md).
+If you do not find the information you are looking for, please reach out to the team by opening
+an issue on our [GitHub repository](https://github.com/microsoft/farmvibes-ai/issues) or browsing
+through our [known issues](https://github.com/microsoft/farmvibes-ai/labels/known%20issues).
diff --git a/docs/source/docfiles/markdown/REST_API.md b/docs/source/docfiles/markdown/REST_API.md
new file mode 100644
index 00000000..a0e9ccad
--- /dev/null
+++ b/docs/source/docfiles/markdown/REST_API.md
@@ -0,0 +1,232 @@
+# REST API
+
+Once the FarmVibes.AI cluster is up and running, you can interact with it using the REST API, which provides a set of endpoints that allow you to list and describe workflows, as well as manage workflow runs.
+The REST API is available at the URL and port specified during cluster creation, and its address is printed in the terminal once the setup is complete. You can also check the address by running the following command in the terminal:
+
+```bash
+$ farmvibes-ai status
+2024-01-01 00:00:00,000 - INFO - Cluster farmvibes-ai-username is running with 1 servers and 0 agents.
+2024-01-01 00:00:00,001 - INFO - Service url is http://ip.address:port
+```
+
+## Interacting with the API
+
+The API is accessible from the [FarmVibes.AI Python client](https://microsoft.github.io/farmvibes-ai/docfiles/markdown/CLIENT.html), which provides an interface to interact with the cluster, list workflows, and manage workflow runs.
+Alternativelly, interacting with the API can be done using any tool that can send HTTP requests, such as `curl` or [Bruno](https://www.usebruno.com/).
+
+For example, to list the available workflows, you can use the following command:
+
+```bash
+$ curl -X GET http://localhost:31108/v0/workflows
+```
+
+Which will return the following list:
+
+```
+["helloworld","farm_ai/land_degradation/landsat_ndvi_trend","farm_ai/land_degradation/ndvi_linear_trend", ...]
+```
+
+For submiting a run of a specific workflow, we need to pass a JSON with the run configuration
+(i.e., workflow name, input geometry and time range, workflow parameters, etc) as the body of the
+request. For example, we can use the following command to create a `helloworld` workflow run:
+
+```bash
+$ curl -X POST -H "Content-Type: application/json" -d
+```
+
+Replacing the body of the request `` with the following:
+
+```json
+{
+ "name": "Hello!",
+ "workflow": "helloworld",
+ "parameters": null,
+ "user_input": {
+ "start_date": "2020-05-01T00:00:00",
+ "end_date": "2020-05-05T00:00:00",
+ "geojson": {
+ "features": [
+ {
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ -119.14896203939314,
+ 46.51578909859286
+ ],
+ [
+ -119.14896203939314,
+ 46.37578909859286
+ ],
+ [
+ -119.28896203939313,
+ 46.37578909859286
+ ],
+ [
+ -119.28896203939313,
+ 46.51578909859286
+ ],
+ [
+ -119.14896203939314,
+ 46.51578909859286
+ ]
+ ]
+ ]
+ },
+ "type": "Feature"
+ }
+ ],
+ "type": "FeatureCollection"
+ }
+ }
+}
+```
+
+To help in understanding the expected format and structure of the json in our requests, we provide in
+our Python client the `_form_payload` method ([`vibe_core.client.FarmvibesAiClient._form_payload`](https://microsoft.github.io/farmvibes-ai/docfiles/code/vibe_core_client/client.html#vibe_core.client.FarmvibesAiClient._form_payload)) that can be used to
+generate the request payload for a given run configuration. For example, the following code could
+be used to obtain the json above for the helloworld workflow:
+
+```python
+from vibe_core.client import get_default_vibe_client
+import shapely.geometry as shpg
+from datetime import datetime
+
+client = get_default_vibe_client()
+
+geom = shpg.Point(-119.21896203939313, 46.44578909859286).buffer(.07, cap_style=3)
+time_range = (datetime(2020, 5, 1), datetime(2020, 5, 5))
+
+payload = client._form_payload("helloworld", None, geom, time_range, None,"Hello!")
+```
+
+Another example, considering the `farm_ai/segmentation/segment_s2` workflow run submited in the
+[Sentinel-2 Segmentation notebook](https://github.com/microsoft/farmvibes-ai/blob/main/notebooks/segment_anything/sentinel2_segmentation.ipynb), would be:
+
+```python
+payload = client._form_payload("farm_ai/segmentation/segment_s2", None, None, None, {"user_input": roi_time_range, "prompts": geom_collection},"SAM segmentation")
+```
+
+Which would generate the following json:
+
+```json
+{
+ "name": "SAM segmentation",
+ "workflow": "farm_ai/segmentation/segment_s2",
+ "parameters": null,
+ "user_input": {
+ "user_input": {
+ "type": "Feature",
+ "stac_version": "1.0.0",
+ "id": "f6465ad0-5e01-4792-ad99-a0bd240c1e7d",
+ "properties": {
+ "start_datetime": "2020-05-01T00:00:00+00:00",
+ "end_datetime": "2020-05-05T00:00:00+00:00",
+ "datetime": "2020-05-01T00:00:00Z"
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ -119.14896203939314,
+ 46.51578909859286
+ ],
+ [
+ -119.14896203939314,
+ 46.37578909859286
+ ],
+ [
+ -119.28896203939313,
+ 46.37578909859286
+ ],
+ [
+ -119.28896203939313,
+ 46.51578909859286
+ ],
+ [
+ -119.14896203939314,
+ 46.51578909859286
+ ]
+ ]
+ ]
+ },
+ "links": [],
+ "assets": {},
+ "bbox": [
+ -119.28896203939313,
+ 46.37578909859286,
+ -119.14896203939314,
+ 46.51578909859286
+ ],
+ "stac_extensions": [],
+ "terravibes_data_type": "DataVibe"
+ },
+ "prompts": {
+ "type": "Feature",
+ "stac_version": "1.0.0",
+ "id": "geo_734c6441-cb25-4c40-8204-6b7286f24bb9",
+ "properties": {
+ "urls": [
+ "/mnt/734c6441-cb25-4c40-8204-6b7286f24bb9_geometry_collection.geojson"
+ ],
+ "start_datetime": "2020-05-01T00:00:00+00:00",
+ "end_datetime": "2020-05-05T00:00:00+00:00",
+ "datetime": "2020-05-01T00:00:00Z"
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ -119.14896203939314,
+ 46.51578909859286
+ ],
+ [
+ -119.14896203939314,
+ 46.37578909859286
+ ],
+ [
+ -119.28896203939313,
+ 46.37578909859286
+ ],
+ [
+ -119.28896203939313,
+ 46.51578909859286
+ ],
+ [
+ -119.14896203939314,
+ 46.51578909859286
+ ]
+ ]
+ ]
+ },
+ "links": [],
+ "assets": {},
+ "bbox": [
+ -119.28896203939313,
+ 46.37578909859286,
+ -119.14896203939314,
+ 46.51578909859286
+ ],
+ "stac_extensions": [],
+ "terravibes_data_type": "ExternalReferenceList"
+ }
+ }
+}
+```
+
+For more information about the `_form_payload` method, please refer to the [FarmVibes.AI Python client documentation](https://microsoft.github.io/farmvibes-ai/docfiles/code/vibe_core_client/client.html#vibe_core.client.FarmvibesAiClient._form_payload).
+
+## Endpoints
+
+We provide below a list of the available endpoints and their descriptions.
+
+-----------------------------
+
+```{eval-rst}
+.. openapi:: ../openapi.json
+ :examples:
+ :format: markdown
+```
diff --git a/docs/source/docfiles/markdown/TROUBLESHOOTING.md b/docs/source/docfiles/markdown/TROUBLESHOOTING.md
index 1e2d056d..d3e54963 100644
--- a/docs/source/docfiles/markdown/TROUBLESHOOTING.md
+++ b/docs/source/docfiles/markdown/TROUBLESHOOTING.md
@@ -1,6 +1,8 @@
# Troubleshooting
This document compiles the most common issues encountered when installing and running FarmVibes.AI platform, grouped into broad categories.
+Besides the issues listed here, we also collect a list of [known issues on our GitHub repository](https://github.com/microsoft/farmvibes-ai/labels/known%20issues)
+that are currently being addressed by the development team.
- **Package installation:**
@@ -224,6 +226,13 @@ This document compiles the most common issues encountered when installing and ru
+
+ Workflow run monitor table not rendering inside notebook
+
+ Make sure to have the `ipywidgets` [package](https://pypi.org/project/ipywidgets/) installed in your environment.
+
+
+
- **Segment Anything Model (SAM):**
diff --git a/docs/source/docfiles/markdown/WORKFLOWS.md b/docs/source/docfiles/markdown/WORKFLOWS.md
index ed585ef0..02ed1383 100644
--- a/docs/source/docfiles/markdown/WORKFLOWS.md
+++ b/docs/source/docfiles/markdown/WORKFLOWS.md
@@ -7,6 +7,7 @@ We group FarmVibes.AI workflows in the following categories:
This includes raw data sources (e.g., Sentinel 1 and 2, LandSat, CropDataLayer) as well as the SpaceEye cloud-removal model;
- **Data Processing**: workflows that transform data into different data types (e.g., computing NDVI/MSAVI/Methane indexes, aggregating mean/max/min statistics of rasters, timeseries aggregation);
- **FarmAI**: composed workflows (data ingestion + processing) whose outputs enable FarmAI scenarios (e.g., predicting conservation practices, estimating soil carbon sequestration, identifying methane leakage);
+- **ForestAI**: composed workflows (data ingestion + processing) whose outputs enable ForestAI scenarios (e.g., detecting forest change, estimating forest extent);
- **ML**: machine learning-related workflows to train, evaluate, and infer models within the FarmVibes.AI platform (e.g., dataset creation, inference);
For a list of all available workflows within the FarmVibes.AI platform, please
diff --git a/docs/source/docfiles/markdown/WORKFLOW_LIST.md b/docs/source/docfiles/markdown/WORKFLOW_LIST.md
index eca05337..473d9a7e 100644
--- a/docs/source/docfiles/markdown/WORKFLOW_LIST.md
+++ b/docs/source/docfiles/markdown/WORKFLOW_LIST.md
@@ -6,6 +6,7 @@ We group FarmVibes.AI workflows in the following categories:
This includes raw data sources (e.g., Sentinel 1 and 2, LandSat, CropDataLayer) as well as the SpaceEye cloud-removal model;
- **Data Processing**: workflows that transform data into different data types (e.g., computing NDVI/MSAVI/Methane indexes, aggregating mean/max/min statistics of rasters, timeseries aggregation);
- **FarmAI**: composed workflows (data ingestion + processing) whose outputs enable FarmAI scenarios (e.g., predicting conservation practices, estimating soil carbon sequestration, identifying methane leakage);
+- **ForestAI**: composed workflows (data ingestion + processing) whose outputs enable ForestAI scenarios (e.g., detecting forest change, estimating forest extent);
- **ML**: machine learning-related workflows to train, evaluate, and infer models within the FarmVibes.AI platform (e.g., dataset creation, inference);
Below is a list of all available workflows within the FarmVibes.AI platform. For each of them, we provide a brief description and a link to the corresponding documentation page.
@@ -44,6 +45,8 @@ Below is a list of all available workflows within the FarmVibes.AI platform. For
- [`gnatsgo/download_gnatsgo` 📄](workflow_yaml/data_ingestion/gnatsgo/download_gnatsgo.md): Downloads gNATSGO raster data that intersect with the input geometry and time range.
+- [`hansen/hansen_forest_change_download` 📄](workflow_yaml/data_ingestion/hansen/hansen_forest_change_download.md): Downloads and merges Global Forest Change (Hansen) rasters that intersect the user-provided geometry/time range.
+
- [`landsat/preprocess_landsat` 📄](workflow_yaml/data_ingestion/landsat/preprocess_landsat.md): Downloads and preprocesses LANDSAT tiles that intersect with the input geometry and time range.
- [`modis/download_modis_surface_reflectance` 📄](workflow_yaml/data_ingestion/modis/download_modis_surface_reflectance.md): Downloads MODIS 8-day surface reflectance rasters that intersect with the input geometry and time range.
@@ -56,8 +59,6 @@ Below is a list of all available workflows within the FarmVibes.AI platform. For
- [`sentinel1/preprocess_s1` 📄](workflow_yaml/data_ingestion/sentinel1/preprocess_s1.md): Downloads and preprocesses tiles of Sentinel-1 imagery that intersect with the input Sentinel-2 products in the input time range.
-- [`sentinel1/preprocess_s1_rtc` 📄](workflow_yaml/data_ingestion/sentinel1/preprocess_s1_rtc.md): Downloads and preprocesses tiles of Sentinel-1 imagery that intersect with the input Sentinel-2 products in the input time range.
-
- [`sentinel2/cloud_ensemble` 📄](workflow_yaml/data_ingestion/sentinel2/cloud_ensemble.md): Computes the cloud probability of a Sentinel-2 L2A raster using an ensemble of five cloud segmentation models.
- [`sentinel2/improve_cloud_mask` 📄](workflow_yaml/data_ingestion/sentinel2/improve_cloud_mask.md): Improves cloud masks by merging the product cloud mask with cloud and shadow masks computed by machine learning segmentation models.
@@ -180,6 +181,13 @@ Below is a list of all available workflows within the FarmVibes.AI platform. For
- [`water/irrigation_classification` 📄](workflow_yaml/farm_ai/water/irrigation_classification.md): Develops 30m pixel-wise irrigation probability map.
+## forest_ai
+
+- [`deforestation/alos_trend_detection` 📄](workflow_yaml/forest_ai/deforestation/alos_trend_detection.md): Detects increase/decrease trends in forest pixel levels over the user-input geometry and time range for the ALOS forest map.
+
+- [`deforestation/ordinal_trend_detection` 📄](workflow_yaml/forest_ai/deforestation/ordinal_trend_detection.md): Detects increase/decrease trends in the pixel levels over the user-input geometry and time range.
+
+
## ml
- [`crop_segmentation` 📄](workflow_yaml/ml/crop_segmentation.md): Runs a crop segmentation model based on NDVI from SpaceEye imagery along the year.
@@ -188,4 +196,8 @@ Below is a list of all available workflows within the FarmVibes.AI platform. For
- [`driveway_detection` 📄](workflow_yaml/ml/driveway_detection.md): Detects driveways in front of houses.
+- [`segment_anything/basemap_prompt_segmentation` 📄](workflow_yaml/ml/segment_anything/basemap_prompt_segmentation.md): Runs Segment Anything Model (SAM) over BingMaps basemap rasters with points and/or bounding boxes as prompts.
+
+- [`segment_anything/s2_prompt_segmentation` 📄](workflow_yaml/ml/segment_anything/s2_prompt_segmentation.md): Runs Segment Anything Model (SAM) over Sentinel-2 rasters with points and/or bounding boxes as prompts.
+
diff --git a/docs/source/docfiles/markdown/data_types_diagram/core_types_hierarchy.md b/docs/source/docfiles/markdown/data_types_diagram/core_types_hierarchy.md
index c9fb1b1b..45f7fbfd 100644
--- a/docs/source/docfiles/markdown/data_types_diagram/core_types_hierarchy.md
+++ b/docs/source/docfiles/markdown/data_types_diagram/core_types_hierarchy.md
@@ -28,10 +28,14 @@ classDiagram
}
class GeometryCollection {
}
+ class OrdinalTrendTest {
+ }
class ProteinSequence {
}
class PydanticAssetVibe {
}
+ class RasterPixelCount {
+ }
class TimeSeries {
}
class Tmp {
@@ -54,7 +58,9 @@ classDiagram
GHGFlux --|> DataVibe
GHGProtocolVibe --|> DataVibe
GeometryCollection --|> DataVibe
+ OrdinalTrendTest --|> DataVibe
ProteinSequence --|> DataVibe
+ RasterPixelCount --|> DataVibe
TimeSeries --|> DataVibe
UnresolvedDataVibe --|> BaseVibe
diff --git a/docs/source/docfiles/markdown/data_types_diagram/farm_hierarchy.md b/docs/source/docfiles/markdown/data_types_diagram/farm_hierarchy.md
index 9b058cec..2deb78fb 100644
--- a/docs/source/docfiles/markdown/data_types_diagram/farm_hierarchy.md
+++ b/docs/source/docfiles/markdown/data_types_diagram/farm_hierarchy.md
@@ -6,6 +6,12 @@ classDiagram
}
class DataVibe {
}
+ class ADMAgPrescription {
+ }
+ class ADMAgPrescriptionInput {
+ }
+ class ADMAgPrescriptionMapInput {
+ }
class ADMAgSeasonalFieldInput {
}
class FertilizerInformation {
@@ -19,6 +25,9 @@ classDiagram
class TillageInformation {
}
DataVibe --|> BaseVibe
+ ADMAgPrescription --|> BaseVibe
+ ADMAgPrescriptionInput --|> BaseVibe
+ ADMAgPrescriptionMapInput --|> BaseVibe
ADMAgSeasonalFieldInput --|> BaseVibe
SeasonalFieldInformation --|> DataVibe
diff --git a/docs/source/docfiles/markdown/data_types_diagram/products_hierarchy.md b/docs/source/docfiles/markdown/data_types_diagram/products_hierarchy.md
index 12db2fde..e90cd707 100644
--- a/docs/source/docfiles/markdown/data_types_diagram/products_hierarchy.md
+++ b/docs/source/docfiles/markdown/data_types_diagram/products_hierarchy.md
@@ -28,6 +28,8 @@ classDiagram
}
class GNATSGOProduct {
}
+ class HansenProduct {
+ }
class HerbieProduct {
}
class LandsatProduct {
@@ -48,6 +50,7 @@ classDiagram
GEDIProduct --|> DataVibe
GLADProduct --|> DataVibe
GNATSGOProduct --|> DataVibe
+ HansenProduct --|> DataVibe
HerbieProduct --|> DataVibe
LandsatProduct --|> DataVibe
ModisProduct --|> DataVibe
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/admag/admag_seasonal_field.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/admag/admag_seasonal_field.md
index 02e04341..d9f5206f 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/admag/admag_seasonal_field.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/admag/admag_seasonal_field.md
@@ -1,5 +1,42 @@
# data_ingestion/admag/admag_seasonal_field
+Generates SeasonalFieldInformation using ADMAg (Microsoft Azure Data Manager for Agriculture). The workflow creates a DataVibe subclass SeasonalFieldInformation that contains farm-related operations (e.g., fertilization, harvest, tillage, planting, crop name).
+
+```{mermaid}
+ graph TD
+ inp1>admag_input]
+ out1>seasonal_field]
+ tsk1{{admag_seasonal_field}}
+ inp1>admag_input] -- admag_input --> tsk1{{admag_seasonal_field}}
+ tsk1{{admag_seasonal_field}} -- seasonal_field --> out1>seasonal_field]
+```
+
+## Sources
+
+- **admag_input**: Unique identifiers for ADMAg seasonal field, and party.
+
+## Sinks
+
+- **seasonal_field**: Crop SeasonalFieldInformation which contains SeasonalFieldInformation that contains farm-related operations (e.g., fertilization, harvest, tillage, planting, crop name).
+
+## Parameters
+
+- **base_url**: Azure Data Manager for Agriculture host. Please visit https://aka.ms/farmvibesDMA to check how to get these credentials.
+
+- **client_id**: Azure Data Manager for Agriculture client id. Please visit https://aka.ms/farmvibesDMA to check how to get these credentials.
+
+- **client_secret**: Azure Data Manager for Agriculture client secret. Please visit https://aka.ms/farmvibesDMA to check how to get these credentials.
+
+- **authority**: Azure Data Manager for Agriculture authority. Please visit https://aka.ms/farmvibesDMA to check how to get these credentials.
+
+- **default_scope**: Azure Data Manager for Agriculture default scope. Please visit https://aka.ms/farmvibesDMA to check how to get these credentials.
+
+## Tasks
+
+- **admag_seasonal_field**: Establishes the connection with ADMAg and fetches seasonal field information.
+
+## Workflow Yaml
+
```yaml
name: admag_seasonal_field
@@ -31,7 +68,7 @@ description:
that contains farm-related operations (e.g., fertilization, harvest, tillage,
planting, crop name).
sources:
- admag_input: Unique identifiers for ADMAg seasonal field, boundary, and farmer.
+ admag_input: Unique identifiers for ADMAg seasonal field, and party.
sinks:
seasonal_field: Crop SeasonalFieldInformation which contains SeasonalFieldInformation
that contains farm-related operations (e.g., fertilization, harvest, tillage,
@@ -49,13 +86,4 @@ description:
https://aka.ms/farmvibesDMA to check how to get these credentials.
-```
-
-```{mermaid}
- graph TD
- inp1>admag_input]
- out1>seasonal_field]
- tsk1{{admag_seasonal_field}}
- inp1>admag_input] -- admag_input --> tsk1{{admag_seasonal_field}}
- tsk1{{admag_seasonal_field}} -- seasonal_field --> out1>seasonal_field]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/admag/prescriptions.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/admag/prescriptions.md
index d4ebeecf..bf21c61c 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/admag/prescriptions.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/admag/prescriptions.md
@@ -1,10 +1,57 @@
# data_ingestion/admag/prescriptions
+Fetches prescriptions using ADMAg (Microsoft Azure Data Manager for Agriculture). The workflow fetch prescriptions (sensor samples) linked to prescription_map_id. Each sensor sample have the information of nutrient (Nitrogen, Carbon, Phosphorus, pH, Latitude, Longitude etc., ). The Latitude & Longitude used to create a point geometry. Geometry and nutrient information transformed to GeoJSON. The GeoJSON stored as asset in farmvibes-ai.
+
+```{mermaid}
+ graph TD
+ inp1>admag_input]
+ out1>response]
+ tsk1{{list_prescriptions}}
+ tsk2{{get_prescription}}
+ tsk3{{admag_prescriptions}}
+ tsk1{{list_prescriptions}} -- prescriptions/prescription_without_geom_input --> tsk2{{get_prescription}}
+ tsk2{{get_prescription}} -- prescription_with_geom/prescriptions_with_geom_input --> tsk3{{admag_prescriptions}}
+ inp1>admag_input] -- admag_input --> tsk1{{list_prescriptions}}
+ inp1>admag_input] -- admag_input --> tsk3{{admag_prescriptions}}
+ tsk3{{admag_prescriptions}} -- response --> out1>response]
+```
+
+## Sources
+
+- **admag_input**: Required inputs to access ADMAg resources, party_id and prescription_map_id that helps fetching prescriptions.
+
+## Sinks
+
+- **response**: Prescriptions received from ADMAg.
+
+## Parameters
+
+- **base_url**: URL to access the registered app. Refer this url to create required resources for admag. https://learn.microsoft.com/en-us/azure/data-manager-for-agri/quickstart-install-data-manager-for-agriculture
+
+- **client_id**: Value uniquely identifies registered application in the Microsoft identity platform. Visit url https://learn.microsoft.com/en-us/azure/data-manager-for-agri/quickstart-install-data-manager-for-agriculture to register the app.
+
+- **client_secret**: Sometimes called an application password, a client secret is a string value your app can use in place of a certificate to identity itself.
+
+- **authority**: The endpoint URIs for your app are generated automatically when you register or configure your app. It is used by client to obtain authorization from the resource owner
+
+- **default_scope**: URL for default azure OAuth2 permissions
+
+## Tasks
+
+- **list_prescriptions**: List available prescriptions using prescription map.
+
+- **get_prescription**: Get prescription using ADMAg API.
+
+- **admag_prescriptions**: Downloads boundary and prescriptions linked to seasonal field from ADMAg data source.
+
+## Workflow Yaml
+
```yaml
name: admag_prescritpions
sources:
admag_input:
+ - list_prescriptions.admag_input
- admag_prescriptions.admag_input
sinks:
response: admag_prescriptions.response
@@ -15,6 +62,24 @@ parameters:
authority: null
default_scope: null
tasks:
+ list_prescriptions:
+ op: list_prescriptions
+ op_dir: admag
+ parameters:
+ base_url: '@from(base_url)'
+ client_id: '@from(client_id)'
+ client_secret: '@from(client_secret)'
+ authority: '@from(authority)'
+ default_scope: '@from(default_scope)'
+ get_prescription:
+ op: get_prescription
+ op_dir: admag
+ parameters:
+ base_url: '@from(base_url)'
+ client_id: '@from(client_id)'
+ client_secret: '@from(client_secret)'
+ authority: '@from(authority)'
+ default_scope: '@from(default_scope)'
admag_prescriptions:
op: prescriptions
op_dir: admag
@@ -24,6 +89,13 @@ tasks:
client_secret: '@from(client_secret)'
authority: '@from(authority)'
default_scope: '@from(default_scope)'
+edges:
+- origin: list_prescriptions.prescriptions
+ destination:
+ - get_prescription.prescription_without_geom_input
+- origin: get_prescription.prescription_with_geom
+ destination:
+ - admag_prescriptions.prescriptions_with_geom_input
description:
short_description: Fetches prescriptions using ADMAg (Microsoft Azure Data Manager
for Agriculture).
@@ -33,7 +105,7 @@ description:
geometry. Geometry and nutrient information transformed to GeoJSON. The GeoJSON
stored as asset in farmvibes-ai.
sources:
- admag_input: Required inputs to access ADMAg resources, farmer_id and prescription_map_id
+ admag_input: Required inputs to access ADMAg resources, party_id and prescription_map_id
that helps fetching prescriptions.
sinks:
response: Prescriptions received from ADMAg.
@@ -51,13 +123,4 @@ description:
default_scope: URL for default azure OAuth2 permissions
-```
-
-```{mermaid}
- graph TD
- inp1>admag_input]
- out1>response]
- tsk1{{admag_prescriptions}}
- inp1>admag_input] -- admag_input --> tsk1{{admag_prescriptions}}
- tsk1{{admag_prescriptions}} -- response --> out1>response]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/airbus/airbus_download.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/airbus/airbus_download.md
index 81d7c27a..f3338809 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/airbus/airbus_download.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/airbus/airbus_download.md
@@ -1,5 +1,38 @@
# data_ingestion/airbus/airbus_download
+Downloads available AirBus imagery for the input geometry and time range. The workflow will check available imagery, using the AirBus API, that contains the input geometry and inside the input time range. Matching images will be purchased (if they are not already in the user's library) and downloaded. This workflow requires an AirBus API key.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>raster]
+ tsk1{{list}}
+ tsk2{{download}}
+ tsk1{{list}} -- airbus_products --> tsk2{{download}}
+ inp1>user_input] -- input_item --> tsk1{{list}}
+ tsk2{{download}} -- downloaded_products --> out1>raster]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **raster**: AirBus raster.
+
+## Parameters
+
+- **api_key**: AirBus API key. Required to run the workflow.
+
+## Tasks
+
+- **list**: Lists available AirBus products for the input geometry and time range.
+
+- **download**: Downloads the AirBus imagery from the listed product.
+
+## Workflow Yaml
+
```yaml
name: airbus_download
@@ -38,15 +71,4 @@ description:
api_key: AirBus API key. Required to run the workflow.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>raster]
- tsk1{{list}}
- tsk2{{download}}
- tsk1{{list}} -- airbus_products --> tsk2{{download}}
- inp1>user_input] -- input_item --> tsk1{{list}}
- tsk2{{download}} -- downloaded_products --> out1>raster]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/airbus/airbus_price.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/airbus/airbus_price.md
index fe025d19..ac6ac4f8 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/airbus/airbus_price.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/airbus/airbus_price.md
@@ -1,5 +1,38 @@
# data_ingestion/airbus/airbus_price
+Prices available AirBus imagery for the input geometry and time range. The workflow will check available imagery, using the AirBus API, that contains the input geometry inside the input time range. The aggregate price (in kB) for matching images will be computed, discounting images already in the user's library. This workflow requires an AirBus API key.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>price]
+ tsk1{{list}}
+ tsk2{{price}}
+ tsk1{{list}} -- airbus_products --> tsk2{{price}}
+ inp1>user_input] -- input_item --> tsk1{{list}}
+ tsk2{{price}} -- products_price --> out1>price]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **price**: Price for all matching imagery.
+
+## Parameters
+
+- **api_key**: AirBus API key. Required to run the workflow.
+
+## Tasks
+
+- **list**: Lists available AirBus products for the input geometry and time range.
+
+- **price**: Calculates the aggregate price (in kB) for selected AirBus images, discounting images already in the user's library.
+
+## Workflow Yaml
+
```yaml
name: airbus_price
@@ -38,15 +71,4 @@ description:
api_key: AirBus API key. Required to run the workflow.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>price]
- tsk1{{list}}
- tsk2{{price}}
- tsk1{{list}} -- airbus_products --> tsk2{{price}}
- inp1>user_input] -- input_item --> tsk1{{list}}
- tsk2{{price}} -- products_price --> out1>price]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/alos/alos_forest_extent_download.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/alos/alos_forest_extent_download.md
index 0094a816..9e544b44 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/alos/alos_forest_extent_download.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/alos/alos_forest_extent_download.md
@@ -1,5 +1,38 @@
# data_ingestion/alos/alos_forest_extent_download
+Downloads Advanced Land Observing Satellite (ALOS) forest/non-forest classification map. The workflow lists all ALOS forest/non-forest classification products that intersect with the input geometry and time range (available range 2015-2020), then downloads the data for each of them. The data will be returned in the form of rasters.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>downloaded_product]
+ tsk1{{list}}
+ tsk2{{download}}
+ tsk1{{list}} -- alos_products/product --> tsk2{{download}}
+ inp1>user_input] -- input_data --> tsk1{{list}}
+ tsk2{{download}} -- raster --> out1>downloaded_product]
+```
+
+## Sources
+
+- **user_input**: Geometry of interest for which to download the ALOS forest/non-forest classification map.
+
+## Sinks
+
+- **downloaded_product**: Downloaded ALOS forest/non-forest classification map.
+
+## Parameters
+
+- **pc_key**: Planetary computer API key.
+
+## Tasks
+
+- **list**: Lists ALOS forest products for input geometry and time range.
+
+- **download**: Downloads Advanced Land Observing Satellite (ALOS) forest/non-forest classification map.
+
+## Workflow Yaml
+
```yaml
name: alos_forest_extent_download
@@ -35,15 +68,4 @@ description:
downloaded_product: Downloaded ALOS forest/non-forest classification map.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>downloaded_product]
- tsk1{{list}}
- tsk2{{download}}
- tsk1{{list}} -- alos_products/product --> tsk2{{download}}
- inp1>user_input] -- input_data --> tsk1{{list}}
- tsk2{{download}} -- raster --> out1>downloaded_product]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/alos/alos_forest_extent_download_merge.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/alos/alos_forest_extent_download_merge.md
index 427c31ad..046e46b5 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/alos/alos_forest_extent_download_merge.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/alos/alos_forest_extent_download_merge.md
@@ -1,5 +1,46 @@
# data_ingestion/alos/alos_forest_extent_download_merge
+Downloads Advanced Land Observing Satellite (ALOS) forest/non-forest classification map and merges it into a single raster. The workflow lists the ALOS forest/non-forest classification products that intersect with the input geometry and time range (available range 2015-2020), and downloads the filtered products. The workflow processes the downloaded products and merge them into a single raster.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>merged_raster]
+ out2>categorical_raster]
+ tsk1{{alos_forest_extent_download}}
+ tsk2{{group_rasters_by_time}}
+ tsk3{{merge}}
+ tsk1{{alos_forest_extent_download}} -- downloaded_product/rasters --> tsk2{{group_rasters_by_time}}
+ tsk2{{group_rasters_by_time}} -- raster_groups/raster_sequence --> tsk3{{merge}}
+ inp1>user_input] -- user_input --> tsk1{{alos_forest_extent_download}}
+ tsk3{{merge}} -- raster --> out1>merged_raster]
+ tsk1{{alos_forest_extent_download}} -- downloaded_product --> out2>categorical_raster]
+```
+
+## Sources
+
+- **user_input**: Geometry of interest for which to download the ALOS forest/non-forest classification map.
+
+## Sinks
+
+- **merged_raster**: ALOS forest/non-forest classification products converted to raster and merged.
+
+- **categorical_raster**: ALOS forest/non-forest classification products that intersect with the input geometry & time range.
+
+## Parameters
+
+- **pc_key**: Planetary computer API key.
+
+## Tasks
+
+- **alos_forest_extent_download**: Downloads Advanced Land Observing Satellite (ALOS) forest/non-forest classification map.
+
+- **group_rasters_by_time**: This op groups rasters in time according to 'criterion'.
+
+- **merge**: Merges rasters in a sequence to a single raster.
+
+## Workflow Yaml
+
```yaml
name: alos_forest_extent_download_merge
@@ -48,19 +89,4 @@ description:
pc_key: Planetary computer API key.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>merged_raster]
- out2>categorical_raster]
- tsk1{{alos_forest_extent_download}}
- tsk2{{group_rasters_by_time}}
- tsk3{{merge}}
- tsk1{{alos_forest_extent_download}} -- downloaded_product/rasters --> tsk2{{group_rasters_by_time}}
- tsk2{{group_rasters_by_time}} -- raster_groups/raster_sequence --> tsk3{{merge}}
- inp1>user_input] -- user_input --> tsk1{{alos_forest_extent_download}}
- tsk3{{merge}} -- raster --> out1>merged_raster]
- tsk1{{alos_forest_extent_download}} -- downloaded_product --> out2>categorical_raster]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/bing/basemap_download.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/bing/basemap_download.md
index 881d3632..9f30f0db 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/bing/basemap_download.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/bing/basemap_download.md
@@ -1,5 +1,40 @@
# data_ingestion/bing/basemap_download
+Downloads Bing Maps basemaps. The workflow will list all tiles intersecting with the input geometry for a given zoom level and download a basemap for each of them using Bing Maps API. The basemap tiles will be returned as individual rasters.
+
+```{mermaid}
+ graph TD
+ inp1>input_geometry]
+ out1>basemaps]
+ tsk1{{list}}
+ tsk2{{download}}
+ tsk1{{list}} -- products/input_product --> tsk2{{download}}
+ inp1>input_geometry] -- user_input --> tsk1{{list}}
+ tsk2{{download}} -- basemap --> out1>basemaps]
+```
+
+## Sources
+
+- **input_geometry**: Geometry of interest for which to download the basemap tiles.
+
+## Sinks
+
+- **basemaps**: Downloaded basemaps.
+
+## Parameters
+
+- **api_key**: Required BingMaps API key.
+
+- **zoom_level**: Zoom level of interest, ranging from 0 to 20. For instance, a zoom level of 1 corresponds to a resolution of 78271.52 m/pixel, a zoom level of 10 corresponds to 152.9 m/pixel, and a zoom level of 19 corresponds to 0.3 m/pixel. For more information on zoom levels and their corresponding scale and resolution, please refer to the BingMaps API documentation at https://learn.microsoft.com/en-us/bingmaps/articles/understanding-scale-and-resolution
+
+## Tasks
+
+- **list**: Lists BingMaps basemap tile products intersecting the input geometry for a given `zoom_level`.
+
+- **download**: Downloads a basemap tile represented by a BingMapsProduct using BingMapsAPI.
+
+## Workflow Yaml
+
```yaml
name: basemap_download
@@ -36,15 +71,4 @@ description:
basemaps: Downloaded basemaps.
-```
-
-```{mermaid}
- graph TD
- inp1>input_geometry]
- out1>basemaps]
- tsk1{{list}}
- tsk2{{download}}
- tsk1{{list}} -- products/input_product --> tsk2{{download}}
- inp1>input_geometry] -- user_input --> tsk1{{list}}
- tsk2{{download}} -- basemap --> out1>basemaps]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/bing/basemap_download_merge.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/bing/basemap_download_merge.md
index 1ad37192..121723af 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/bing/basemap_download_merge.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/bing/basemap_download_merge.md
@@ -1,5 +1,46 @@
# data_ingestion/bing/basemap_download_merge
+Downloads Bing Maps basemap tiles and merges them into a single raster. The workflow will list all tiles intersecting with the input geometry for a given zoom level, and download a basemap for each of them using Bing Maps API. The basemaps will be merged into a single raster with the union of the geometries of all tiles.
+
+```{mermaid}
+ graph TD
+ inp1>input_geometry]
+ out1>merged_basemap]
+ tsk1{{basemap_download}}
+ tsk2{{to_sequence}}
+ tsk3{{merge}}
+ tsk1{{basemap_download}} -- basemaps/list_rasters --> tsk2{{to_sequence}}
+ tsk2{{to_sequence}} -- rasters_seq/raster_sequence --> tsk3{{merge}}
+ inp1>input_geometry] -- input_geometry --> tsk1{{basemap_download}}
+ tsk3{{merge}} -- raster --> out1>merged_basemap]
+```
+
+## Sources
+
+- **input_geometry**: Geometry of interest for which to download the basemap tiles.
+
+## Sinks
+
+- **merged_basemap**: Merged basemap raster.
+
+## Parameters
+
+- **api_key**: Required BingMaps API key.
+
+- **zoom_level**: Zoom level of interest, ranging from 0 to 20. For instance, a zoom level of 1 corresponds to a resolution of 78271.52 m/pixel, a zoom level of 10 corresponds to 152.9 m/pixel, and a zoom level of 19 corresponds to 0.3 m/pixel. For more information on zoom levels and their corresponding scale and resolution, please refer to the BingMaps API documentation at https://learn.microsoft.com/en-us/bingmaps/articles/understanding-scale-and-resolution
+
+- **merge_resolution**: Determines how the resolution of the output raster is defined. One of 'equal' (breaks if the resolution of the sequence rasters are not the same), 'lowest' (uses the lowest resolution among rasters), 'highest' (uses the highest resolution among rasters), or 'average' (averages the resolution of all rasters in the sequence).
+
+## Tasks
+
+- **basemap_download**: Downloads Bing Maps basemaps.
+
+- **to_sequence**: Combines a list of Rasters into a RasterSequence.
+
+- **merge**: Merges rasters in a sequence to a single raster.
+
+## Workflow Yaml
+
```yaml
name: basemap_download_merge
@@ -44,17 +85,4 @@ description:
merged_basemap: Merged basemap raster.
-```
-
-```{mermaid}
- graph TD
- inp1>input_geometry]
- out1>merged_basemap]
- tsk1{{basemap_download}}
- tsk2{{to_sequence}}
- tsk3{{merge}}
- tsk1{{basemap_download}} -- basemaps/list_rasters --> tsk2{{to_sequence}}
- tsk2{{to_sequence}} -- rasters_seq/raster_sequence --> tsk3{{merge}}
- inp1>input_geometry] -- input_geometry --> tsk1{{basemap_download}}
- tsk3{{merge}} -- raster --> out1>merged_basemap]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/cdl/download_cdl.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/cdl/download_cdl.md
index aa4d0424..1f844998 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/cdl/download_cdl.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/cdl/download_cdl.md
@@ -1,5 +1,34 @@
# data_ingestion/cdl/download_cdl
+Downloads crop classes maps in the continental USA for the input time range. The workflow will download crop-specific land cover maps from the USDA Cropland Data Layer, available for the continental United States. The input geometry must intersect with the coverage area.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>raster]
+ tsk1{{list_cdl}}
+ tsk2{{download_cdl}}
+ tsk1{{list_cdl}} -- cdl_products/input_product --> tsk2{{download_cdl}}
+ inp1>user_input] -- input_item --> tsk1{{list_cdl}}
+ tsk2{{download_cdl}} -- cdl_raster --> out1>raster]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **raster**: CDL land cover raster.
+
+## Tasks
+
+- **list_cdl**: Lists all years for the input time range and creates a product for each of them to be downloaded.
+
+- **download_cdl**: Downloads a CategoricalRaster from a CDLProduct.
+
+## Workflow Yaml
+
```yaml
name: download_cdl
@@ -30,15 +59,4 @@ description:
raster: CDL land cover raster.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>raster]
- tsk1{{list_cdl}}
- tsk2{{download_cdl}}
- tsk1{{list_cdl}} -- cdl_products/input_product --> tsk2{{download_cdl}}
- inp1>user_input] -- input_item --> tsk1{{list_cdl}}
- tsk2{{download_cdl}} -- cdl_raster --> out1>raster]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/dem/download_dem.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/dem/download_dem.md
index 2ab1eca9..57419d31 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/dem/download_dem.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/dem/download_dem.md
@@ -1,5 +1,42 @@
# data_ingestion/dem/download_dem
+Downloads digital elevation map tiles that intersect with the input geometry and time range. The workflow will download digital elevation maps from the USGS 3DEP datasets (available for the United States at 10 and 30 meters) or Copernicus DEM GLO-30 (globally at 30 meters) through the Planetary Computer. For more information, see https://planetarycomputer.microsoft.com/dataset/3dep-seamless and https://planetarycomputer.microsoft.com/dataset/cop-dem-glo-30 .
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>raster]
+ tsk1{{list}}
+ tsk2{{download}}
+ tsk1{{list}} -- dem_products/input_product --> tsk2{{download}}
+ inp1>user_input] -- input_items --> tsk1{{list}}
+ tsk2{{download}} -- downloaded_product --> out1>raster]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **raster**: DEM raster.
+
+## Parameters
+
+- **pc_key**: Optional Planetary Computer API key.
+
+- **resolution**: Spatial resolution of the DEM. 10m and 30m are available.
+
+- **provider**: Provider of the DEM. "USGS3DEP" and "CopernicusDEM30" are available.
+
+## Tasks
+
+- **list**: Lists digital elevation map tiles that intersect with the input geometry and time range.
+
+- **download**: Downloads digital elevation map raster given a DemProduct.
+
+## Workflow Yaml
+
```yaml
name: download_dem
@@ -44,15 +81,4 @@ description:
provider: Provider of the DEM. "USGS3DEP" and "CopernicusDEM30" are available.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>raster]
- tsk1{{list}}
- tsk2{{download}}
- tsk1{{list}} -- dem_products/input_product --> tsk2{{download}}
- inp1>user_input] -- input_items --> tsk1{{list}}
- tsk2{{download}} -- downloaded_product --> out1>raster]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/gedi/download_gedi.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/gedi/download_gedi.md
index dbd2eb0c..17cc5147 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/gedi/download_gedi.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/gedi/download_gedi.md
@@ -1,5 +1,40 @@
# data_ingestion/gedi/download_gedi
+Downloads GEDI products for the input region and time range. The workflow downloads Global Ecosystem Dynamics Investigation (GEDI) products at the desired processing level using NASA's EarthData API. This workflow requires an EarthData API token.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>product]
+ tsk1{{list}}
+ tsk2{{download}}
+ tsk1{{list}} -- gedi_products/gedi_product --> tsk2{{download}}
+ inp1>user_input] -- input_data --> tsk1{{list}}
+ tsk2{{download}} -- downloaded_product --> out1>product]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **product**: GEDI products.
+
+## Parameters
+
+- **earthdata_token**: API token for the EarthData platform. Required to run the workflow.
+
+- **processing_level**: GEDI product processing level. One of 'GEDI01_B.002', 'GEDI02_A.002', 'GEDI02_B.002'.
+
+## Tasks
+
+- **list**: Lists GEDI Products from NASA's EarthData API.
+
+- **download**: Downloads GEDI products.
+
+## Workflow Yaml
+
```yaml
name: download_gedi
@@ -39,15 +74,4 @@ description:
'GEDI02_B.002'.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>product]
- tsk1{{list}}
- tsk2{{download}}
- tsk1{{list}} -- gedi_products/gedi_product --> tsk2{{download}}
- inp1>user_input] -- input_data --> tsk1{{list}}
- tsk2{{download}} -- downloaded_product --> out1>product]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/gedi/download_gedi_rh100.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/gedi/download_gedi_rh100.md
index 15563e8d..a77db935 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/gedi/download_gedi_rh100.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/gedi/download_gedi_rh100.md
@@ -1,5 +1,41 @@
# data_ingestion/gedi/download_gedi_rh100
+Downloads L2B GEDI products and extracts RH100 variables. The workflow will download the products for the input region and time range, and then extract RH100 variables for each of the beam shots. Each value is geolocated according to the lowest mode latitude and longitude values.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>rh100]
+ tsk1{{download}}
+ tsk2{{extract}}
+ tsk1{{download}} -- product/gedi_product --> tsk2{{extract}}
+ inp1>user_input] -- user_input --> tsk1{{download}}
+ inp1>user_input] -- roi --> tsk2{{extract}}
+ tsk2{{extract}} -- rh100 --> out1>rh100]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **rh100**: Points in EPSG:4326 with their associated RH100 values.
+
+## Parameters
+
+- **earthdata_token**: API token for the EarthData platform. Required to run the workflow.
+
+- **check_quality**: Whether to filter points according to the quality flag.
+
+## Tasks
+
+- **download**: Downloads GEDI products for the input region and time range.
+
+- **extract**: Extracts RH100 variables within the region of interest of a GEDIProduct.
+
+## Workflow Yaml
+
```yaml
name: download_gedi_rh100
@@ -38,16 +74,4 @@ description:
check_quality: Whether to filter points according to the quality flag.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>rh100]
- tsk1{{download}}
- tsk2{{extract}}
- tsk1{{download}} -- product/gedi_product --> tsk2{{extract}}
- inp1>user_input] -- user_input --> tsk1{{download}}
- inp1>user_input] -- roi --> tsk2{{extract}}
- tsk2{{extract}} -- rh100 --> out1>rh100]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/glad/glad_forest_extent_download.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/glad/glad_forest_extent_download.md
index 6c3d7e41..cc0bf726 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/glad/glad_forest_extent_download.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/glad/glad_forest_extent_download.md
@@ -1,5 +1,34 @@
# data_ingestion/glad/glad_forest_extent_download
+Downloads Global Land Analysis (GLAD) forest extent data. The workflow will list all GLAD forest extent products that intersect with the input geometry and download the data for each of them. The data will be returned as rasters.
+
+```{mermaid}
+ graph TD
+ inp1>input_item]
+ out1>downloaded_product]
+ tsk1{{list}}
+ tsk2{{download}}
+ tsk1{{list}} -- glad_products/glad_product --> tsk2{{download}}
+ inp1>input_item] -- input_item --> tsk1{{list}}
+ tsk2{{download}} -- downloaded_product --> out1>downloaded_product]
+```
+
+## Sources
+
+- **input_item**: Geometry of interest for which to download the GLAD forest extent data.
+
+## Sinks
+
+- **downloaded_product**: Downloaded GLAD forest extent product.
+
+## Tasks
+
+- **list**: Lists Global Land Analysis (GLAD) forest products that intersect the user-provided geometry/time range.
+
+- **download**: Downloads a GLADProduct
+
+## Workflow Yaml
+
```yaml
name: glad_forest_extent_download
@@ -31,15 +60,4 @@ description:
downloaded_product: Downloaded GLAD forest extent product.
-```
-
-```{mermaid}
- graph TD
- inp1>input_item]
- out1>downloaded_product]
- tsk1{{list}}
- tsk2{{download}}
- tsk1{{list}} -- glad_products/glad_product --> tsk2{{download}}
- inp1>input_item] -- input_item --> tsk1{{list}}
- tsk2{{download}} -- downloaded_product --> out1>downloaded_product]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/glad/glad_forest_extent_download_merge.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/glad/glad_forest_extent_download_merge.md
index 4f3a5dfd..ccb0fc8b 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/glad/glad_forest_extent_download_merge.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/glad/glad_forest_extent_download_merge.md
@@ -1,5 +1,42 @@
# data_ingestion/glad/glad_forest_extent_download_merge
+Downloads the tiles from Global Land Analysis (GLAD) forest data that intersect with the user input geometry and time range, and merges them into a single raster. The workflow lists the GLAD forest products that intersect with the input geometry and time range, and downloads the filtered products. The downloaded products are merged into a single raster and classified. The result tiles have pixel values categorized into two classes - 0 (non-forest) and 1 (forest). This workflow uses the same forest definition as the Food and Agriculture Organization of the United Nations (FAO).
+
+```{mermaid}
+ graph TD
+ inp1>input_item]
+ out1>merged_product]
+ out2>categorical_raster]
+ tsk1{{glad_forest_extent_download}}
+ tsk2{{group_rasters_by_time}}
+ tsk3{{merge}}
+ tsk1{{glad_forest_extent_download}} -- downloaded_product/rasters --> tsk2{{group_rasters_by_time}}
+ tsk2{{group_rasters_by_time}} -- raster_groups/raster_sequence --> tsk3{{merge}}
+ inp1>input_item] -- input_item --> tsk1{{glad_forest_extent_download}}
+ tsk3{{merge}} -- raster --> out1>merged_product]
+ tsk1{{glad_forest_extent_download}} -- downloaded_product --> out2>categorical_raster]
+```
+
+## Sources
+
+- **input_item**: Geometry of interest for which to download the GLAD forest extent data.
+
+## Sinks
+
+- **merged_product**: Merged GLAD forest extent product to geometry of interest.
+
+- **categorical_raster**: Raster with the GLAD forest extent data.
+
+## Tasks
+
+- **glad_forest_extent_download**: Downloads Global Land Analysis (GLAD) forest extent data.
+
+- **group_rasters_by_time**: This op groups rasters in time according to 'criterion'.
+
+- **merge**: Merges rasters in a sequence to a single raster.
+
+## Workflow Yaml
+
```yaml
name: glad_forest_extent_download_merge
@@ -44,19 +81,4 @@ description:
categorical_raster: Raster with the GLAD forest extent data.
-```
-
-```{mermaid}
- graph TD
- inp1>input_item]
- out1>merged_product]
- out2>categorical_raster]
- tsk1{{glad_forest_extent_download}}
- tsk2{{group_rasters_by_time}}
- tsk3{{merge}}
- tsk1{{glad_forest_extent_download}} -- downloaded_product/rasters --> tsk2{{group_rasters_by_time}}
- tsk2{{group_rasters_by_time}} -- raster_groups/raster_sequence --> tsk3{{merge}}
- inp1>input_item] -- input_item --> tsk1{{glad_forest_extent_download}}
- tsk3{{merge}} -- raster --> out1>merged_product]
- tsk1{{glad_forest_extent_download}} -- downloaded_product --> out2>categorical_raster]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/gnatsgo/download_gnatsgo.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/gnatsgo/download_gnatsgo.md
index c54c541b..b0dd9828 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/gnatsgo/download_gnatsgo.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/gnatsgo/download_gnatsgo.md
@@ -1,5 +1,79 @@
# data_ingestion/gnatsgo/download_gnatsgo
+Downloads gNATSGO raster data that intersect with the input geometry and time range. This workflow lists and downloads raster products of gNATSGO dataset from Planetary Computer. Input geometry must fall within Continel USA, whereas input time range can be arbitrary (all gNATSGO assets are from 2020-07-01). For more information on the available properties, see https://planetarycomputer.microsoft.com/dataset/gnatsgo-rasters.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>raster]
+ tsk1{{list}}
+ tsk2{{download}}
+ tsk1{{list}} -- gnatsgo_products/gnatsgo_product --> tsk2{{download}}
+ inp1>user_input] -- input_item --> tsk1{{list}}
+ tsk2{{download}} -- downloaded_raster --> out1>raster]
+```
+
+## Sources
+
+- **user_input**: Geometry of interest (arbitrary time range).
+
+## Sinks
+
+- **raster**: Raster with desired property.
+
+## Parameters
+
+- **pc_key**: Optional Planetary Computer API key.
+
+- **variable**: Options are:
+ aws{DEPTH} - Available water storage estimate (AWS) for the DEPTH zone.
+ soc{DEPTH} - Soil organic carbon stock estimate (SOC) for the DEPTH zone.
+ tk{DEPTH}a - Thickness of soil components used in the DEPTH zone for the AWS calculation.
+ tk{DEPTH}s - Thickness of soil components used in the DEPTH zone for the SOC calculation.
+ mukey - Map unit key, a unique identifier of a record for matching with gNATSGO tables.
+ droughty - Drought vulnerability estimate.
+ nccpi3all - National Commodity Crop Productivity Index that has the highest value among Corn
+and Soybeans, Small Grains, or Cotton for major earthy components.
+ nccpi3corn - National Commodity Crop Productivity Index for Corn for major earthy
+components.
+ nccpi3cot - National Commodity Crop Productivity Index for Cotton for major earthy
+components.
+ nccpi3sg - National Commodity Crop Productivity Index for Small Grains for major earthy
+components.
+ nccpi3soy - National Commodity Crop Productivity Index for Soy for major earthy components.
+ pctearthmc - National Commodity Crop Productivity Index map unit percent earthy is the map
+unit summed comppct_r for major earthy components.
+ pwsl1pomu - Potential Wetland Soil Landscapes (PWSL).
+ rootznaws - Root zone (commodity crop) available water storage estimate (RZAWS).
+ rootznemc - Root zone depth is the depth within the soil profile that commodity crop (cc)
+roots can effectively extract water and nutrients for growth.
+ musumcpct - Sum of the comppct_r (SSURGO component table) values for all listed components
+in the map unit.
+ musumcpcta - Sum of the comppct_r (SSURGO component table) values used in the available
+water storage calculation for the map unit.
+ musumcpcts - Sum of the comppct_r (SSURGO component table) values used in the soil organic
+carbon calculation for the map unit.
+gNATSGO has properties available for multiple soil depths. You may exchange DEPTH in the variable names above for any of the following (all measured in cm):
+ 0_5
+ 0_20
+ 0_30
+ 5_20
+ 0_100
+ 0_150
+ 0_999
+ 20_50
+ 50_100
+ 100_150
+ 150_999
+
+## Tasks
+
+- **list**: Lists gNATSGO products from Planetary Computer that intersect with input geometry.
+
+- **download**: Downloads the raster asset for 'variable' given a GNATSGO product.
+
+## Workflow Yaml
+
```yaml
name: download_gnatsgo
@@ -66,15 +140,4 @@ description:
\ 0_999\n 20_50\n 50_100\n 100_150\n 150_999"
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>raster]
- tsk1{{list}}
- tsk2{{download}}
- tsk1{{list}} -- gnatsgo_products/gnatsgo_product --> tsk2{{download}}
- inp1>user_input] -- input_item --> tsk1{{list}}
- tsk2{{download}} -- downloaded_raster --> out1>raster]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/hansen/hansen_forest_change_download.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/hansen/hansen_forest_change_download.md
new file mode 100644
index 00000000..1a36c647
--- /dev/null
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/hansen/hansen_forest_change_download.md
@@ -0,0 +1,110 @@
+# data_ingestion/hansen/hansen_forest_change_download
+
+Downloads and merges Global Forest Change (Hansen) rasters that intersect the user-provided geometry/time range. The workflow lists Global Forest Change (Hansen) products that intersect the user-provided geometry/time range, downloads the data for each of them, and merges the rasters. The dataset is available at 30m resolution and is updated annually. The data contains information on forest cover, loss, and gain. The default dataset version is GFC-2022-v1.10 and is passed to the workflow as the parameter tiles_folder_url. For the default version, the dataset is available from 2000 to 2022. Dataset details can be found at https://storage.googleapis.com/earthenginepartners-hansen/GFC-2022-v1.10/download.html.
+
+```{mermaid}
+ graph TD
+ inp1>input_item]
+ out1>merged_raster]
+ out2>downloaded_raster]
+ tsk1{{list}}
+ tsk2{{download}}
+ tsk3{{group}}
+ tsk4{{merge}}
+ tsk1{{list}} -- hansen_products/hansen_product --> tsk2{{download}}
+ tsk2{{download}} -- raster/rasters --> tsk3{{group}}
+ tsk3{{group}} -- raster_groups/raster_sequence --> tsk4{{merge}}
+ inp1>input_item] -- input_item --> tsk1{{list}}
+ tsk4{{merge}} -- raster --> out1>merged_raster]
+ tsk2{{download}} -- raster --> out2>downloaded_raster]
+```
+
+## Sources
+
+- **input_item**: User-provided geometry and time range.
+
+## Sinks
+
+- **merged_raster**: Merged Global Forest Change (Hansen) data as a raster.
+
+- **downloaded_raster**: Individual Global Forest Change (Hansen) rasters prior to the merge operation.
+
+## Parameters
+
+- **layer_name**: Name of the Global Forest Change (Hansen) layer. Can be any of the following names 'treecover2000', 'loss', 'gain', 'lossyear', 'datamask', 'first', 'last'.
+
+- **tiles_folder_url**: URL to the Global Forest Change (Hansen) dataset. It specifies the dataset version and is used to download the data.
+
+## Tasks
+
+- **list**: Lists Global Forest Change (Hansen) products that intersect the user-provided geometry/time range.
+
+- **download**: Downloads Global Forest Change (Hansen) data.
+
+- **group**: This op groups rasters in time according to 'criterion'.
+
+- **merge**: Merges rasters in a sequence to a single raster.
+
+## Workflow Yaml
+
+```yaml
+
+name: glad_forest_change_download
+sources:
+ input_item:
+ - list.input_item
+sinks:
+ merged_raster: merge.raster
+ downloaded_raster: download.raster
+parameters:
+ layer_name: null
+ tiles_folder_url: https://storage.googleapis.com/earthenginepartners-hansen/GFC-2022-v1.10/
+tasks:
+ list:
+ op: list_hansen_products
+ parameters:
+ tiles_folder_url: '@from(tiles_folder_url)'
+ layer_name: '@from(layer_name)'
+ download:
+ op: download_hansen
+ group:
+ op: group_rasters_by_time
+ parameters:
+ criterion: year
+ merge:
+ op: merge_rasters
+edges:
+- origin: list.hansen_products
+ destination:
+ - download.hansen_product
+- origin: download.raster
+ destination:
+ - group.rasters
+- origin: group.raster_groups
+ destination:
+ - merge.raster_sequence
+description:
+ short_description: Downloads and merges Global Forest Change (Hansen) rasters that
+ intersect the user-provided geometry/time range.
+ long_description: The workflow lists Global Forest Change (Hansen) products that
+ intersect the user-provided geometry/time range, downloads the data for each of
+ them, and merges the rasters. The dataset is available at 30m resolution and is
+ updated annually. The data contains information on forest cover, loss, and gain.
+ The default dataset version is GFC-2022-v1.10 and is passed to the workflow as
+ the parameter tiles_folder_url. For the default version, the dataset is available
+ from 2000 to 2022. Dataset details can be found at https://storage.googleapis.com/earthenginepartners-hansen/GFC-2022-v1.10/download.html.
+ sources:
+ input_item: User-provided geometry and time range.
+ sinks:
+ merged_raster: Merged Global Forest Change (Hansen) data as a raster.
+ downloaded_raster: Individual Global Forest Change (Hansen) rasters prior to the
+ merge operation.
+ parameters:
+ tiles_folder_url: URL to the Global Forest Change (Hansen) dataset. It specifies
+ the dataset version and is used to download the data.
+ layer_name: Name of the Global Forest Change (Hansen) layer. Can be any of the
+ following names 'treecover2000', 'loss', 'gain', 'lossyear', 'datamask', 'first',
+ 'last'.
+
+
+```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/landsat/preprocess_landsat.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/landsat/preprocess_landsat.md
index ae984f20..0e167384 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/landsat/preprocess_landsat.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/landsat/preprocess_landsat.md
@@ -1,5 +1,44 @@
# data_ingestion/landsat/preprocess_landsat
+Downloads and preprocesses LANDSAT tiles that intersect with the input geometry and time range. The workflow will download the tile bands from the Planetary Computer and stack them into a single raster at 30m resolution.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>raster]
+ tsk1{{list}}
+ tsk2{{download}}
+ tsk3{{stack}}
+ tsk1{{list}} -- landsat_products/landsat_product --> tsk2{{download}}
+ tsk2{{download}} -- downloaded_product/landsat_product --> tsk3{{stack}}
+ inp1>user_input] -- input_item --> tsk1{{list}}
+ tsk3{{stack}} -- landsat_raster --> out1>raster]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **raster**: LANDSAT rasters at 30m resolution.
+
+## Parameters
+
+- **pc_key**: Optional Planetary Computer API key.
+
+- **qa_mask_value**: Bitmap for which pixel to be included. See documentation for each bit in https://www.usgs.gov/media/images/landsat-collection-2-pixel-quality-assessment-bit-index For example, the default value 64 (i.e. 1<<6 ) corresponds to "Clear" pixels
+
+## Tasks
+
+- **list**: Lists LANDSAT tiles that intersect with the input geometry and time range.
+
+- **download**: Downloads LANDSAT tile bands from product.
+
+- **stack**: Stacks downloaded bands into a single raster.
+
+## Workflow Yaml
+
```yaml
name: preprocess_landsat
@@ -45,17 +84,4 @@ description:
For example, the default value 64 (i.e. 1<<6 ) corresponds to "Clear" pixels
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>raster]
- tsk1{{list}}
- tsk2{{download}}
- tsk3{{stack}}
- tsk1{{list}} -- landsat_products/landsat_product --> tsk2{{download}}
- tsk2{{download}} -- downloaded_product/landsat_product --> tsk3{{stack}}
- inp1>user_input] -- input_item --> tsk1{{list}}
- tsk3{{stack}} -- landsat_raster --> out1>raster]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/modis/download_modis_surface_reflectance.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/modis/download_modis_surface_reflectance.md
index 7c8dbd06..8024938d 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/modis/download_modis_surface_reflectance.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/modis/download_modis_surface_reflectance.md
@@ -1,5 +1,40 @@
# data_ingestion/modis/download_modis_surface_reflectance
+Downloads MODIS 8-day surface reflectance rasters that intersect with the input geometry and time range. The workflow will download MODIS raster images either at 250m or 500m resolution. The products are available at a 8-day interval and pixel values are selected based on low clouds, low view angle, and highest index value. Notice that only bands 1, 2 and quality control are available on 250m. For more information, see https://planetarycomputer.microsoft.com/dataset/modis-09Q1-061 https://planetarycomputer.microsoft.com/dataset/modis-09A1-061
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>raster]
+ tsk1{{list}}
+ tsk2{{download}}
+ tsk1{{list}} -- modis_products/product --> tsk2{{download}}
+ inp1>user_input] -- input_data --> tsk1{{list}}
+ tsk2{{download}} -- raster --> out1>raster]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **raster**: Products containing MODIS reflectance bands and data.
+
+## Parameters
+
+- **pc_key**: Optional Planetary Computer API key.
+
+- **resolution_m**: Product resolution, in meters. Either 250 or 500.
+
+## Tasks
+
+- **list**: Lists MODIS 8-day surface reflectance rasters intersecting with the input geometry and time range for desired resolution.
+
+- **download**: Downloads MODIS surface reflectance rasters.
+
+## Workflow Yaml
+
```yaml
name: download_modis_surface_reflectance
@@ -41,15 +76,4 @@ description:
resolution_m: Product resolution, in meters. Either 250 or 500.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>raster]
- tsk1{{list}}
- tsk2{{download}}
- tsk1{{list}} -- modis_products/product --> tsk2{{download}}
- inp1>user_input] -- input_data --> tsk1{{list}}
- tsk2{{download}} -- raster --> out1>raster]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/modis/download_modis_vegetation_index.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/modis/download_modis_vegetation_index.md
index ca221ee7..1ca2651d 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/modis/download_modis_vegetation_index.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/modis/download_modis_vegetation_index.md
@@ -1,5 +1,42 @@
# data_ingestion/modis/download_modis_vegetation_index
+Downloads MODIS 16-day vegetation index products that intersect with the input geometry and time range. The workflow will download products at the chosen index and resolution. The products are available at a 16-day interval and pixel values are selected based on low clouds, low view angle, and highest index value. Vegetation index values range from (-2000 to 10000). For more information, see https://planetarycomputer.microsoft.com/dataset/modis-13Q1-061 and https://lpdaac.usgs.gov/products/mod13a1v061/ .
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>index]
+ tsk1{{list}}
+ tsk2{{download}}
+ tsk1{{list}} -- modis_products/product --> tsk2{{download}}
+ inp1>user_input] -- input_data --> tsk1{{list}}
+ tsk2{{download}} -- index --> out1>index]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **index**: Products containing the chosen index at the chosen resolution.
+
+## Parameters
+
+- **index**: Vegetation index that should be downloaded. Either 'evi' or 'ndvi'.
+
+- **pc_key**: Optional Planetary Computer API key.
+
+- **resolution_m**: Product resolution, in meters. Either 250 or 500.
+
+## Tasks
+
+- **list**: Lists MODIS vegetation products for input geometry, time range and resolution.
+
+- **download**: Downloads selected index raster from Modis product.
+
+## Workflow Yaml
+
```yaml
name: download_modis_vegetation_index
@@ -44,15 +81,4 @@ description:
resolution_m: Product resolution, in meters. Either 250 or 500.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>index]
- tsk1{{list}}
- tsk2{{download}}
- tsk1{{list}} -- modis_products/product --> tsk2{{download}}
- inp1>user_input] -- input_data --> tsk1{{list}}
- tsk2{{download}} -- index --> out1>index]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/naip/download_naip.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/naip/download_naip.md
index 915ea69e..ce471e87 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/naip/download_naip.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/naip/download_naip.md
@@ -1,5 +1,38 @@
# data_ingestion/naip/download_naip
+Downloads NAIP tiles that intersect with the input geometry and time range.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>raster]
+ tsk1{{list}}
+ tsk2{{download}}
+ tsk1{{list}} -- naip_products/input_product --> tsk2{{download}}
+ inp1>user_input] -- input_item --> tsk1{{list}}
+ tsk2{{download}} -- downloaded_product --> out1>raster]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **raster**: NAIP tiles.
+
+## Parameters
+
+- **pc_key**: Optional Planetary Computer API key.
+
+## Tasks
+
+- **list**: Lists Naip tiles that intersect with input geometry and time range.
+
+- **download**: Downloads Naip raster from Naip product.
+
+## Workflow Yaml
+
```yaml
name: download_naip
@@ -33,15 +66,4 @@ description:
pc_key: Optional Planetary Computer API key.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>raster]
- tsk1{{list}}
- tsk2{{download}}
- tsk1{{list}} -- naip_products/input_product --> tsk2{{download}}
- inp1>user_input] -- input_item --> tsk1{{list}}
- tsk2{{download}} -- downloaded_product --> out1>raster]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/osm_road_geometries.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/osm_road_geometries.md
index 0ba4612c..c266c945 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/osm_road_geometries.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/osm_road_geometries.md
@@ -1,5 +1,45 @@
# data_ingestion/osm_road_geometries
+Downloads road geometry for input region from Open Street Maps. The workflow downloads information from Open Street Maps for the target region and generates geometries for roads that intercept the input region bounding box.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>roads]
+ tsk1{{download}}
+ inp1>user_input] -- input_region --> tsk1{{download}}
+ tsk1{{download}} -- roads --> out1>roads]
+```
+
+## Sources
+
+- **user_input**: List of external references.
+
+## Sinks
+
+- **roads**: Geometry collection with road geometries that intercept the input region bounding box.
+
+## Parameters
+
+- **network_type**: Type of roads that will be selected. One of:
+ - 'drive_service': get drivable streets, including service roads.
+ - 'walk': get all streets and paths that pedestrians can use (this network type ignores
+ one-way directionality).
+ - 'bike': get all streets and paths that cyclists can use.
+ - 'all': download all non-private OSM streets and paths (this is the default network type
+ unless you specify a different one).
+ - 'all_private': download all OSM streets and paths, including private-access ones.
+ - 'drive': get drivable public streets (but not service roads).
+For more information see https://osmnx.readthedocs.io/en/stable/index.html.
+
+- **buffer_size**: Size of buffer, in meters, to search for nodes in OSM.
+
+## Tasks
+
+- **download**: Downloads road geometry for input region from Open Street Maps.
+
+## Workflow Yaml
+
```yaml
name: osm_road_geometries
@@ -40,13 +80,4 @@ description:
buffer_size: Size of buffer, in meters, to search for nodes in OSM.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>roads]
- tsk1{{download}}
- inp1>user_input] -- input_region --> tsk1{{download}}
- tsk1{{download}} -- roads --> out1>roads]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel1/preprocess_s1.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel1/preprocess_s1.md
index 643f8495..d25e498d 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel1/preprocess_s1.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel1/preprocess_s1.md
@@ -1,8 +1,75 @@
# data_ingestion/sentinel1/preprocess_s1
+Downloads and preprocesses tiles of Sentinel-1 imagery that intersect with the input Sentinel-2 products in the input time range. The workflow fetches Sentinel-1 tiles that intersects with the Sentinel-2 products, downloads and preprocesses them, and produces Sentinel-1 rasters in the Sentinel-2 tiling system.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ inp2>s2_products]
+ out1>raster]
+ tsk1{{union}}
+ tsk2{{merge_geom_tr}}
+ tsk3{{list}}
+ tsk4{{filter}}
+ tsk5{{download}}
+ tsk6{{tile}}
+ tsk7{{group}}
+ tsk8{{merge}}
+ tsk1{{union}} -- merged/geometry --> tsk2{{merge_geom_tr}}
+ tsk2{{merge_geom_tr}} -- merged/input_item --> tsk3{{list}}
+ tsk3{{list}} -- sentinel_products/items --> tsk4{{filter}}
+ tsk4{{filter}} -- filtered_items/sentinel_product --> tsk5{{download}}
+ tsk5{{download}} -- downloaded_product/sentinel1_products --> tsk6{{tile}}
+ tsk6{{tile}} -- tiled_products/rasters --> tsk7{{group}}
+ tsk7{{group}} -- raster_groups/raster_group --> tsk8{{merge}}
+ inp1>user_input] -- time_range --> tsk2{{merge_geom_tr}}
+ inp2>s2_products] -- items --> tsk1{{union}}
+ inp2>s2_products] -- bounds_items --> tsk4{{filter}}
+ inp2>s2_products] -- sentinel2_products --> tsk6{{tile}}
+ tsk8{{merge}} -- merged_product --> out1>raster]
+```
+
+## Sources
+
+- **user_input**: Time range of interest.
+
+- **s2_products**: Sentinel-2 products whose geometries are used to select Sentinel-1 tiles.
+
+## Sinks
+
+- **raster**: Sentinel-1 rasters in the Sentinel-2 tiling system.
+
+## Parameters
+
+- **pc_key**: Planetary Computer API key.
+
+- **min_cover**: Minimum amount of cover required for a group to be used.
+
+- **dl_timeout**: Maximum time, in seconds, before a band reading operation times out.
+
+## Tasks
+
+- **union**: Create item with merged geometry from item list.
+
+- **merge_geom_tr**: Create item that contains the geometry from one item and the time range from another.
+
+- **list**: List Sentinel-1 GRD or RTC products given geometry and time range.
+
+- **filter**: Select items necessary to spatially cover the geometry of the bounds items.
+
+- **download**: Downloads the Sentinel-1 RTC product bands.
+
+- **tile**: Match Sentinel-1 products that intersect with Sentinel-2 tiles.
+
+- **group**: Groups raster files representing the same tile and moment in time that might have been partially generated and split due to the movement of Sentinel-1 through base stations.
+
+- **merge**: Merge items from the same absolute orbit into the appropriate MGRS (Sentinel-2 tiling system) tile.
+
+## Workflow Yaml
+
```yaml
-name: preprocess_s1
+name: preprocess_s1_rtc
sources:
user_input:
- merge_geom_tr.time_range
@@ -15,6 +82,7 @@ sinks:
parameters:
pc_key: null
min_cover: 0.4
+ dl_timeout: null
tasks:
union:
op: merge_geometries
@@ -32,10 +100,10 @@ tasks:
op: download_sentinel1
parameters:
api_key: '@from(pc_key)'
+ timeout_s: '@from(dl_timeout)'
tile:
- op: tile_sentinel1
- preprocess:
- op: apply_sentinel1_snap_processing
+ op: tile_sentinel1_rtc
+ op_dir: tile_sentinel1
group:
op: group_sentinel1_orbits
merge:
@@ -57,9 +125,6 @@ edges:
destination:
- tile.sentinel1_products
- origin: tile.tiled_products
- destination:
- - preprocess.sentinel1_product
-- origin: preprocess.preprocessed_product
destination:
- group.rasters
- origin: group.raster_groups
@@ -81,33 +146,4 @@ description:
pc_key: Planetary Computer API key.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- inp2>s2_products]
- out1>raster]
- tsk1{{union}}
- tsk2{{merge_geom_tr}}
- tsk3{{list}}
- tsk4{{filter}}
- tsk5{{download}}
- tsk6{{tile}}
- tsk7{{preprocess}}
- tsk8{{group}}
- tsk9{{merge}}
- tsk1{{union}} -- merged/geometry --> tsk2{{merge_geom_tr}}
- tsk2{{merge_geom_tr}} -- merged/input_item --> tsk3{{list}}
- tsk3{{list}} -- sentinel_products/items --> tsk4{{filter}}
- tsk4{{filter}} -- filtered_items/sentinel_product --> tsk5{{download}}
- tsk5{{download}} -- downloaded_product/sentinel1_products --> tsk6{{tile}}
- tsk6{{tile}} -- tiled_products/sentinel1_product --> tsk7{{preprocess}}
- tsk7{{preprocess}} -- preprocessed_product/rasters --> tsk8{{group}}
- tsk8{{group}} -- raster_groups/raster_group --> tsk9{{merge}}
- inp1>user_input] -- time_range --> tsk2{{merge_geom_tr}}
- inp2>s2_products] -- items --> tsk1{{union}}
- inp2>s2_products] -- bounds_items --> tsk4{{filter}}
- inp2>s2_products] -- sentinel2_products --> tsk6{{tile}}
- tsk9{{merge}} -- merged_product --> out1>raster]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel1/preprocess_s1_rtc.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel1/preprocess_s1_rtc.md
deleted file mode 100644
index 5f5ece2a..00000000
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel1/preprocess_s1_rtc.md
+++ /dev/null
@@ -1,111 +0,0 @@
-# data_ingestion/sentinel1/preprocess_s1_rtc
-
-```yaml
-
-name: preprocess_s1_rtc
-sources:
- user_input:
- - merge_geom_tr.time_range
- s2_products:
- - union.items
- - filter.bounds_items
- - tile.sentinel2_products
-sinks:
- raster: merge.merged_product
-parameters:
- pc_key: null
- min_cover: 0.4
- dl_timeout: null
-tasks:
- union:
- op: merge_geometries
- merge_geom_tr:
- op: merge_geometry_and_time_range
- list:
- op: list_sentinel1_products_pc
- op_dir: list_sentinel1_products
- parameters:
- collection: rtc
- filter:
- op: select_necessary_coverage_items
- parameters:
- min_cover: '@from(min_cover)'
- group_attribute: orbit_number
- download:
- op: download_sentinel1_rtc
- parameters:
- api_key: '@from(pc_key)'
- timeout_s: '@from(dl_timeout)'
- tile:
- op: tile_sentinel1_rtc
- op_dir: tile_sentinel1
- group:
- op: group_sentinel1_orbits
- merge:
- op: merge_sentinel1_orbits
-edges:
-- origin: union.merged
- destination:
- - merge_geom_tr.geometry
-- origin: merge_geom_tr.merged
- destination:
- - list.input_item
-- origin: list.sentinel_products
- destination:
- - filter.items
-- origin: filter.filtered_items
- destination:
- - download.sentinel_product
-- origin: download.downloaded_product
- destination:
- - tile.sentinel1_products
-- origin: tile.tiled_products
- destination:
- - group.rasters
-- origin: group.raster_groups
- destination:
- - merge.raster_group
-description:
- short_description: Downloads and preprocesses tiles of Sentinel-1 imagery that intersect
- with the input Sentinel-2 products in the input time range.
- long_description: The workflow fetches Sentinel-1 tiles that intersects with the
- Sentinel-2 products, downloads and preprocesses them, and produces Sentinel-1
- rasters in the Sentinel-2 tiling system.
- sources:
- user_input: Time range of interest.
- s2_products: Sentinel-2 products whose geometries are used to select Sentinel-1
- tiles.
- sinks:
- raster: Sentinel-1 rasters in the Sentinel-2 tiling system.
- parameters:
- pc_key: Planetary Computer API key.
-
-
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- inp2>s2_products]
- out1>raster]
- tsk1{{union}}
- tsk2{{merge_geom_tr}}
- tsk3{{list}}
- tsk4{{filter}}
- tsk5{{download}}
- tsk6{{tile}}
- tsk7{{group}}
- tsk8{{merge}}
- tsk1{{union}} -- merged/geometry --> tsk2{{merge_geom_tr}}
- tsk2{{merge_geom_tr}} -- merged/input_item --> tsk3{{list}}
- tsk3{{list}} -- sentinel_products/items --> tsk4{{filter}}
- tsk4{{filter}} -- filtered_items/sentinel_product --> tsk5{{download}}
- tsk5{{download}} -- downloaded_product/sentinel1_products --> tsk6{{tile}}
- tsk6{{tile}} -- tiled_products/rasters --> tsk7{{group}}
- tsk7{{group}} -- raster_groups/raster_group --> tsk8{{merge}}
- inp1>user_input] -- time_range --> tsk2{{merge_geom_tr}}
- inp2>s2_products] -- items --> tsk1{{union}}
- inp2>s2_products] -- bounds_items --> tsk4{{filter}}
- inp2>s2_products] -- sentinel2_products --> tsk6{{tile}}
- tsk8{{merge}} -- merged_product --> out1>raster]
-```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel2/cloud_ensemble.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel2/cloud_ensemble.md
index cbfcd622..33546b57 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel2/cloud_ensemble.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel2/cloud_ensemble.md
@@ -1,5 +1,54 @@
# data_ingestion/sentinel2/cloud_ensemble
+Computes the cloud probability of a Sentinel-2 L2A raster using an ensemble of five cloud segmentation models. The workflow computes cloud probabilities for each model independently, and averages them to obtain a single probability map.
+
+```{mermaid}
+ graph TD
+ inp1>sentinel_raster]
+ out1>cloud_probability]
+ tsk1{{cloud1}}
+ tsk2{{cloud2}}
+ tsk3{{cloud3}}
+ tsk4{{cloud4}}
+ tsk5{{cloud5}}
+ tsk6{{ensemble}}
+ tsk1{{cloud1}} -- cloud_probability/cloud1 --> tsk6{{ensemble}}
+ tsk2{{cloud2}} -- cloud_probability/cloud2 --> tsk6{{ensemble}}
+ tsk3{{cloud3}} -- cloud_probability/cloud3 --> tsk6{{ensemble}}
+ tsk4{{cloud4}} -- cloud_probability/cloud4 --> tsk6{{ensemble}}
+ tsk5{{cloud5}} -- cloud_probability/cloud5 --> tsk6{{ensemble}}
+ inp1>sentinel_raster] -- sentinel_raster --> tsk1{{cloud1}}
+ inp1>sentinel_raster] -- sentinel_raster --> tsk2{{cloud2}}
+ inp1>sentinel_raster] -- sentinel_raster --> tsk3{{cloud3}}
+ inp1>sentinel_raster] -- sentinel_raster --> tsk4{{cloud4}}
+ inp1>sentinel_raster] -- sentinel_raster --> tsk5{{cloud5}}
+ tsk6{{ensemble}} -- cloud_probability --> out1>cloud_probability]
+```
+
+## Sources
+
+- **sentinel_raster**: Sentinel-2 L2A raster.
+
+## Sinks
+
+- **cloud_probability**: Cloud probability map.
+
+## Tasks
+
+- **cloud1**: Computes cloud probabilities using a convolutional segmentation model for L2A.
+
+- **cloud2**: Computes cloud probabilities using a convolutional segmentation model for L2A.
+
+- **cloud3**: Computes cloud probabilities using a convolutional segmentation model for L2A.
+
+- **cloud4**: Computes cloud probabilities using a convolutional segmentation model for L2A.
+
+- **cloud5**: Computes cloud probabilities using a convolutional segmentation model for L2A.
+
+- **ensemble**: Computes ensemble cloud probabilities from all 5 models.
+
+## Workflow Yaml
+
```yaml
name: cloud_ensemble
@@ -62,27 +111,4 @@ description:
cloud_probability: Cloud probability map.
-```
-
-```{mermaid}
- graph TD
- inp1>sentinel_raster]
- out1>cloud_probability]
- tsk1{{cloud1}}
- tsk2{{cloud2}}
- tsk3{{cloud3}}
- tsk4{{cloud4}}
- tsk5{{cloud5}}
- tsk6{{ensemble}}
- tsk1{{cloud1}} -- cloud_probability/cloud1 --> tsk6{{ensemble}}
- tsk2{{cloud2}} -- cloud_probability/cloud2 --> tsk6{{ensemble}}
- tsk3{{cloud3}} -- cloud_probability/cloud3 --> tsk6{{ensemble}}
- tsk4{{cloud4}} -- cloud_probability/cloud4 --> tsk6{{ensemble}}
- tsk5{{cloud5}} -- cloud_probability/cloud5 --> tsk6{{ensemble}}
- inp1>sentinel_raster] -- sentinel_raster --> tsk1{{cloud1}}
- inp1>sentinel_raster] -- sentinel_raster --> tsk2{{cloud2}}
- inp1>sentinel_raster] -- sentinel_raster --> tsk3{{cloud3}}
- inp1>sentinel_raster] -- sentinel_raster --> tsk4{{cloud4}}
- inp1>sentinel_raster] -- sentinel_raster --> tsk5{{cloud5}}
- tsk6{{ensemble}} -- cloud_probability --> out1>cloud_probability]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel2/improve_cloud_mask.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel2/improve_cloud_mask.md
index 08705065..730a0e62 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel2/improve_cloud_mask.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel2/improve_cloud_mask.md
@@ -1,5 +1,55 @@
# data_ingestion/sentinel2/improve_cloud_mask
+Improves cloud masks by merging the product cloud mask with cloud and shadow masks computed by machine learning segmentation models. This workflow computes cloud and shadow probabilities using segmentation models, thresholds them, and merges the models' masks with the product mask.
+
+```{mermaid}
+ graph TD
+ inp1>s2_raster]
+ inp2>product_mask]
+ out1>mask]
+ tsk1{{cloud}}
+ tsk2{{shadow}}
+ tsk3{{merge}}
+ tsk1{{cloud}} -- cloud_probability --> tsk3{{merge}}
+ tsk2{{shadow}} -- shadow_probability --> tsk3{{merge}}
+ inp1>s2_raster] -- sentinel_raster --> tsk1{{cloud}}
+ inp1>s2_raster] -- sentinel_raster --> tsk2{{shadow}}
+ inp2>product_mask] -- product_mask --> tsk3{{merge}}
+ tsk3{{merge}} -- merged_cloud_mask --> out1>mask]
+```
+
+## Sources
+
+- **s2_raster**: Sentinel-2 L2A raster.
+
+- **product_mask**: Cloud mask obtained from the product's quality indicators.
+
+## Sinks
+
+- **mask**: Improved cloud mask.
+
+## Parameters
+
+- **cloud_thr**: Confidence threshold to assign a pixel as cloud.
+
+- **shadow_thr**: Confidence threshold to assign a pixel as shadow.
+
+- **in_memory**: Whether to load the whole raster in memory when running predictions. Uses more memory (~4GB/worker) but speeds up inference for fast models.
+
+- **cloud_model**: ONNX file for the cloud model. Available models are 'cloud_model{idx}_cpu.onnx' with idx ∈ {1, 2} being FPN-based models, which are more accurate but slower, and idx ∈ {3, 4, 5} being cheaplab models, which are less accurate but faster.
+
+- **shadow_model**: ONNX file for the shadow model. 'shadow.onnx' is the only currently available model.
+
+## Tasks
+
+- **cloud**: Computes cloud probabilities using a convolutional segmentation model for L2A.
+
+- **shadow**: Computes shadow probabilities using a convolutional segmentation model for L2A.
+
+- **merge**: Merges cloud, shadow and product cloud masks into a single mask.
+
+## Workflow Yaml
+
```yaml
name: improve_cloud_mask
@@ -64,20 +114,4 @@ description:
available model.
-```
-
-```{mermaid}
- graph TD
- inp1>s2_raster]
- inp2>product_mask]
- out1>mask]
- tsk1{{cloud}}
- tsk2{{shadow}}
- tsk3{{merge}}
- tsk1{{cloud}} -- cloud_probability --> tsk3{{merge}}
- tsk2{{shadow}} -- shadow_probability --> tsk3{{merge}}
- inp1>s2_raster] -- sentinel_raster --> tsk1{{cloud}}
- inp1>s2_raster] -- sentinel_raster --> tsk2{{shadow}}
- inp2>product_mask] -- product_mask --> tsk3{{merge}}
- tsk3{{merge}} -- merged_cloud_mask --> out1>mask]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel2/improve_cloud_mask_ensemble.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel2/improve_cloud_mask_ensemble.md
index 01bebbf9..ea175a74 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel2/improve_cloud_mask_ensemble.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel2/improve_cloud_mask_ensemble.md
@@ -1,5 +1,49 @@
# data_ingestion/sentinel2/improve_cloud_mask_ensemble
+Improves cloud masks by merging the product cloud mask with cloud and shadow masks computed by an ensemble of machine learning segmentation models. This workflow computes cloud and shadow probabilities using and ensemble of segmentation models, thresholds them, and merges the models' masks with the product mask.
+
+```{mermaid}
+ graph TD
+ inp1>s2_raster]
+ inp2>product_mask]
+ out1>mask]
+ tsk1{{cloud}}
+ tsk2{{shadow}}
+ tsk3{{merge}}
+ tsk1{{cloud}} -- cloud_probability --> tsk3{{merge}}
+ tsk2{{shadow}} -- shadow_probability --> tsk3{{merge}}
+ inp1>s2_raster] -- sentinel_raster --> tsk1{{cloud}}
+ inp1>s2_raster] -- sentinel_raster --> tsk2{{shadow}}
+ inp2>product_mask] -- product_mask --> tsk3{{merge}}
+ tsk3{{merge}} -- merged_cloud_mask --> out1>mask]
+```
+
+## Sources
+
+- **s2_raster**: Sentinel-2 L2A raster.
+
+- **product_mask**: Cloud mask obtained from the product's quality indicators.
+
+## Sinks
+
+- **mask**: Improved cloud mask.
+
+## Parameters
+
+- **cloud_thr**: Confidence threshold to assign a pixel as cloud.
+
+- **shadow_thr**: Confidence threshold to assign a pixel as shadow.
+
+## Tasks
+
+- **cloud**: Computes the cloud probability of a Sentinel-2 L2A raster using an ensemble of five cloud segmentation models.
+
+- **shadow**: Computes shadow probabilities using a convolutional segmentation model for L2A.
+
+- **merge**: Merges cloud, shadow and product cloud masks into a single mask.
+
+## Workflow Yaml
+
```yaml
name: improve_cloud_mask_ensemble
@@ -48,20 +92,4 @@ description:
shadow_thr: Confidence threshold to assign a pixel as shadow.
-```
-
-```{mermaid}
- graph TD
- inp1>s2_raster]
- inp2>product_mask]
- out1>mask]
- tsk1{{cloud}}
- tsk2{{shadow}}
- tsk3{{merge}}
- tsk1{{cloud}} -- cloud_probability --> tsk3{{merge}}
- tsk2{{shadow}} -- shadow_probability --> tsk3{{merge}}
- inp1>s2_raster] -- sentinel_raster --> tsk1{{cloud}}
- inp1>s2_raster] -- sentinel_raster --> tsk2{{shadow}}
- inp2>product_mask] -- product_mask --> tsk3{{merge}}
- tsk3{{merge}} -- merged_cloud_mask --> out1>mask]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel2/preprocess_s2.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel2/preprocess_s2.md
index 49010c0a..b7f845fe 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel2/preprocess_s2.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel2/preprocess_s2.md
@@ -1,5 +1,63 @@
# data_ingestion/sentinel2/preprocess_s2
+Downloads and preprocesses Sentinel-2 imagery that covers the input geometry and time range. This workflow selects a minimum set of tiles that covers the input geometry, downloads Sentinel-2 imagery for the selected time range, and preprocesses it by generating a single multi-band raster at 10m resolution.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>raster]
+ out2>mask]
+ tsk1{{list}}
+ tsk2{{filter}}
+ tsk3{{download}}
+ tsk4{{group}}
+ tsk5{{merge}}
+ tsk1{{list}} -- sentinel_products/items --> tsk2{{filter}}
+ tsk2{{filter}} -- filtered_items/sentinel_product --> tsk3{{download}}
+ tsk3{{download}} -- raster/rasters --> tsk4{{group}}
+ tsk3{{download}} -- cloud/masks --> tsk4{{group}}
+ tsk4{{group}} -- raster_groups/raster_group --> tsk5{{merge}}
+ tsk4{{group}} -- mask_groups/mask_group --> tsk5{{merge}}
+ inp1>user_input] -- input_item --> tsk1{{list}}
+ inp1>user_input] -- bounds_items --> tsk2{{filter}}
+ tsk5{{merge}} -- output_raster --> out1>raster]
+ tsk5{{merge}} -- output_mask --> out2>mask]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **raster**: Sentinel-2 L2A rasters with all bands resampled to 10m resolution.
+
+- **mask**: Cloud mask at 10m resolution from the product's quality indicators.
+
+## Parameters
+
+- **min_tile_cover**: Minimum RoI coverage to consider a set of tiles sufficient.
+
+- **max_tiles_per_time**: Maximum number of tiles used to cover the RoI in each date.
+
+- **pc_key**: Optional Planetary Computer API key.
+
+- **dl_timeout**: Maximum time, in seconds, before a band reading operation times out.
+
+## Tasks
+
+- **list**: Lists Sentinel-2 products that intersect with input geometry and time range.
+
+- **filter**: Select items necessary to spatially cover the geometry of the bounds items.
+
+- **download**: Downloads and preprocesses Sentinel-2 products.
+
+- **group**: Groups raster files representing the same tile and moment in time that might have been partially generated and split due to the movement of Sentinel-2 through base stations.
+
+- **merge**: Combines raster files grouped by group_sentinel2_orbits into a single raster.
+
+## Workflow Yaml
+
```yaml
name: preprocess_s2
@@ -69,26 +127,4 @@ description:
pc_key: Optional Planetary Computer API key.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>raster]
- out2>mask]
- tsk1{{list}}
- tsk2{{filter}}
- tsk3{{download}}
- tsk4{{group}}
- tsk5{{merge}}
- tsk1{{list}} -- sentinel_products/items --> tsk2{{filter}}
- tsk2{{filter}} -- filtered_items/sentinel_product --> tsk3{{download}}
- tsk3{{download}} -- raster/rasters --> tsk4{{group}}
- tsk3{{download}} -- cloud/masks --> tsk4{{group}}
- tsk4{{group}} -- raster_groups/raster_group --> tsk5{{merge}}
- tsk4{{group}} -- mask_groups/mask_group --> tsk5{{merge}}
- inp1>user_input] -- input_item --> tsk1{{list}}
- inp1>user_input] -- bounds_items --> tsk2{{filter}}
- tsk5{{merge}} -- output_raster --> out1>raster]
- tsk5{{merge}} -- output_mask --> out2>mask]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel2/preprocess_s2_ensemble_masks.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel2/preprocess_s2_ensemble_masks.md
index 133ee6f6..1b774adb 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel2/preprocess_s2_ensemble_masks.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel2/preprocess_s2_ensemble_masks.md
@@ -1,5 +1,51 @@
# data_ingestion/sentinel2/preprocess_s2_ensemble_masks
+Downloads and preprocesses Sentinel-2 imagery that covers the input geometry and time range, and computes improved cloud masks using an ensemble of cloud and shadow segmentation models. This workflow selects a minimum set of tiles that covers the input geometry, downloads Sentinel-2 imagery for the selected time range, and preprocesses it by generating a single multi-band raster at 10m resolution. It then improves cloud masks by merging the product mask with cloud and shadow masks computed using an ensemble of cloud and shadow segmentation models.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>raster]
+ out2>mask]
+ tsk1{{s2}}
+ tsk2{{cloud}}
+ tsk1{{s2}} -- raster/s2_raster --> tsk2{{cloud}}
+ tsk1{{s2}} -- mask/product_mask --> tsk2{{cloud}}
+ inp1>user_input] -- user_input --> tsk1{{s2}}
+ tsk1{{s2}} -- raster --> out1>raster]
+ tsk2{{cloud}} -- mask --> out2>mask]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **raster**: Sentinel-2 L2A rasters with all bands resampled to 10m resolution.
+
+- **mask**: Cloud masks at 10m resolution.
+
+## Parameters
+
+- **min_tile_cover**: Minimum RoI coverage to consider a set of tiles sufficient.
+
+- **max_tiles_per_time**: Maximum number of tiles used to cover the RoI in each date.
+
+- **cloud_thr**: Confidence threshold to assign a pixel as cloud.
+
+- **shadow_thr**: Confidence threshold to assign a pixel as shadow.
+
+- **pc_key**: Optional Planetary Computer API key.
+
+## Tasks
+
+- **s2**: Downloads and preprocesses Sentinel-2 imagery that covers the input geometry and time range.
+
+- **cloud**: Improves cloud masks by merging the product cloud mask with cloud and shadow masks computed by an ensemble of machine learning segmentation models.
+
+## Workflow Yaml
+
```yaml
name: preprocess_s2_ensemble_masks
@@ -50,18 +96,4 @@ description:
mask: Cloud masks at 10m resolution.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>raster]
- out2>mask]
- tsk1{{s2}}
- tsk2{{cloud}}
- tsk1{{s2}} -- raster/s2_raster --> tsk2{{cloud}}
- tsk1{{s2}} -- mask/product_mask --> tsk2{{cloud}}
- inp1>user_input] -- user_input --> tsk1{{s2}}
- tsk1{{s2}} -- raster --> out1>raster]
- tsk2{{cloud}} -- mask --> out2>mask]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel2/preprocess_s2_improved_masks.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel2/preprocess_s2_improved_masks.md
index d69b1e58..5f9fa237 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel2/preprocess_s2_improved_masks.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/sentinel2/preprocess_s2_improved_masks.md
@@ -1,5 +1,59 @@
# data_ingestion/sentinel2/preprocess_s2_improved_masks
+Downloads and preprocesses Sentinel-2 imagery that covers the input geometry and time range, and computes improved cloud masks using cloud and shadow segmentation models. This workflow selects a minimum set of tiles that covers the input geometry, downloads Sentinel-2 imagery for the selected time range, and preprocesses it by generating a single multi-band raster at 10m resolution. It then improves cloud masks by merging the product mask with cloud and shadow masks computed using cloud and shadow segmentation models.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>raster]
+ out2>mask]
+ tsk1{{s2}}
+ tsk2{{cloud}}
+ tsk1{{s2}} -- raster/s2_raster --> tsk2{{cloud}}
+ tsk1{{s2}} -- mask/product_mask --> tsk2{{cloud}}
+ inp1>user_input] -- user_input --> tsk1{{s2}}
+ tsk1{{s2}} -- raster --> out1>raster]
+ tsk2{{cloud}} -- mask --> out2>mask]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **raster**: Sentinel-2 L2A rasters with all bands resampled to 10m resolution.
+
+- **mask**: Cloud masks at 10m resolution.
+
+## Parameters
+
+- **min_tile_cover**: Minimum RoI coverage to consider a set of tiles sufficient.
+
+- **max_tiles_per_time**: Maximum number of tiles used to cover the RoI in each date.
+
+- **cloud_thr**: Confidence threshold to assign a pixel as cloud.
+
+- **shadow_thr**: Confidence threshold to assign a pixel as shadow.
+
+- **in_memory**: Whether to load the whole raster in memory when running predictions. Uses more memory (~4GB/worker) but speeds up inference for fast models.
+
+- **cloud_model**: ONNX file for the cloud model. Available models are 'cloud_model{idx}_cpu.onnx' with idx ∈ {1, 2} being FPN-based models, which are more accurate but slower, and idx ∈ {3, 4, 5} being cheaplab models, which are less accurate but faster.
+
+- **shadow_model**: ONNX file for the shadow model. 'shadow.onnx' is the only currently available model.
+
+- **pc_key**: Optional Planetary Computer API key.
+
+- **dl_timeout**: Maximum time, in seconds, before a band reading operation times out.
+
+## Tasks
+
+- **s2**: Downloads and preprocesses Sentinel-2 imagery that covers the input geometry and time range.
+
+- **cloud**: Improves cloud masks by merging the product cloud mask with cloud and shadow masks computed by machine learning segmentation models.
+
+## Workflow Yaml
+
```yaml
name: preprocess_s2_improved_masks
@@ -58,18 +112,4 @@ description:
mask: Cloud masks at 10m resolution.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>raster]
- out2>mask]
- tsk1{{s2}}
- tsk2{{cloud}}
- tsk1{{s2}} -- raster/s2_raster --> tsk2{{cloud}}
- tsk1{{s2}} -- mask/product_mask --> tsk2{{cloud}}
- inp1>user_input] -- user_input --> tsk1{{s2}}
- tsk1{{s2}} -- raster --> out1>raster]
- tsk2{{cloud}} -- mask --> out2>mask]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/soil/soilgrids.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/soil/soilgrids.md
index 9eccbf63..cd4f96df 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/soil/soilgrids.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/soil/soilgrids.md
@@ -1,5 +1,51 @@
# data_ingestion/soil/soilgrids
+Downloads digital soil mapping information from SoilGrids for the input geometry. The workflow downloads a raster containing the map and identifiers for the input geometry. SoilGrids is a system for digital soil mapping based on global compilation of soil profile data and environmental layers.
+
+```{mermaid}
+ graph TD
+ inp1>input_item]
+ out1>downloaded_raster]
+ tsk1{{download_soilgrids}}
+ inp1>input_item] -- input_item --> tsk1{{download_soilgrids}}
+ tsk1{{download_soilgrids}} -- downloaded_raster --> out1>downloaded_raster]
+```
+
+## Sources
+
+- **input_item**: Input geometry.
+
+## Sinks
+
+- **downloaded_raster**: Raster with the map and identifiers requested.
+
+## Parameters
+
+- **map**: Map to download. Options:
+ - wrb - World Reference Base classes and probabilites
+ - bdod - Bulk density - kg/dm^3
+ - cec - Cation exchange capacity at ph 7 - cmol(c)/kg
+ - cfvo - Coarse fragments volumetric) - cm3/100cm3 (vol%)
+ - clay - Clay content - g/100g (%)
+ - nitrogen - Nitrogen - g/kg
+ - phh2o - Soil pH in H2O - pH
+ - sand - Sand content - g/100g (%)
+ - silt - Silt content - g/100g (%)
+ - soc - Soil organic carbon content - g/kg
+ - ocs - Soil organic carbon stock - kg/m^3
+ - ocd - Organic carbon densities - kg/m^3
+
+- **identifier**: Variable identifier to be downloaded. Depends on map.
+ - wrb: Acrisols, Albeluvisols, Alisols, Andosols, Arenosols, Calcisols, Cambisols,
+Chernozems, Cryosols, Durisols, Ferralsols, Fluvisols, Gleysols, Gypsisols, Histosols, Kastanozems, Leptosols, Lixisols, Luvisols, MostProbable, Nitisols, Phaeozems, Planosols, Plinthosols, Podzols, Regosols, Solonchaks, Solonetz, Stagnosols, Umbrisols, Vertisols.
+Other identifiers follow the nomenclature defined in the [link=https://www.isric.org/explore/soilgrids/faq-soilgrids#What_do_the_filename_codes_mean]SoilGrids documentation page: https://www.isric.org/explore/soilgrids/faq-soilgrids#What_do_the_filename_codes_mean[/].
+
+## Tasks
+
+- **download_soilgrids**: Downloads digital soil mapping information from SoilGrids for the input geometry.
+
+## Workflow Yaml
+
```yaml
name: soilgrids
@@ -47,13 +93,4 @@ description:
\ documentation page: https://www.isric.org/explore/soilgrids/faq-soilgrids#What_do_the_filename_codes_mean[/]."
-```
-
-```{mermaid}
- graph TD
- inp1>input_item]
- out1>downloaded_raster]
- tsk1{{download_soilgrids}}
- inp1>input_item] -- input_item --> tsk1{{download_soilgrids}}
- tsk1{{download_soilgrids}} -- downloaded_raster --> out1>downloaded_raster]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/soil/usda.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/soil/usda.md
index 7362b8a6..6f7c35f5 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/soil/usda.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/soil/usda.md
@@ -1,5 +1,38 @@
# data_ingestion/soil/usda
+Downloads USDA soil classification raster. The workflow will download a global raster with USDA soil classes at 1/30 degree resolution.
+
+```{mermaid}
+ graph TD
+ inp1>input_item]
+ out1>downloaded_raster]
+ tsk1{{datavibe_filter}}
+ tsk2{{download_usda_soils}}
+ tsk1{{datavibe_filter}} -- output_item/input_item --> tsk2{{download_usda_soils}}
+ inp1>input_item] -- input_item --> tsk1{{datavibe_filter}}
+ tsk2{{download_usda_soils}} -- downloaded_raster --> out1>downloaded_raster]
+```
+
+## Sources
+
+- **input_item**: Dummy input.
+
+## Sinks
+
+- **downloaded_raster**: Raster with USDA soil classes.
+
+## Parameters
+
+- **ignore**: Selection of each field of input item should be ignored (among "time_range", "geometry", or "all" for both of them).
+
+## Tasks
+
+- **datavibe_filter**: Filters out time range and/or geometry information from the input item.
+
+- **download_usda_soils**: Downloads a global raster with USDA soil classes at 1/30 degree resolution.
+
+## Workflow Yaml
+
```yaml
name: usda_soils
@@ -34,15 +67,4 @@ description:
"geometry", or "all" for both of them).
-```
-
-```{mermaid}
- graph TD
- inp1>input_item]
- out1>downloaded_raster]
- tsk1{{datavibe_filter}}
- tsk2{{download_usda_soils}}
- tsk1{{datavibe_filter}} -- output_item/input_item --> tsk2{{download_usda_soils}}
- inp1>input_item] -- input_item --> tsk1{{datavibe_filter}}
- tsk2{{download_usda_soils}} -- downloaded_raster --> out1>downloaded_raster]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/spaceeye/spaceeye.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/spaceeye/spaceeye.md
index 22cdbd87..4dbda7d0 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/spaceeye/spaceeye.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/spaceeye/spaceeye.md
@@ -1,5 +1,55 @@
# data_ingestion/spaceeye/spaceeye
+Runs the SpaceEye cloud removal pipeline, yielding daily cloud-free images for the input geometry and time range. The workflow fetches both Sentinel-1 and Sentinel-2 tiles that cover the input geometry and time range, preprocesses them, computes cloud masks, and runs SpaceEye inference in a sliding window on the retrieved tiles. This workflow can be reused as a preprocess step in many applications that require cloud-free Sentinel-2 data. For more information about SpaceEye, read the paper: https://arxiv.org/abs/2106.08408.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>raster]
+ tsk1{{preprocess}}
+ tsk2{{spaceeye}}
+ tsk1{{preprocess}} -- s2_raster/s2_rasters --> tsk2{{spaceeye}}
+ tsk1{{preprocess}} -- s1_raster/s1_rasters --> tsk2{{spaceeye}}
+ tsk1{{preprocess}} -- cloud_mask/cloud_rasters --> tsk2{{spaceeye}}
+ inp1>user_input] -- user_input --> tsk1{{preprocess}}
+ inp1>user_input] -- input_data --> tsk2{{spaceeye}}
+ tsk2{{spaceeye}} -- raster --> out1>raster]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **raster**: Cloud-free rasters.
+
+## Parameters
+
+- **duration**: Time window, in days, considered in the inference. Controls the amount of temporal context for inpainting clouds. Larger windows require more compute and memory.
+
+- **time_overlap**: Overlap ratio of each temporal window. Controls the temporal step between windows as a fraction of the window size.
+
+- **min_tile_cover**: Minimum RoI coverage to consider a set of tiles sufficient.
+
+- **max_tiles_per_time**: Maximum number of tiles used to cover the RoI in each date.
+
+- **cloud_thr**: Confidence threshold to assign a pixel as cloud.
+
+- **shadow_thr**: Confidence threshold to assign a pixel as shadow.
+
+- **pc_key**: Optional Planetary Computer API key.
+
+- **s2_timeout**: Maximum time, in seconds, before a band reading operation times out.
+
+## Tasks
+
+- **preprocess**: Runs the SpaceEye preprocessing pipeline.
+
+- **spaceeye**: Performs SpaceEye inference to generate daily cloud-free images given Sentinel data and cloud masks.
+
+## Workflow Yaml
+
```yaml
name: spaceeye
@@ -58,18 +108,4 @@ description:
parameters: null
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>raster]
- tsk1{{preprocess}}
- tsk2{{spaceeye}}
- tsk1{{preprocess}} -- s2_raster/s2_rasters --> tsk2{{spaceeye}}
- tsk1{{preprocess}} -- s1_raster/s1_rasters --> tsk2{{spaceeye}}
- tsk1{{preprocess}} -- cloud_mask/cloud_rasters --> tsk2{{spaceeye}}
- inp1>user_input] -- user_input --> tsk1{{preprocess}}
- inp1>user_input] -- input_data --> tsk2{{spaceeye}}
- tsk2{{spaceeye}} -- raster --> out1>raster]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/spaceeye/spaceeye_inference.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/spaceeye/spaceeye_inference.md
index 53995783..20309895 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/spaceeye/spaceeye_inference.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/spaceeye/spaceeye_inference.md
@@ -1,5 +1,66 @@
# data_ingestion/spaceeye/spaceeye_inference
+Performs SpaceEye inference to generate daily cloud-free images given Sentinel data and cloud masks. The workflow will group input Sentinel-1, Sentinel-2, and cloud mask rasters into spatio-temporal windows and perform inference of each window. The windows will then be merged into rasters for the RoI. More information about SpaceEye available in the paper: https://arxiv.org/abs/2106.08408.
+
+```{mermaid}
+ graph TD
+ inp1>input_data]
+ inp2>s1_rasters]
+ inp3>s2_rasters]
+ inp4>cloud_rasters]
+ out1>raster]
+ tsk1{{group_s1}}
+ tsk2{{group_s2}}
+ tsk3{{group_mask}}
+ tsk4{{spaceeye}}
+ tsk5{{split}}
+ tsk1{{group_s1}} -- tile_sequences/s1_products --> tsk4{{spaceeye}}
+ tsk2{{group_s2}} -- tile_sequences/s2_products --> tsk4{{spaceeye}}
+ tsk3{{group_mask}} -- tile_sequences/cloud_masks --> tsk4{{spaceeye}}
+ tsk4{{spaceeye}} -- spaceeye_sequence/sequences --> tsk5{{split}}
+ inp1>input_data] -- input_data --> tsk1{{group_s1}}
+ inp1>input_data] -- input_data --> tsk2{{group_s2}}
+ inp1>input_data] -- input_data --> tsk3{{group_mask}}
+ inp2>s1_rasters] -- rasters --> tsk1{{group_s1}}
+ inp3>s2_rasters] -- rasters --> tsk2{{group_s2}}
+ inp4>cloud_rasters] -- rasters --> tsk3{{group_mask}}
+ tsk5{{split}} -- rasters --> out1>raster]
+```
+
+## Sources
+
+- **input_data**: Time range and region of interest. Will determine the spatio-temporal windows and region for the output rasters.
+
+- **s1_rasters**: Sentinel-1 rasters tiled to the Sentinel-2 grid.
+
+- **s2_rasters**: Sentinel-2 tile rasters for the input time range.
+
+- **cloud_rasters**: Cloud masks for each of the Sentinel-2 tiles.
+
+## Sinks
+
+- **raster**: Cloud-free rasters for the input time range and region of interest.
+
+## Parameters
+
+- **duration**: Time window, in days, considered in the inference. Controls the amount of temporal context for inpainting clouds. Larger windows require more compute and memory.
+
+- **time_overlap**: Overlap ratio of each temporal window. Controls the temporal step between windows as a fraction of the window size.
+
+## Tasks
+
+- **group_s1**: Groups Sentinel-1 tiles into time windows of defined duration.
+
+- **group_s2**: Groups Sentinel-2 tiles into time windows of defined duration.
+
+- **group_mask**: Groups Sentinel-2 cloud masks into time windows of defined duration.
+
+- **spaceeye**: Runs SpaceEye to remove clouds in input rasters.
+
+- **split**: Splits a list of multiple TileSequence back to a list of Rasters.
+
+## Workflow Yaml
+
```yaml
name: spaceeye_inference
@@ -81,29 +142,4 @@ description:
between windows as a fraction of the window size.
-```
-
-```{mermaid}
- graph TD
- inp1>input_data]
- inp2>s1_rasters]
- inp3>s2_rasters]
- inp4>cloud_rasters]
- out1>raster]
- tsk1{{group_s1}}
- tsk2{{group_s2}}
- tsk3{{group_mask}}
- tsk4{{spaceeye}}
- tsk5{{split}}
- tsk1{{group_s1}} -- tile_sequences/s1_products --> tsk4{{spaceeye}}
- tsk2{{group_s2}} -- tile_sequences/s2_products --> tsk4{{spaceeye}}
- tsk3{{group_mask}} -- tile_sequences/cloud_masks --> tsk4{{spaceeye}}
- tsk4{{spaceeye}} -- spaceeye_sequence/sequences --> tsk5{{split}}
- inp1>input_data] -- input_data --> tsk1{{group_s1}}
- inp1>input_data] -- input_data --> tsk2{{group_s2}}
- inp1>input_data] -- input_data --> tsk3{{group_mask}}
- inp2>s1_rasters] -- rasters --> tsk1{{group_s1}}
- inp3>s2_rasters] -- rasters --> tsk2{{group_s2}}
- inp4>cloud_rasters] -- rasters --> tsk3{{group_mask}}
- tsk5{{split}} -- rasters --> out1>raster]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/spaceeye/spaceeye_interpolation.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/spaceeye/spaceeye_interpolation.md
index 38140361..33772c17 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/spaceeye/spaceeye_interpolation.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/spaceeye/spaceeye_interpolation.md
@@ -1,5 +1,52 @@
# data_ingestion/spaceeye/spaceeye_interpolation
+Runs the SpaceEye cloud removal pipeline using an interpolation-based algorithm, yielding daily cloud-free images for the input geometry and time range. The workflow fetches Sentinel-2 tiles that cover the input geometry and time range, preprocesses them, computes cloud masks, and runs SpaceEye inference in a sliding window on the retrieved tiles. This workflow can be reused as a preprocess step in many applications that require cloud-free Sentinel-2 data. For more information about SpaceEye, read the [link=https://arxiv.org/abs/2106.08408]paper: https://arxiv.org/abs/2106.08408[/link].
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>raster]
+ tsk1{{preprocess}}
+ tsk2{{spaceeye}}
+ tsk1{{preprocess}} -- raster/s2_rasters --> tsk2{{spaceeye}}
+ tsk1{{preprocess}} -- mask/cloud_rasters --> tsk2{{spaceeye}}
+ inp1>user_input] -- user_input --> tsk1{{preprocess}}
+ inp1>user_input] -- input_data --> tsk2{{spaceeye}}
+ tsk2{{spaceeye}} -- raster --> out1>raster]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **raster**: Cloud-free rasters.
+
+## Parameters
+
+- **duration**: Time window, in days, considered in the inference. Controls the amount of temporal context for inpainting clouds. Larger windows require more compute and memory.
+
+- **time_overlap**: Overlap ratio of each temporal window. Controls the temporal step between windows as a fraction of the window size.
+
+- **min_tile_cover**: Minimum RoI coverage to consider a set of tiles sufficient.
+
+- **max_tiles_per_time**: Maximum number of tiles used to cover the RoI in each date.
+
+- **cloud_thr**: Confidence threshold to assign a pixel as cloud.
+
+- **shadow_thr**: Confidence threshold to assign a pixel as shadow.
+
+- **pc_key**: Optional Planetary Computer API key.
+
+## Tasks
+
+- **preprocess**: Downloads and preprocesses Sentinel-2 imagery that covers the input geometry and time range, and computes improved cloud masks using cloud and shadow segmentation models.
+
+- **spaceeye**: Performs temporal damped interpolation to generate daily cloud-free images given Sentinel-2 data and cloud masks.
+
+## Workflow Yaml
+
```yaml
name: spaceeye_interpolation
@@ -63,17 +110,4 @@ description:
pc_key: Optional Planetary Computer API key.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>raster]
- tsk1{{preprocess}}
- tsk2{{spaceeye}}
- tsk1{{preprocess}} -- raster/s2_rasters --> tsk2{{spaceeye}}
- tsk1{{preprocess}} -- mask/cloud_rasters --> tsk2{{spaceeye}}
- inp1>user_input] -- user_input --> tsk1{{preprocess}}
- inp1>user_input] -- input_data --> tsk2{{spaceeye}}
- tsk2{{spaceeye}} -- raster --> out1>raster]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/spaceeye/spaceeye_interpolation_inference.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/spaceeye/spaceeye_interpolation_inference.md
index 07679d06..1bd24935 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/spaceeye/spaceeye_interpolation_inference.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/spaceeye/spaceeye_interpolation_inference.md
@@ -1,5 +1,57 @@
# data_ingestion/spaceeye/spaceeye_interpolation_inference
+Performs temporal damped interpolation to generate daily cloud-free images given Sentinel-2 data and cloud masks. The workflow will group input Sentinel-2 and cloud mask rasters into spatio-temporal windows and perform inference of each window. The windows will then be merged into rasters for the RoI. More information about SpaceEye available in the paper: https://arxiv.org/abs/2106.08408.
+
+```{mermaid}
+ graph TD
+ inp1>input_data]
+ inp2>s2_rasters]
+ inp3>cloud_rasters]
+ out1>raster]
+ tsk1{{group_s2}}
+ tsk2{{group_mask}}
+ tsk3{{spaceeye}}
+ tsk4{{split}}
+ tsk1{{group_s2}} -- tile_sequences/s2_products --> tsk3{{spaceeye}}
+ tsk2{{group_mask}} -- tile_sequences/cloud_masks --> tsk3{{spaceeye}}
+ tsk3{{spaceeye}} -- spaceeye_sequence/sequences --> tsk4{{split}}
+ inp1>input_data] -- input_data --> tsk1{{group_s2}}
+ inp1>input_data] -- input_data --> tsk2{{group_mask}}
+ inp2>s2_rasters] -- rasters --> tsk1{{group_s2}}
+ inp3>cloud_rasters] -- rasters --> tsk2{{group_mask}}
+ tsk4{{split}} -- rasters --> out1>raster]
+```
+
+## Sources
+
+- **input_data**: Time range and region of interest. Will determine the spatio-temporal windows and region for the output rasters.
+
+- **s2_rasters**: Sentinel-2 tile rasters for the input time range.
+
+- **cloud_rasters**: Cloud masks for each of the Sentinel-2 tiles.
+
+## Sinks
+
+- **raster**: Cloud-free rasters for the input time range and region of interest.
+
+## Parameters
+
+- **duration**: Time window, in days, considered in the inference. Controls the amount of temporal context for inpainting clouds. Larger windows require more compute and memory.
+
+- **time_overlap**: Overlap ratio of each temporal window. Controls the temporal step between windows as a fraction of the window size.
+
+## Tasks
+
+- **group_s2**: Groups Sentinel-2 tiles into time windows of defined duration.
+
+- **group_mask**: Groups Sentinel-2 cloud masks into time windows of defined duration.
+
+- **spaceeye**: Runs the interpolation version of SpaceEye to remove clouds in input rasters.
+
+- **split**: Splits a list of multiple TileSequence back to a list of Rasters.
+
+## Workflow Yaml
+
```yaml
name: spaceeye_interpolation_inference
@@ -69,24 +121,4 @@ description:
between windows as a fraction of the window size.
-```
-
-```{mermaid}
- graph TD
- inp1>input_data]
- inp2>s2_rasters]
- inp3>cloud_rasters]
- out1>raster]
- tsk1{{group_s2}}
- tsk2{{group_mask}}
- tsk3{{spaceeye}}
- tsk4{{split}}
- tsk1{{group_s2}} -- tile_sequences/s2_products --> tsk3{{spaceeye}}
- tsk2{{group_mask}} -- tile_sequences/cloud_masks --> tsk3{{spaceeye}}
- tsk3{{spaceeye}} -- spaceeye_sequence/sequences --> tsk4{{split}}
- inp1>input_data] -- input_data --> tsk1{{group_s2}}
- inp1>input_data] -- input_data --> tsk2{{group_mask}}
- inp2>s2_rasters] -- rasters --> tsk1{{group_s2}}
- inp3>cloud_rasters] -- rasters --> tsk2{{group_mask}}
- tsk4{{split}} -- rasters --> out1>raster]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/spaceeye/spaceeye_preprocess.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/spaceeye/spaceeye_preprocess.md
index 88888c2c..c0a9da93 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/spaceeye/spaceeye_preprocess.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/spaceeye/spaceeye_preprocess.md
@@ -1,8 +1,62 @@
# data_ingestion/spaceeye/spaceeye_preprocess
+Runs the SpaceEye preprocessing pipeline. The workflow fetches both Sentinel-1 and Sentinel-2 tiles that cover the input geometry and time range and preprocesses them. It also computes improved cloud masks using cloud and shadow segmentation models.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>s2_raster]
+ out2>s1_raster]
+ out3>cloud_mask]
+ tsk1{{s2}}
+ tsk2{{s1}}
+ tsk1{{s2}} -- raster/s2_products --> tsk2{{s1}}
+ inp1>user_input] -- user_input --> tsk1{{s2}}
+ inp1>user_input] -- user_input --> tsk2{{s1}}
+ tsk1{{s2}} -- raster --> out1>s2_raster]
+ tsk2{{s1}} -- raster --> out2>s1_raster]
+ tsk1{{s2}} -- mask --> out3>cloud_mask]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **s2_raster**: Sentinel-2 rasters.
+
+- **s1_raster**: Sentinel-1 rasters.
+
+- **cloud_mask**: Cloud and cloud shadow mask.
+
+## Parameters
+
+- **min_tile_cover**: Minimum RoI coverage to consider a set of tiles sufficient.
+
+- **max_tiles_per_time**: Maximum number of tiles used to cover the RoI in each date.
+
+- **cloud_thr**: Confidence threshold to assign a pixel as cloud.
+
+- **shadow_thr**: Confidence threshold to assign a pixel as shadow.
+
+- **pc_key**: Optional Planetary Computer API key.
+
+- **s1_timeout**: Maximum time, in seconds, before a band reading operation times out.
+
+- **s2_timeout**: Maximum time, in seconds, before a band reading operation times out.
+
+## Tasks
+
+- **s2**: Downloads and preprocesses Sentinel-2 imagery that covers the input geometry and time range, and computes improved cloud masks using cloud and shadow segmentation models.
+
+- **s1**: Downloads and preprocesses tiles of Sentinel-1 imagery that intersect with the input Sentinel-2 products in the input time range.
+
+## Workflow Yaml
+
```yaml
-name: spaceeye_preprocess
+name: spaceeye_preprocess_rtc
sources:
user_input:
- s2.user_input
@@ -12,11 +66,12 @@ sinks:
s1_raster: s1.raster
cloud_mask: s2.mask
parameters:
- min_tile_cover: null
+ min_tile_cover: 0.4
max_tiles_per_time: null
cloud_thr: null
shadow_thr: null
- pc_key: null
+ pc_key: '@SECRET(eywa-secrets, pc-sub-key)'
+ s1_timeout: null
s2_timeout: null
tasks:
s2:
@@ -27,9 +82,13 @@ tasks:
cloud_thr: '@from(cloud_thr)'
shadow_thr: '@from(shadow_thr)'
pc_key: '@from(pc_key)'
+ in_memory: true
dl_timeout: '@from(s2_timeout)'
s1:
workflow: data_ingestion/sentinel1/preprocess_s1
+ parameters:
+ pc_key: '@from(pc_key)'
+ dl_timeout: '@from(s1_timeout)'
edges:
- origin: s2.raster
destination:
@@ -47,20 +106,4 @@ description:
cloud_mask: Cloud and cloud shadow mask.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>s2_raster]
- out2>s1_raster]
- out3>cloud_mask]
- tsk1{{s2}}
- tsk2{{s1}}
- tsk1{{s2}} -- raster/s2_products --> tsk2{{s1}}
- inp1>user_input] -- user_input --> tsk1{{s2}}
- inp1>user_input] -- user_input --> tsk2{{s1}}
- tsk1{{s2}} -- raster --> out1>s2_raster]
- tsk2{{s1}} -- raster --> out2>s1_raster]
- tsk1{{s2}} -- mask --> out3>cloud_mask]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/spaceeye/spaceeye_preprocess_ensemble.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/spaceeye/spaceeye_preprocess_ensemble.md
index 11bd46a0..7fcfa842 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/spaceeye/spaceeye_preprocess_ensemble.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/spaceeye/spaceeye_preprocess_ensemble.md
@@ -1,5 +1,47 @@
# data_ingestion/spaceeye/spaceeye_preprocess_ensemble
+Runs the SpaceEye preprocessing pipeline with an ensemble of cloud segmentation models. The workflow fetches both Sentinel-1 and Sentinel-2 tiles that cover the input geometry and time range and preprocesses them, it also computes improved cloud masks using cloud and shadow segmentation models. Cloud probabilities are computed with an ensemble of five models.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>s2_raster]
+ out2>s1_raster]
+ out3>cloud_mask]
+ tsk1{{s2}}
+ tsk2{{s1}}
+ tsk1{{s2}} -- raster/s2_products --> tsk2{{s1}}
+ inp1>user_input] -- user_input --> tsk1{{s2}}
+ inp1>user_input] -- user_input --> tsk2{{s1}}
+ tsk1{{s2}} -- raster --> out1>s2_raster]
+ tsk2{{s1}} -- raster --> out2>s1_raster]
+ tsk1{{s2}} -- mask --> out3>cloud_mask]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **s2_raster**: Sentinel-2 rasters.
+
+- **s1_raster**: Sentinel-1 rasters.
+
+- **cloud_mask**: Cloud and cloud shadow mask.
+
+## Parameters
+
+- **pc_key**: Optional Planetary Computer API key.
+
+## Tasks
+
+- **s2**: Downloads and preprocesses Sentinel-2 imagery that covers the input geometry and time range, and computes improved cloud masks using an ensemble of cloud and shadow segmentation models.
+
+- **s1**: Downloads and preprocesses tiles of Sentinel-1 imagery that intersect with the input Sentinel-2 products in the input time range.
+
+## Workflow Yaml
+
```yaml
name: spaceeye_preprocess_ensemble
@@ -12,7 +54,7 @@ sinks:
s1_raster: s1.raster
cloud_mask: s2.mask
parameters:
- pc_key: null
+ pc_key: '@SECRET(eywa-secrets, pc-sub-key)'
tasks:
s2:
workflow: data_ingestion/sentinel2/preprocess_s2_ensemble_masks
@@ -43,20 +85,4 @@ description:
pc_key: Optional Planetary Computer API key.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>s2_raster]
- out2>s1_raster]
- out3>cloud_mask]
- tsk1{{s2}}
- tsk2{{s1}}
- tsk1{{s2}} -- raster/s2_products --> tsk2{{s1}}
- inp1>user_input] -- user_input --> tsk1{{s2}}
- inp1>user_input] -- user_input --> tsk2{{s1}}
- tsk1{{s2}} -- raster --> out1>s2_raster]
- tsk2{{s1}} -- raster --> out2>s1_raster]
- tsk1{{s2}} -- mask --> out3>cloud_mask]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/user_data/ingest_geometry.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/user_data/ingest_geometry.md
index 1f54f5f3..7a80108a 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/user_data/ingest_geometry.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/user_data/ingest_geometry.md
@@ -1,5 +1,34 @@
# data_ingestion/user_data/ingest_geometry
+Adds user geometries into the cluster storage, allowing for them to be used on workflows. The workflow downloads geometries provided in the references and generates GeometryCollection objects with local assets that can be used in other operations.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>geometry]
+ tsk1{{unpack}}
+ tsk2{{download}}
+ tsk1{{unpack}} -- ref_list/input_ref --> tsk2{{download}}
+ inp1>user_input] -- input_refs --> tsk1{{unpack}}
+ tsk2{{download}} -- downloaded --> out1>geometry]
+```
+
+## Sources
+
+- **user_input**: List of external references.
+
+## Sinks
+
+- **geometry**: GeometryCollections with downloaded assets.
+
+## Tasks
+
+- **unpack**: Unpacks the urls from the list of external references.
+
+- **download**: Downloads geometries provided in the reference and generates a GeometryCollection.
+
+## Workflow Yaml
+
```yaml
name: ingest_geometry
@@ -30,15 +59,4 @@ description:
geometry: GeometryCollections with downloaded assets.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>geometry]
- tsk1{{unpack}}
- tsk2{{download}}
- tsk1{{unpack}} -- ref_list/input_ref --> tsk2{{download}}
- inp1>user_input] -- input_refs --> tsk1{{unpack}}
- tsk2{{download}} -- downloaded --> out1>geometry]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/user_data/ingest_raster.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/user_data/ingest_raster.md
index 673bde0c..8d0274bb 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/user_data/ingest_raster.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/user_data/ingest_raster.md
@@ -1,5 +1,34 @@
# data_ingestion/user_data/ingest_raster
+Adds user rasters into the cluster storage, allowing for them to be used on workflows. The workflow downloads rasters provided in the references and generates Raster objects with local assets that can be used in other operations.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>raster]
+ tsk1{{unpack}}
+ tsk2{{download}}
+ tsk1{{unpack}} -- ref_list/input_ref --> tsk2{{download}}
+ inp1>user_input] -- input_refs --> tsk1{{unpack}}
+ tsk2{{download}} -- downloaded --> out1>raster]
+```
+
+## Sources
+
+- **user_input**: List of external references.
+
+## Sinks
+
+- **raster**: Rasters with downloaded assets.
+
+## Tasks
+
+- **unpack**: Unpacks the urls from the list of external references.
+
+- **download**: Downloads the raster from the input reference's url.
+
+## Workflow Yaml
+
```yaml
name: ingest_raster
@@ -29,15 +58,4 @@ description:
raster: Rasters with downloaded assets.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>raster]
- tsk1{{unpack}}
- tsk2{{download}}
- tsk1{{unpack}} -- ref_list/input_ref --> tsk2{{download}}
- inp1>user_input] -- input_refs --> tsk1{{unpack}}
- tsk2{{download}} -- downloaded --> out1>raster]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/user_data/ingest_smb.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/user_data/ingest_smb.md
index 169fc3d6..75a4a2c7 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/user_data/ingest_smb.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/user_data/ingest_smb.md
@@ -1,5 +1,48 @@
# data_ingestion/user_data/ingest_smb
+Adds user rasters into the cluster storage from an SMB share, allowing for them to be used on workflows. The workflow downloads rasters from the provided SMB share and generates Raster objects with local assets that can be used in other operations.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>rasters]
+ tsk1{{download}}
+ inp1>user_input] -- user_input --> tsk1{{download}}
+ tsk1{{download}} -- rasters --> out1>rasters]
+```
+
+## Sources
+
+- **user_input**: DataVibe containing the time range and geometry metadata of the set rasters to be downloaded.
+
+## Sinks
+
+- **rasters**: Rasters with downloaded assets.
+
+## Parameters
+
+- **server_name**: The name of the SMB server
+
+- **server_ip**: The IP address of the SMB server
+
+- **server_port**: The port to connect to on the SMB server
+
+- **username**: Username used to connect to server
+
+- **password**: Password to access server
+
+- **share_name**: Name of file share
+
+- **directory_path**: Path to directory containing rasters
+
+- **bands**: Ordered list of bands within the rasters
+
+## Tasks
+
+- **download**: Downloads rasters from an SMB share.
+
+## Workflow Yaml
+
```yaml
name: ingest_smb
@@ -46,13 +89,4 @@ description:
rasters: Rasters with downloaded assets.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>rasters]
- tsk1{{download}}
- inp1>user_input] -- user_input --> tsk1{{download}}
- tsk1{{download}} -- rasters --> out1>rasters]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/download_chirps.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/download_chirps.md
index 7245de19..92ed4cc3 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/download_chirps.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/download_chirps.md
@@ -1,5 +1,40 @@
# data_ingestion/weather/download_chirps
+Downloads accumulated precipitation data from the CHIRPS dataset.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>product]
+ tsk1{{list_chirps}}
+ tsk2{{download_chirps}}
+ tsk1{{list_chirps}} -- chirps_products/chirps_product --> tsk2{{download_chirps}}
+ inp1>user_input] -- input_item --> tsk1{{list_chirps}}
+ tsk2{{download_chirps}} -- downloaded_product --> out1>product]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **product**: TIFF file containing accumulated precipitation.
+
+## Parameters
+
+- **freq**: daily or monthly frequencies
+
+- **res**: p05 for 0.05 degree resolution or p25 for 0.25 degree resolution, p25 is only available daily
+
+## Tasks
+
+- **list_chirps**: Lists products from the CHIRPS dataset with desired frequency and resolution for input geometry and time range.
+
+- **download_chirps**: Downloads accumulated precipitation data from listed products.
+
+## Workflow Yaml
+
```yaml
name: chirps
@@ -36,15 +71,4 @@ description:
only available daily
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>product]
- tsk1{{list_chirps}}
- tsk2{{download_chirps}}
- tsk1{{list_chirps}} -- chirps_products/chirps_product --> tsk2{{download_chirps}}
- inp1>user_input] -- input_item --> tsk1{{list_chirps}}
- tsk2{{download_chirps}} -- downloaded_product --> out1>product]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/download_era5.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/download_era5.md
index d56761dc..4b13dcc3 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/download_era5.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/download_era5.md
@@ -1,5 +1,53 @@
# data_ingestion/weather/download_era5
+Hourly estimated weather variables. Hourly weather variables obtained from combining observations and numerical model runs to estimate the state of the atmosphere.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>downloaded_product]
+ tsk1{{list}}
+ tsk2{{download}}
+ tsk1{{list}} -- era5_products/era5_product --> tsk2{{download}}
+ inp1>user_input] -- input_item --> tsk1{{list}}
+ tsk2{{download}} -- downloaded_product --> out1>downloaded_product]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **downloaded_product**: 30km resolution weather variables.
+
+## Parameters
+
+- **pc_key**: Optional Planetary Computer API key.
+
+- **variable**: Options are:
+ 2t - 2 meter temperature (default)
+ 100u - 100 meter U wind component
+ 100v - 100 meter V wind component
+ 10u - 10 meter U wind component
+ 10v - 10 meter V wind component
+ 2d - 2 meter dewpoint temperature
+ mn2t - Minimum temperature at 2 meters since previous post-processing
+ msl - Mean sea level pressure
+ mx2t - Maximum temperature at 2 meters since previous post-processing
+ sp - Surface pressure
+ ssrd - Surface solar radiation downwards
+ sst - Sea surface temperature
+ tp - Total precipitation
+
+## Tasks
+
+- **list**: Lists ERA5 products for input geometry and time range.
+
+- **download**: Downloads requested property from ERA5 products.
+
+## Workflow Yaml
+
```yaml
name: download_era5
@@ -43,15 +91,4 @@ description:
\ radiation downwards\n sst - Sea surface temperature\n tp - Total precipitation"
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>downloaded_product]
- tsk1{{list}}
- tsk2{{download}}
- tsk1{{list}} -- era5_products/era5_product --> tsk2{{download}}
- inp1>user_input] -- input_item --> tsk1{{list}}
- tsk2{{download}} -- downloaded_product --> out1>downloaded_product]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/download_era5_monthly.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/download_era5_monthly.md
index 56122e61..2c5985dc 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/download_era5_monthly.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/download_era5_monthly.md
@@ -1,5 +1,51 @@
# data_ingestion/weather/download_era5_monthly
+Monthly estimated weather variables. Monthly weather variables obtained from combining observations and numerical model runs to estimate the state of the atmosphere.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>downloaded_product]
+ tsk1{{list}}
+ tsk2{{download}}
+ tsk1{{list}} -- era5_products/era5_product --> tsk2{{download}}
+ inp1>user_input] -- input_item --> tsk1{{list}}
+ tsk2{{download}} -- downloaded_product --> out1>downloaded_product]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **downloaded_product**: 30km resolution weather variables.
+
+## Parameters
+
+- **cds_api_key**: api key for Copernicus CDS (https://cds.climate.copernicus.eu/user/register)
+
+- **variable**: Options are:
+ 2t - 2 meter temperature (default)
+ 100u - 100 meter U wind component
+ 100v - 100 meter V wind component
+ 10u - 10 meter U wind component
+ 10v - 10 meter V wind component
+ 2d - 2 meter dewpoint temperature
+ msl - Mean sea level pressure
+ sp - Surface pressure
+ ssrd - Surface solar radiation downwards
+ sst - Sea surface temperature
+ tp - Total precipitation
+
+## Tasks
+
+- **list**: Lists monthly ERA5 products for the input time range and geometry.
+
+- **download**: Downloads requested property from ERA5 products.
+
+## Workflow Yaml
+
```yaml
name: download_era5_monthly
@@ -43,15 +89,4 @@ description:
\ - Total precipitation"
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>downloaded_product]
- tsk1{{list}}
- tsk2{{download}}
- tsk1{{list}} -- era5_products/era5_product --> tsk2{{download}}
- inp1>user_input] -- input_item --> tsk1{{list}}
- tsk2{{download}} -- downloaded_product --> out1>downloaded_product]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/download_gridmet.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/download_gridmet.md
index e99907b3..8c5b81a4 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/download_gridmet.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/download_gridmet.md
@@ -1,5 +1,54 @@
# data_ingestion/weather/download_gridmet
+Daily surface meteorological properties from GridMET. The workflow downloads weather and hydrological data for the input time range. Data is available for the contiguous US and southern British Columbia surfaces from 1979-present, with a daily temporal resolution and a ~4-km (1/24th degree) spatial resolution.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>downloaded_product]
+ tsk1{{list}}
+ tsk2{{download}}
+ tsk1{{list}} -- products/input_product --> tsk2{{download}}
+ inp1>user_input] -- input_item --> tsk1{{list}}
+ tsk2{{download}} -- downloaded_product --> out1>downloaded_product]
+```
+
+## Sources
+
+- **user_input**: Time range of interest.
+
+## Sinks
+
+- **downloaded_product**: Downloaded variable for each year in the input time range.
+
+## Parameters
+
+- **variable**: Options are:
+ bi - Burning Index
+ erc - Energy Release Component
+ etr - Daily reference evapotranspiration (alfafa, units = mm)
+ fm100 - Fuel Moisture (100-hr, units = %)
+ fm1000 - Fuel Moisture (1000-hr, units = %)
+ pet - Potential evapotranspiration (reference grass evapotranspiration, units = mm)
+ pr - Precipitation amount (daily total, units = mm)
+ rmax - Maximum relative humidity (units = %)
+ rmin - Minimum relative humidity (units = %)
+ sph - Specific humididy (units = kg/kg)
+ srad - Downward surface shortwave radiation (units = W/m^2)
+ th - Wind direction (degrees clockwise from North)
+ tmmn - Minimum temperature (units = K)
+ tmmx - Maximum temperature (units = K)
+ vpd - Vapor Pressure Deficit (units = kPa)
+ vs - Wind speed at 10m (units = m/s)
+
+## Tasks
+
+- **list**: Lists GridMET products of `variable` from years intersecting with input time range.
+
+- **download**: Downloads Climatology Lab weather products (TerraClimate and GridMET) defined by the input product.
+
+## Workflow Yaml
+
```yaml
name: download_gridmet
@@ -46,15 +95,4 @@ description:
\ vs - Wind speed at 10m (units = m/s)"
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>downloaded_product]
- tsk1{{list}}
- tsk2{{download}}
- tsk1{{list}} -- products/input_product --> tsk2{{download}}
- inp1>user_input] -- input_item --> tsk1{{list}}
- tsk2{{download}} -- downloaded_product --> out1>downloaded_product]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/download_herbie.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/download_herbie.md
index 4daaad77..a2bb5c8c 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/download_herbie.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/download_herbie.md
@@ -1,5 +1,48 @@
# data_ingestion/weather/download_herbie
+Downloads forecast data for provided location & time range using herbie python package. Herbie is a python package that downloads recent and archived numerical weather prediction (NWP) model outputs from different cloud archive sources. Its most popular capability is to download HRRR model data. NWP data in GRIB2 format can be read with xarray+cfgrib. Model data Herbie can retrieve includes the High Resolution Rapid Refresh (HRRR), Rapid Refresh (RAP), Global Forecast System (GFS), National Blend of Models (NBM), Rapid Refresh Forecast System - Prototype (RRFS), and ECMWF open data forecast products (ECMWF).
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>forecast]
+ tsk1{{list_herbie}}
+ tsk2{{download_herbie}}
+ tsk1{{list_herbie}} -- product/herbie_product --> tsk2{{download_herbie}}
+ inp1>user_input] -- input_item --> tsk1{{list_herbie}}
+ tsk2{{download_herbie}} -- forecast --> out1>forecast]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **forecast**: Grib file with the requested forecast.
+
+## Parameters
+
+- **model**: Model name as defined in the models template folder. CASE INSENSITIVE Below are examples of model types 'hrrr' HRRR contiguous United States model 'hrrrak' HRRR Alaska model (alias 'alaska') 'rap' RAP model 'gfs' Global Forecast System (atmosphere) 'gfs_wave' Global Forecast System (wave) 'rrfs' Rapid Refresh Forecast System prototype for more information see https://herbie.readthedocs.io/en/latest/user_guide/model_info.html
+
+- **product**: Output variable product file type (sfc (surface fields), prs (pressure fields), nat (native fields), subh (subhourly fields)). Not specifying this will use the first product in model template file.
+
+- **frequency**: frequency in hours of the forecast
+
+- **forecast_lead_times**: Forecast lead time in the format [start_time, end_time, increment] (in hours). This parameter can be None, and in this case see parameter 'forecast_start_date' for more details. You cannot specify 'forecast_lead_times' and 'forecast_start_date' at the same time.
+
+- **forecast_start_date**: latest datetime (in the format "%Y-%m-%d %H:%M") for which analysis (zero lead time) are retrieved. After this datetime, forecasts with progressively increasing lead times are retrieved. If this parameter is set to None and 'forecast_lead_times' is also set to None, then the workflow returns analysis (zero lead time) up to the latest analysis available, and from that point it returns forecasts with progressively increasing lead times.
+
+- **search_text**: It's a regular expression used to search on GRIB2 Index files and allow you to download just the layer of the file required instead of complete file. For more information on search_text refer to below url. https://blaylockbk.github.io/Herbie/_build/html/user_guide/searchString.html
+
+## Tasks
+
+- **list_herbie**: Lists herbie products.
+
+- **download_herbie**: Download herbie grib files.
+
+## Workflow Yaml
+
```yaml
name: download_herbie
@@ -12,10 +55,8 @@ parameters:
model: hrrr
product: null
frequency: 1
- forecast_lead_times:
- - 0
- - 1
- - 1
+ forecast_lead_times: null
+ forecast_start_date: null
search_text: :TMP:2 m
tasks:
list_herbie:
@@ -25,6 +66,7 @@ tasks:
product: '@from(product)'
frequency: '@from(frequency)'
forecast_lead_times: '@from(forecast_lead_times)'
+ forecast_start_date: '@from(forecast_start_date)'
search_text: '@from(search_text)'
download_herbie:
op: download_herbie
@@ -57,21 +99,18 @@ description:
will use the first product in model template file.
frequency: frequency in hours of the forecast
forecast_lead_times: Forecast lead time in the format [start_time, end_time, increment]
- (in hours)
+ (in hours). This parameter can be None, and in this case see parameter 'forecast_start_date'
+ for more details. You cannot specify 'forecast_lead_times' and 'forecast_start_date'
+ at the same time.
+ forecast_start_date: latest datetime (in the format "%Y-%m-%d %H:%M") for which
+ analysis (zero lead time) are retrieved. After this datetime, forecasts with
+ progressively increasing lead times are retrieved. If this parameter is set
+ to None and 'forecast_lead_times' is also set to None, then the workflow returns
+ analysis (zero lead time) up to the latest analysis available, and from that
+ point it returns forecasts with progressively increasing lead times.
search_text: It's a regular expression used to search on GRIB2 Index files and
allow you to download just the layer of the file required instead of complete
file. For more information on search_text refer to below url. https://blaylockbk.github.io/Herbie/_build/html/user_guide/searchString.html
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>forecast]
- tsk1{{list_herbie}}
- tsk2{{download_herbie}}
- tsk1{{list_herbie}} -- product/herbie_product --> tsk2{{download_herbie}}
- inp1>user_input] -- input_item --> tsk1{{list_herbie}}
- tsk2{{download_herbie}} -- forecast --> out1>forecast]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/download_terraclimate.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/download_terraclimate.md
index 23bdd08e..a8c31a4e 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/download_terraclimate.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/download_terraclimate.md
@@ -1,5 +1,52 @@
# data_ingestion/weather/download_terraclimate
+Monthly climate and hydroclimate properties from TerraClimate. The workflow downloads weather and hydrological data for the input time range. Data is available for global terrestrial surfaces from 1958-present, with a monthly temporal resolution and a ~4-km (1/24th degree) spatial resolution.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>downloaded_product]
+ tsk1{{list}}
+ tsk2{{download}}
+ tsk1{{list}} -- products/input_product --> tsk2{{download}}
+ inp1>user_input] -- input_item --> tsk1{{list}}
+ tsk2{{download}} -- downloaded_product --> out1>downloaded_product]
+```
+
+## Sources
+
+- **user_input**: Time range of interest.
+
+## Sinks
+
+- **downloaded_product**: Downloaded variable for each year in the input time range.
+
+## Parameters
+
+- **variable**: Options are:
+ aet - Actual Evapotranspiration (monthly total, units = mm)
+ def - Climate Water Deficit (monthly total, units = mm)
+ pet - Potential evapotranspiration (monthly total, units = mm)
+ ppt - Precipitation (monthly total, units = mm)
+ q - Runoff (monthly total, units = mm)
+ soil - Soil Moisture (total column at end of month, units = mm)
+ srad - Downward surface shortwave radiation (units = W/m2)
+ swe - Snow water equivalent (at end of month, units = mm)
+ tmax - Max Temperature (average for month, units = C)
+ tmin - Min Temperature (average for month, units = C)
+ vap - Vapor pressure (average for month, units = kPa)
+ ws - Wind speed (average for month, units = m/s)
+ vpd - Vapor Pressure Deficit (average for month, units = kPa)
+ PDSI - Palmer Drought Severity Index (at end of month, units = unitless)
+
+## Tasks
+
+- **list**: Lists TerraClimate products of `variable` from years intersecting with input time range.
+
+- **download**: Downloads Climatology Lab weather products (TerraClimate and GridMET) defined by the input product.
+
+## Workflow Yaml
+
```yaml
name: download_terraclimate
@@ -46,15 +93,4 @@ description:
\ = unitless)"
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>downloaded_product]
- tsk1{{list}}
- tsk2{{download}}
- tsk1{{list}} -- products/input_product --> tsk2{{download}}
- inp1>user_input] -- input_item --> tsk1{{list}}
- tsk2{{download}} -- downloaded_product --> out1>downloaded_product]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/get_ambient_weather.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/get_ambient_weather.md
index d32b0f61..71ca7308 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/get_ambient_weather.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/get_ambient_weather.md
@@ -1,5 +1,40 @@
# data_ingestion/weather/get_ambient_weather
+Downloads weather data from an Ambient Weather station. The workflow connects to the Ambient Weather REST API and requests data for the input time range. The input geometry will be used to find a device inside the region. If not devices are found in the geometry, the workflow will fail. Connection to the API requires an API key and an App key.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>weather]
+ tsk1{{get_weather}}
+ inp1>user_input] -- user_input --> tsk1{{get_weather}}
+ tsk1{{get_weather}} -- weather --> out1>weather]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **weather**: Weather data from the station.
+
+## Parameters
+
+- **api_key**: Ambient Weather API key.
+
+- **app_key**: Ambient Weather App key.
+
+- **limit**: Maximum number of data points. If -1, do not limit.
+
+- **feed_interval**: Interval between samples. Defined by the weather station.
+
+## Tasks
+
+- **get_weather**: Connects to the Ambient Weather REST API and requests weather data for the input time range from stations within input geometry.
+
+## Workflow Yaml
+
```yaml
name: get_ambient_weather
@@ -40,13 +75,4 @@ description:
feed_interval: Interval between samples. Defined by the weather station.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>weather]
- tsk1{{get_weather}}
- inp1>user_input] -- user_input --> tsk1{{get_weather}}
- tsk1{{get_weather}} -- weather --> out1>weather]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/get_forecast.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/get_forecast.md
index deb96ed3..5b560ecc 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/get_forecast.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/get_forecast.md
@@ -1,5 +1,43 @@
# data_ingestion/weather/get_forecast
+Downloads weather forecast data from NOAA Global Forecast System (GFS) for the input time range. The workflow downloads global forecast data from the Planetary Computer with 13km resolution between grid points. The workflow requires a SAS token to access the blob storage, which can be found at https://planetarycomputer.microsoft.com/dataset/storage/noaa-gfs.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>forecast]
+ tsk1{{preprocessing}}
+ tsk2{{gfs_download}}
+ tsk3{{read_forecast}}
+ tsk1{{preprocessing}} -- time --> tsk2{{gfs_download}}
+ tsk1{{preprocessing}} -- location --> tsk3{{read_forecast}}
+ tsk2{{gfs_download}} -- global_forecast --> tsk3{{read_forecast}}
+ inp1>user_input] -- user_input --> tsk1{{preprocessing}}
+ tsk3{{read_forecast}} -- local_forecast --> out1>forecast]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **forecast**: Weather forecast data.
+
+## Parameters
+
+- **noaa_gfs_token**: SAS token to access blob storage.
+
+## Tasks
+
+- **preprocessing**: Gets the most relevant model date and forecast hour of product for the given input day, time and location.
+
+- **gfs_download**: Downloads the global forecast for the given input time.
+
+- **read_forecast**: Extracts the local data from a global forecast.
+
+## Workflow Yaml
+
```yaml
name: get_forecast
@@ -48,18 +86,4 @@ description:
noaa_gfs_token: SAS token to access blob storage.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>forecast]
- tsk1{{preprocessing}}
- tsk2{{gfs_download}}
- tsk3{{read_forecast}}
- tsk1{{preprocessing}} -- time --> tsk2{{gfs_download}}
- tsk1{{preprocessing}} -- location --> tsk3{{read_forecast}}
- tsk2{{gfs_download}} -- global_forecast --> tsk3{{read_forecast}}
- inp1>user_input] -- user_input --> tsk1{{preprocessing}}
- tsk3{{read_forecast}} -- local_forecast --> out1>forecast]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/herbie_forecast.md b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/herbie_forecast.md
index faffe2fa..8f1d67d7 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/herbie_forecast.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_ingestion/weather/herbie_forecast.md
@@ -1,5 +1,52 @@
# data_ingestion/weather/herbie_forecast
+Downloads forecast observations for provided location & time range using herbie python package. Herbie is a python package that downloads recent and archived numerical weather prediction (NWP) model outputs from different cloud archive sources. Its most popular capability is to download HRRR model data. NWP data in GRIB2 format can be read with xarray+cfgrib. Model data Herbie can retrieve includes the High Resolution Rapid Refresh (HRRR), Rapid Refresh (RAP), Global Forecast System (GFS), National Blend of Models (NBM), Rapid Refresh Forecast System - Prototype (RRFS), and ECMWF open data forecast products (ECMWF).
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>weather_forecast]
+ out2>forecast_range]
+ tsk1{{forecast_range}}
+ tsk2{{forecast_download}}
+ tsk1{{forecast_range}} -- download_period/user_input --> tsk2{{forecast_download}}
+ inp1>user_input] -- user_input --> tsk1{{forecast_range}}
+ tsk2{{forecast_download}} -- weather_forecast --> out1>weather_forecast]
+ tsk1{{forecast_range}} -- download_period --> out2>forecast_range]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **weather_forecast**: Downloaded Forecast observations, cleaned, interpolated and mapped to each hour.
+
+- **forecast_range**: Time range of forecast observations.
+
+## Parameters
+
+- **forecast_lead_times**: Help to define forecast lead time in hours. Accept the input in range format. Example - (1, 25, 1) For more information refer below url. https://blaylockbk.github.io/Herbie/_build/html/reference_guide/_autosummary/herbie.archive.Herbie.html
+
+- **search_text**: It's a regular expression used to search on GRIB2 Index files and allow you to download just the layer of the file required instead of complete file. For more information on search_text refer to below url. https://blaylockbk.github.io/Herbie/_build/html/user_guide/searchString.html
+
+- **weather_type**: It's a user preferred text to represent weather parameter type (temperature, humidity, wind_speed etc). This is used as column name for the output returned by operator.
+
+- **model**: Model name as defined in the models template folder. CASE INSENSITIVE Below are examples of model types 'hrrr' HRRR contiguous United States model 'hrrrak' HRRR Alaska model (alias 'alaska') 'rap' RAP model 'gfs' Global Forecast System (atmosphere) 'gfs_wave' Global Forecast System (wave) 'rrfs' Rapid Refresh Forecast System prototype
+
+- **overwrite**: If true, look for GRIB2 file even if local copy exists. If false, use the local copy
+
+- **product**: Output variable product file type (sfc (surface fields), prs (pressure fields), nat (native fields), subh (subhourly fields)). Not specifying this will use the first product in model template file.
+
+## Tasks
+
+- **forecast_range**: Splits input time range according to frequency and number of hours in lead time.
+
+- **forecast_download**: Downloads forecast observations with Herbie.
+
+## Workflow Yaml
+
```yaml
name: forecast_weather
@@ -75,17 +122,4 @@ description:
by operator.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>weather_forecast]
- out2>forecast_range]
- tsk1{{forecast_range}}
- tsk2{{forecast_download}}
- tsk1{{forecast_range}} -- download_period/user_input --> tsk2{{forecast_download}}
- inp1>user_input] -- user_input --> tsk1{{forecast_range}}
- tsk2{{forecast_download}} -- weather_forecast --> out1>weather_forecast]
- tsk1{{forecast_range}} -- download_period --> out2>forecast_range]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_processing/chunk_onnx/chunk_onnx.md b/docs/source/docfiles/markdown/workflow_yaml/data_processing/chunk_onnx/chunk_onnx.md
index dafb56ec..879d499a 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_processing/chunk_onnx/chunk_onnx.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_processing/chunk_onnx/chunk_onnx.md
@@ -1,5 +1,49 @@
# data_processing/chunk_onnx/chunk_onnx
+Runs an Onnx model over all rasters in the input to produce a single raster. This workflow is intended to apply an Onnx model over all rasters in the input to produce a single raster output. This can be used, for instance, to compute time-series analysis of a list of rasters that span multiple times. The analysis can be any computation that can be expressed as an Onnx model (for an example, see notebooks/crop_cycles/crop_cycles.ipynb). In order to run the model in parallel (and avoid running out of memory if the list of rasters is large), the input rasters are divided spatially into chunks (that span all times). The Onnx model is applied to these chunks and then combined back to produce the final output.
+
+```{mermaid}
+ graph TD
+ inp1>rasters]
+ out1>raster]
+ tsk1{{chunk_raster}}
+ tsk2{{list_to_sequence}}
+ tsk3{{compute_onnx}}
+ tsk4{{combine_chunks}}
+ tsk1{{chunk_raster}} -- chunk_series/chunk --> tsk3{{compute_onnx}}
+ tsk2{{list_to_sequence}} -- rasters_seq/input_raster --> tsk3{{compute_onnx}}
+ tsk3{{compute_onnx}} -- output_raster/chunks --> tsk4{{combine_chunks}}
+ inp1>rasters] -- rasters --> tsk1{{chunk_raster}}
+ inp1>rasters] -- list_rasters --> tsk2{{list_to_sequence}}
+ tsk4{{combine_chunks}} -- raster --> out1>raster]
+```
+
+## Sources
+
+- **rasters**: Input rasters.
+
+## Sinks
+
+- **raster**: Result of the Onnx model run.
+
+## Parameters
+
+- **model_file**: An Onnx model which needs to be deployed with "farmvibes-ai local add-onnx" command.
+
+- **step**: Size of the chunk in pixels.
+
+## Tasks
+
+- **chunk_raster**: Splits input rasters into a series of chunks.
+
+- **list_to_sequence**: Combines a list of Rasters into a RasterSequence.
+
+- **compute_onnx**: Runs the onnx model across chunks of the input rasters.
+
+- **combine_chunks**: Combines series of chunks into a final raster.
+
+## Workflow Yaml
+
```yaml
name: chunk_onnx
@@ -60,20 +104,4 @@ description:
step: Size of the chunk in pixels.
-```
-
-```{mermaid}
- graph TD
- inp1>rasters]
- out1>raster]
- tsk1{{chunk_raster}}
- tsk2{{list_to_sequence}}
- tsk3{{compute_onnx}}
- tsk4{{combine_chunks}}
- tsk1{{chunk_raster}} -- chunk_series/chunk --> tsk3{{compute_onnx}}
- tsk2{{list_to_sequence}} -- rasters_seq/input_raster --> tsk3{{compute_onnx}}
- tsk3{{compute_onnx}} -- output_raster/chunks --> tsk4{{combine_chunks}}
- inp1>rasters] -- rasters --> tsk1{{chunk_raster}}
- inp1>rasters] -- list_rasters --> tsk2{{list_to_sequence}}
- tsk4{{combine_chunks}} -- raster --> out1>raster]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_processing/chunk_onnx/chunk_onnx_sequence.md b/docs/source/docfiles/markdown/workflow_yaml/data_processing/chunk_onnx/chunk_onnx_sequence.md
index b4614c4a..f85ae376 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_processing/chunk_onnx/chunk_onnx_sequence.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_processing/chunk_onnx/chunk_onnx_sequence.md
@@ -1,5 +1,45 @@
# data_processing/chunk_onnx/chunk_onnx_sequence
+Runs an Onnx model over all rasters in the input to produce a single raster. This workflow is intended to run an Onnx model on all input rasters to produce a single raster output. This can be used, for instance, to compute time-series analysis of a list of rasters that span multiple times. The analysis can be any computation that can be expressed as an Onnx model (for an example, see notebooks/crop_cycles/crop_cycles.ipynb). In order to run the model in parallel (and avoid running out of memory if the list of rasters is large), the input rasters are divided spatially into chunks (that span all times). The Onnx model is applied to these chunks and then combined back to produce the final output.
+
+```{mermaid}
+ graph TD
+ inp1>rasters]
+ out1>raster]
+ tsk1{{chunk_raster}}
+ tsk2{{compute_onnx}}
+ tsk3{{combine_chunks}}
+ tsk1{{chunk_raster}} -- chunk_series/chunk --> tsk2{{compute_onnx}}
+ tsk2{{compute_onnx}} -- output_raster/chunks --> tsk3{{combine_chunks}}
+ inp1>rasters] -- rasters --> tsk1{{chunk_raster}}
+ inp1>rasters] -- input_raster --> tsk2{{compute_onnx}}
+ tsk3{{combine_chunks}} -- raster --> out1>raster]
+```
+
+## Sources
+
+- **rasters**: Input rasters.
+
+## Sinks
+
+- **raster**: Result of the Onnx model run.
+
+## Parameters
+
+- **model_file**: An Onnx model which needs to be deployed with "farmvibes-ai local add-onnx" command.
+
+- **step**: Size of the chunk in pixels.
+
+## Tasks
+
+- **chunk_raster**: Splits input rasters into a series of chunks.
+
+- **compute_onnx**: Runs the onnx model across chunks of the input rasters.
+
+- **combine_chunks**: Combines series of chunks into a final raster.
+
+## Workflow Yaml
+
```yaml
name: chunk_onnx_sequence
@@ -55,18 +95,4 @@ description:
step: Size of the chunk in pixels.
-```
-
-```{mermaid}
- graph TD
- inp1>rasters]
- out1>raster]
- tsk1{{chunk_raster}}
- tsk2{{compute_onnx}}
- tsk3{{combine_chunks}}
- tsk1{{chunk_raster}} -- chunk_series/chunk --> tsk2{{compute_onnx}}
- tsk2{{compute_onnx}} -- output_raster/chunks --> tsk3{{combine_chunks}}
- inp1>rasters] -- rasters --> tsk1{{chunk_raster}}
- inp1>rasters] -- input_raster --> tsk2{{compute_onnx}}
- tsk3{{combine_chunks}} -- raster --> out1>raster]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_processing/clip/clip.md b/docs/source/docfiles/markdown/workflow_yaml/data_processing/clip/clip.md
index 680a81e2..6f31f6c3 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_processing/clip/clip.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_processing/clip/clip.md
@@ -1,5 +1,34 @@
# data_processing/clip/clip
+Performs a soft clip on an input raster based on a provided reference geometry. The workflow outputs a new raster copied from the input raster with its geometry metadata as the intersection between the input raster's geometry and the provided reference geometry. The workflow raises an error if there is no intersection between both geometries.
+
+```{mermaid}
+ graph TD
+ inp1>raster]
+ inp2>input_geometry]
+ out1>clipped_raster]
+ tsk1{{clip_raster}}
+ inp1>raster] -- raster --> tsk1{{clip_raster}}
+ inp2>input_geometry] -- input_item --> tsk1{{clip_raster}}
+ tsk1{{clip_raster}} -- clipped_raster --> out1>clipped_raster]
+```
+
+## Sources
+
+- **raster**: Input raster to be clipped.
+
+- **input_geometry**: Reference geometry.
+
+## Sinks
+
+- **clipped_raster**: Clipped raster with the reference geometry.
+
+## Tasks
+
+- **clip_raster**: Soft clips the input raster based on the provided referente geometry.
+
+## Workflow Yaml
+
```yaml
name: clip
@@ -29,15 +58,4 @@ description:
parameters: null
-```
-
-```{mermaid}
- graph TD
- inp1>raster]
- inp2>input_geometry]
- out1>clipped_raster]
- tsk1{{clip_raster}}
- inp1>raster] -- raster --> tsk1{{clip_raster}}
- inp2>input_geometry] -- input_item --> tsk1{{clip_raster}}
- tsk1{{clip_raster}} -- clipped_raster --> out1>clipped_raster]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_processing/gradient/raster_gradient.md b/docs/source/docfiles/markdown/workflow_yaml/data_processing/gradient/raster_gradient.md
index c09c717b..b36ad676 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_processing/gradient/raster_gradient.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_processing/gradient/raster_gradient.md
@@ -1,5 +1,30 @@
# data_processing/gradient/raster_gradient
+Computes the gradient of each band of the input raster with a Sobel operator.
+
+```{mermaid}
+ graph TD
+ inp1>raster]
+ out1>gradient]
+ tsk1{{gradient}}
+ inp1>raster] -- input_raster --> tsk1{{gradient}}
+ tsk1{{gradient}} -- output_raster --> out1>gradient]
+```
+
+## Sources
+
+- **raster**: Input raster.
+
+## Sinks
+
+- **gradient**: Raster with the gradients.
+
+## Tasks
+
+- **gradient**: Computes the gradient of each band of the input raster with a Sobel operator.
+
+## Workflow Yaml
+
```yaml
name: raster_gradient
@@ -23,13 +48,4 @@ description:
parameters: null
-```
-
-```{mermaid}
- graph TD
- inp1>raster]
- out1>gradient]
- tsk1{{gradient}}
- inp1>raster] -- input_raster --> tsk1{{gradient}}
- tsk1{{gradient}} -- output_raster --> out1>gradient]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_processing/heatmap/classification.md b/docs/source/docfiles/markdown/workflow_yaml/data_processing/heatmap/classification.md
index d1ec2e5b..fef8e003 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_processing/heatmap/classification.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_processing/heatmap/classification.md
@@ -1,5 +1,83 @@
# data_processing/heatmap/classification
+Utilizes input Sentinel-2 satellite imagery & the sensor samples as labeled data that contain nutrient information (Nitrogen, Carbon, pH, Phosphorus) to train a model using Random Forest classifier. The inference operation predicts nutrients in soil for the chosen farm boundary.
+ The workflow generates a heatmap for selected nutrient. It relies on sample soil data that
+contain information of nutrients. The quantity of samples define the accuracy of the heat map
+generation. During the research performed testing with samples spaced at 200 feet, 100 feet and
+50 feet. The 50 feet sample spaced distance provided results matching to the ground truth.
+Generating heatmaps with this approach reduces the number of samples. It utilizes the logic
+below behind the scenes to generate heatmap.
+ - Read the sentinel raster provided.
+ - Sensor samples needs to be uploaded into prescriptions entity in Azure
+ data manager for Agriculture (ADMAg). ADMAg is having hierarchy to hold
+ information of Party, Field, Seasons, Crop etc. Prior to
+ uploading prescriptions, it is required to build hierarchy and
+ a `prescription_map_id`. All prescriptions uploaded to ADMAg are
+ related to farm hierarchy through `prescription_map_id`. Please refer to
+ https://learn.microsoft.com/en-us/rest/api/data-manager-for-agri/ for
+ more information on ADMAg.
+ - Compute indices using the spyndex python package.
+ - Clip the satellite imagery & sensor samples using farm boundary.
+ - Perform spatial interpolation to find raster pixels within the offset distance
+ from sample location and assign the value of nutrients to group of pixels.
+ - Classify the data based on number of bins.
+ - Train the model using Random Forest classifier.
+ - Predict the nutrients using the satellite imagery.
+ - Generate a shape file using the predicted outputs.
+
+```{mermaid}
+ graph TD
+ inp1>input_raster]
+ inp2>samples]
+ out1>result]
+ tsk1{{compute_index}}
+ tsk2{{soil_sample_heatmap}}
+ tsk1{{compute_index}} -- index_raster/raster --> tsk2{{soil_sample_heatmap}}
+ inp1>input_raster] -- raster --> tsk1{{compute_index}}
+ inp2>samples] -- samples --> tsk2{{soil_sample_heatmap}}
+ tsk2{{soil_sample_heatmap}} -- result --> out1>result]
+```
+
+## Sources
+
+- **input_raster**: Input raster for index computation.
+
+- **samples**: External references to sensor samples for nutrients.
+
+## Sinks
+
+- **result**: Zip file containing cluster geometries.
+
+## Parameters
+
+- **attribute_name**: Nutrient property name in sensor samples geojson file. For example CARBON (C), Nitrogen (N), Phosphorus (P) etc.,
+
+- **buffer**: Offset distance from sample to perform interpolate operations with raster.
+
+- **index**: Type of index to be used to generate heatmap. For example - evi, pri etc.,
+
+- **bins**: Possible number of groups used to move value to nearest group using [numpy histogram](https://numpy.org/doc/stable/reference/generated/numpy.histogram.html) and to pre-process the data to support model training with classification .
+
+- **simplify**: Replace small polygons in input with value of their largest neighbor after converting from raster to vector. Accepts 'simplify' or 'convex' or 'none'.
+
+- **tolerance**: All parts of a [simplified geometry](https://geopandas.org/en/stable/docs/reference/api/geopandas.GeoSeries.simplify.html) will be no more than tolerance distance from the original. It has the same units as the coordinate reference system of the GeoSeries. For example, using tolerance=100 in a projected CRS with meters as units means a distance of 100 meters in reality.
+
+- **data_scale**: Accepts True or False. Default is False. On True, it scale data using [StandardScalar] (https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.StandardScaler.html) from scikit-learn package. It Standardize features by removing the mean and scaling to unit variance.
+
+- **max_depth**: The maximum depth of the tree. If None, then nodes are expanded until all leaves are pure or until all leaves contain less than min_samples_split samples. For more details refer to (https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html)
+
+- **n_estimators**: The number of trees in the forest. For more details refer to (https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html)
+
+- **random_state**: Controls both the randomness of the bootstrapping of the samples used when building trees (if bootstrap=True) and the sampling of the features to consider when looking for the best split at each node (if max_features < n_features). For more details refer to (https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html)
+
+## Tasks
+
+- **compute_index**: Computes an index from the bands of an input raster.
+
+- **soil_sample_heatmap**: Generate heatmap for nutrients using satellite or spaceEye imagery.
+
+## Workflow Yaml
+
```yaml
name: heatmap_intermediate
@@ -59,10 +137,10 @@ description:
\ the logic\nbelow behind the scenes to generate heatmap.\n - Read the sentinel\
\ raster provided.\n - Sensor samples needs to be uploaded into prescriptions\
\ entity in Azure\n data manager for Agriculture (ADMAg). ADMAg is having hierarchy\
- \ to hold\n information of Farmer, Field, Seasons, Crop, Boundary etc. Prior\
- \ to\n uploading prescriptions, it is required to build hierarchy and\n \
- \ a `prescription_map_id`. All prescriptions uploaded to ADMAg are\n related\
- \ to farm hierarchy through `prescription_map_id`. Please refer to\n https://learn.microsoft.com/en-us/rest/api/data-manager-for-agri/\
+ \ to hold\n information of Party, Field, Seasons, Crop etc. Prior to\n uploading\
+ \ prescriptions, it is required to build hierarchy and\n a `prescription_map_id`.\
+ \ All prescriptions uploaded to ADMAg are\n related to farm hierarchy through\
+ \ `prescription_map_id`. Please refer to\n https://learn.microsoft.com/en-us/rest/api/data-manager-for-agri/\
\ for\n more information on ADMAg.\n - Compute indices using the spyndex python\
\ package.\n - Clip the satellite imagery & sensor samples using farm boundary.\n\
\ - Perform spatial interpolation to find raster pixels within the offset distance\n\
@@ -103,17 +181,4 @@ description:
n_features). For more details refer to (https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html)
-```
-
-```{mermaid}
- graph TD
- inp1>input_raster]
- inp2>samples]
- out1>result]
- tsk1{{compute_index}}
- tsk2{{soil_sample_heatmap}}
- tsk1{{compute_index}} -- index_raster/raster --> tsk2{{soil_sample_heatmap}}
- inp1>input_raster] -- raster --> tsk1{{compute_index}}
- inp2>samples] -- samples --> tsk2{{soil_sample_heatmap}}
- tsk2{{soil_sample_heatmap}} -- result --> out1>result]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_processing/index/index.md b/docs/source/docfiles/markdown/workflow_yaml/data_processing/index/index.md
index 11aade4f..6b730148 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_processing/index/index.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_processing/index/index.md
@@ -1,5 +1,34 @@
# data_processing/index/index
+Computes an index from the bands of an input raster. In addition to the indices 'ndvi', 'evi', 'msavi', 'ndre', 'reci', 'ndmi', 'methane' and 'pri' all indices in https://github.com/awesome-spectral-indices/awesome-spectral-indices are available (depending on the bands available on the corresponding satellite product).
+
+```{mermaid}
+ graph TD
+ inp1>raster]
+ out1>index_raster]
+ tsk1{{compute_index}}
+ inp1>raster] -- raster --> tsk1{{compute_index}}
+ tsk1{{compute_index}} -- index --> out1>index_raster]
+```
+
+## Sources
+
+- **raster**: Input raster.
+
+## Sinks
+
+- **index_raster**: Single-band raster with the computed index.
+
+## Parameters
+
+- **index**: The choice of index to be computed ('ndvi', 'evi', 'msavi', 'ndre', 'reci', 'ndmi', 'methane', 'pri' or any of the awesome-spectral-indices).
+
+## Tasks
+
+- **compute_index**: Computes `index` over the input raster.
+
+## Workflow Yaml
+
```yaml
name: index
@@ -31,13 +60,4 @@ description:
'ndmi', 'methane', 'pri' or any of the awesome-spectral-indices).
-```
-
-```{mermaid}
- graph TD
- inp1>raster]
- out1>index_raster]
- tsk1{{compute_index}}
- inp1>raster] -- raster --> tsk1{{compute_index}}
- tsk1{{compute_index}} -- index --> out1>index_raster]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_processing/linear_trend/chunked_linear_trend.md b/docs/source/docfiles/markdown/workflow_yaml/data_processing/linear_trend/chunked_linear_trend.md
index 3f1b76b2..0585bc77 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_processing/linear_trend/chunked_linear_trend.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_processing/linear_trend/chunked_linear_trend.md
@@ -1,5 +1,45 @@
# data_processing/linear_trend/chunked_linear_trend
+Computes the pixel-wise linear trend of a list of rasters (e.g. NDVI). The workflow computes the linear trend over chunks of data, combining them into the final raster.
+
+```{mermaid}
+ graph TD
+ inp1>input_rasters]
+ out1>linear_trend_raster]
+ tsk1{{chunk_raster}}
+ tsk2{{linear_trend}}
+ tsk3{{combine_chunks}}
+ tsk1{{chunk_raster}} -- chunk_series/series --> tsk2{{linear_trend}}
+ tsk2{{linear_trend}} -- trend/chunks --> tsk3{{combine_chunks}}
+ inp1>input_rasters] -- rasters --> tsk1{{chunk_raster}}
+ inp1>input_rasters] -- rasters --> tsk2{{linear_trend}}
+ tsk3{{combine_chunks}} -- raster --> out1>linear_trend_raster]
+```
+
+## Sources
+
+- **input_rasters**: List of rasters to compute linear trend.
+
+## Sinks
+
+- **linear_trend_raster**: Raster with the trend and the test statistics.
+
+## Parameters
+
+- **chunk_step_y**: steps used to divide the rasters into chunks in the y direction (units are grid points).
+
+- **chunk_step_x**: steps used to divide the rasters into chunks in the x direction (units are grid points).
+
+## Tasks
+
+- **chunk_raster**: Splits input rasters into a series of chunks.
+
+- **linear_trend**: Computes the pixel-wise linear trend across rasters.
+
+- **combine_chunks**: Combines series of chunks into a final raster.
+
+## Workflow Yaml
+
```yaml
name: chunked_linear_trend
@@ -45,18 +85,4 @@ description:
(units are grid points).
-```
-
-```{mermaid}
- graph TD
- inp1>input_rasters]
- out1>linear_trend_raster]
- tsk1{{chunk_raster}}
- tsk2{{linear_trend}}
- tsk3{{combine_chunks}}
- tsk1{{chunk_raster}} -- chunk_series/series --> tsk2{{linear_trend}}
- tsk2{{linear_trend}} -- trend/chunks --> tsk3{{combine_chunks}}
- inp1>input_rasters] -- rasters --> tsk1{{chunk_raster}}
- inp1>input_rasters] -- rasters --> tsk2{{linear_trend}}
- tsk3{{combine_chunks}} -- raster --> out1>linear_trend_raster]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_processing/merge/match_merge_to_ref.md b/docs/source/docfiles/markdown/workflow_yaml/data_processing/merge/match_merge_to_ref.md
index 1c0b0491..863b2323 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_processing/merge/match_merge_to_ref.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_processing/merge/match_merge_to_ref.md
@@ -1,5 +1,52 @@
# data_processing/merge/match_merge_to_ref
+Resamples input rasters to the reference rasters' grid. The workflow will produce input and reference raster pairs with intersecting geometries. For each pair, the input raster is resampled to match the reference raster's grid. Afterwards, all resampled rasters are groupped if they are contained in a reference raster geometry, and each raster group is matched into single raster. The output should contain the information available in the input rasters, gridded according to the reference rasters.
+
+```{mermaid}
+ graph TD
+ inp1>rasters]
+ inp2>ref_rasters]
+ out1>match_rasters]
+ tsk1{{pair}}
+ tsk2{{match}}
+ tsk3{{group}}
+ tsk4{{merge}}
+ tsk1{{pair}} -- paired_rasters1/ref_raster --> tsk2{{match}}
+ tsk1{{pair}} -- paired_rasters2/raster --> tsk2{{match}}
+ tsk2{{match}} -- output_raster/rasters --> tsk3{{group}}
+ tsk3{{group}} -- raster_groups/raster_sequence --> tsk4{{merge}}
+ inp1>rasters] -- rasters2 --> tsk1{{pair}}
+ inp2>ref_rasters] -- rasters1 --> tsk1{{pair}}
+ inp2>ref_rasters] -- group_by --> tsk3{{group}}
+ tsk4{{merge}} -- raster --> out1>match_rasters]
+```
+
+## Sources
+
+- **rasters**: Input rasters that will be resampled.
+
+- **ref_rasters**: Reference rasters.
+
+## Sinks
+
+- **match_rasters**: Rasters with information from the input rasters on the reference grid.
+
+## Parameters
+
+- **resampling**: Type of resampling when reprojecting the rasters. See [link=https://rasterio.readthedocs.io/en/latest/api/rasterio.enums.html#rasterio.enums.Resampling] rasterio documentation: https://rasterio.readthedocs.io/en/latest/api/rasterio.enums.html#rasterio.enums.Resampling[/] for all available resampling options.
+
+## Tasks
+
+- **pair**: Creates pairs of rasters with intersecting geometries between two input lists of Raster.
+
+- **match**: Resamples the input `raster` to match the grid of `ref_raster`.
+
+- **group**: Groups input rasters that are contained in the geometry of a reference raster.
+
+- **merge**: Merges rasters in a sequence to a single raster.
+
+## Workflow Yaml
+
```yaml
name: match_merge_to_ref
@@ -59,23 +106,4 @@ description:
for all available resampling options.'
-```
-
-```{mermaid}
- graph TD
- inp1>rasters]
- inp2>ref_rasters]
- out1>match_rasters]
- tsk1{{pair}}
- tsk2{{match}}
- tsk3{{group}}
- tsk4{{merge}}
- tsk1{{pair}} -- paired_rasters1/ref_raster --> tsk2{{match}}
- tsk1{{pair}} -- paired_rasters2/raster --> tsk2{{match}}
- tsk2{{match}} -- output_raster/rasters --> tsk3{{group}}
- tsk3{{group}} -- raster_groups/raster_sequence --> tsk4{{merge}}
- inp1>rasters] -- rasters2 --> tsk1{{pair}}
- inp2>ref_rasters] -- rasters1 --> tsk1{{pair}}
- inp2>ref_rasters] -- group_by --> tsk3{{group}}
- tsk4{{merge}} -- raster --> out1>match_rasters]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_processing/outlier/detect_outlier.md b/docs/source/docfiles/markdown/workflow_yaml/data_processing/outlier/detect_outlier.md
index 5a77e9fd..614702e8 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_processing/outlier/detect_outlier.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_processing/outlier/detect_outlier.md
@@ -1,5 +1,46 @@
# data_processing/outlier/detect_outlier
+Fits a single-component Gaussian Mixture Model (GMM) over input data to detect outliers according to the threshold parameter. The workflow outputs segmentation and outlier maps based on the threshold parameter and the likelihood of each sample belonging to the GMM component. It also yields heatmaps of the likelihood, and the mean of GMM's component.
+
+```{mermaid}
+ graph TD
+ inp1>rasters]
+ out1>segmentation]
+ out2>heatmap]
+ out3>outliers]
+ out4>mixture_means]
+ tsk1{{outlier}}
+ inp1>rasters] -- rasters --> tsk1{{outlier}}
+ tsk1{{outlier}} -- segmentation --> out1>segmentation]
+ tsk1{{outlier}} -- heatmap --> out2>heatmap]
+ tsk1{{outlier}} -- outliers --> out3>outliers]
+ tsk1{{outlier}} -- mixture_means --> out4>mixture_means]
+```
+
+## Sources
+
+- **rasters**: Input rasters.
+
+## Sinks
+
+- **segmentation**: Segmentation maps based on the likelihood of each sample belonging to the GMM's single-component.
+
+- **heatmap**: Likelihood maps.
+
+- **outliers**: Outlier maps based on the thresholded likelihood map.
+
+- **mixture_means**: Mean of the GMM.
+
+## Parameters
+
+- **threshold**: Likelihood threshold value to consider a sample as an outlier.
+
+## Tasks
+
+- **outlier**: Fits a single-component Gaussian Mixture Model (GMM) over input rasters to detect outliers according to the threshold parameter.
+
+## Workflow Yaml
+
```yaml
name: detect_outlier
@@ -37,19 +78,4 @@ description:
threshold: Likelihood threshold value to consider a sample as an outlier.
-```
-
-```{mermaid}
- graph TD
- inp1>rasters]
- out1>segmentation]
- out2>heatmap]
- out3>outliers]
- out4>mixture_means]
- tsk1{{outlier}}
- inp1>rasters] -- rasters --> tsk1{{outlier}}
- tsk1{{outlier}} -- segmentation --> out1>segmentation]
- tsk1{{outlier}} -- heatmap --> out2>heatmap]
- tsk1{{outlier}} -- outliers --> out3>outliers]
- tsk1{{outlier}} -- mixture_means --> out4>mixture_means]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_processing/threshold/threshold_raster.md b/docs/source/docfiles/markdown/workflow_yaml/data_processing/threshold/threshold_raster.md
index fbd3c354..ca237596 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_processing/threshold/threshold_raster.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_processing/threshold/threshold_raster.md
@@ -1,5 +1,34 @@
# data_processing/threshold/threshold_raster
+Thresholds values of the input raster if higher than the threshold parameter.
+
+```{mermaid}
+ graph TD
+ inp1>raster]
+ out1>thresholded_raster]
+ tsk1{{threshold_task}}
+ inp1>raster] -- raster --> tsk1{{threshold_task}}
+ tsk1{{threshold_task}} -- thresholded --> out1>thresholded_raster]
+```
+
+## Sources
+
+- **raster**: Input raster.
+
+## Sinks
+
+- **thresholded_raster**: Thresholded raster.
+
+## Parameters
+
+- **threshold**: Threshold value.
+
+## Tasks
+
+- **threshold_task**: Thresholds values of the input raster if higher than the threshold parameter.
+
+## Workflow Yaml
+
```yaml
name: threshold_raster
@@ -28,13 +57,4 @@ description:
threshold: Threshold value.
-```
-
-```{mermaid}
- graph TD
- inp1>raster]
- out1>thresholded_raster]
- tsk1{{threshold_task}}
- inp1>raster] -- raster --> tsk1{{threshold_task}}
- tsk1{{threshold_task}} -- thresholded --> out1>thresholded_raster]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_processing/timeseries/timeseries_aggregation.md b/docs/source/docfiles/markdown/workflow_yaml/data_processing/timeseries/timeseries_aggregation.md
index b9a12869..7b00c33a 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_processing/timeseries/timeseries_aggregation.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_processing/timeseries/timeseries_aggregation.md
@@ -1,5 +1,38 @@
# data_processing/timeseries/timeseries_aggregation
+Computes the mean, standard deviation, maximum, and minimum values of all regions of the raster and aggregates them into a timeseries.
+
+```{mermaid}
+ graph TD
+ inp1>raster]
+ inp2>input_geometry]
+ out1>timeseries]
+ tsk1{{summary}}
+ tsk2{{timeseries}}
+ tsk1{{summary}} -- summary/stats --> tsk2{{timeseries}}
+ inp1>raster] -- raster --> tsk1{{summary}}
+ inp2>input_geometry] -- input_geometry --> tsk1{{summary}}
+ tsk2{{timeseries}} -- timeseries --> out1>timeseries]
+```
+
+## Sources
+
+- **raster**: Input raster.
+
+- **input_geometry**: Geometry of interest.
+
+## Sinks
+
+- **timeseries**: Aggregated statistics of the raster.
+
+## Tasks
+
+- **summary**: Computes the mean, standard deviation, maximum, and minimum values across the whole raster.
+
+- **timeseries**: Aggregates list of summary statistics into a timeseries.
+
+## Workflow Yaml
+
```yaml
name: timeseries_aggregation
@@ -30,17 +63,4 @@ description:
timeseries: Aggregated statistics of the raster.
-```
-
-```{mermaid}
- graph TD
- inp1>raster]
- inp2>input_geometry]
- out1>timeseries]
- tsk1{{summary}}
- tsk2{{timeseries}}
- tsk1{{summary}} -- summary/stats --> tsk2{{timeseries}}
- inp1>raster] -- raster --> tsk1{{summary}}
- inp2>input_geometry] -- input_geometry --> tsk1{{summary}}
- tsk2{{timeseries}} -- timeseries --> out1>timeseries]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/data_processing/timeseries/timeseries_masked_aggregation.md b/docs/source/docfiles/markdown/workflow_yaml/data_processing/timeseries/timeseries_masked_aggregation.md
index 443176b2..963f925d 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/data_processing/timeseries/timeseries_masked_aggregation.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/data_processing/timeseries/timeseries_masked_aggregation.md
@@ -1,5 +1,46 @@
# data_processing/timeseries/timeseries_masked_aggregation
+Computes the mean, standard deviation, maximum, and minimum values of all regions of the raster considered by the mask and aggregates them into a timeseries.
+
+```{mermaid}
+ graph TD
+ inp1>raster]
+ inp2>mask]
+ inp3>input_geometry]
+ out1>timeseries]
+ tsk1{{masked_summary}}
+ tsk2{{timeseries}}
+ tsk1{{masked_summary}} -- summary/stats --> tsk2{{timeseries}}
+ inp1>raster] -- raster --> tsk1{{masked_summary}}
+ inp2>mask] -- mask --> tsk1{{masked_summary}}
+ inp3>input_geometry] -- input_geometry --> tsk1{{masked_summary}}
+ tsk2{{timeseries}} -- timeseries --> out1>timeseries]
+```
+
+## Sources
+
+- **raster**: Input raster.
+
+- **mask**: Mask of the regions to be considered during summarization;
+
+- **input_geometry**: Geometry of interest.
+
+## Sinks
+
+- **timeseries**: Aggregated statistics of the raster considered by the mask.
+
+## Parameters
+
+- **timeseries_masked_thr**: Threshold of the maximum ratio of masked content allowed in a raster. The statistics of rasters with masked content above the threshold (e.g., heavily clouded) are not included in the timeseries.
+
+## Tasks
+
+- **masked_summary**: Computes the mean, standard deviation, maximum, and minimum values across non-masked regions of the raster.
+
+- **timeseries**: Aggregates list of summary statistics into a timeseries.
+
+## Workflow Yaml
+
```yaml
name: timeseries_masked_aggregation
@@ -43,19 +84,4 @@ description:
(e.g., heavily clouded) are not included in the timeseries.
-```
-
-```{mermaid}
- graph TD
- inp1>raster]
- inp2>mask]
- inp3>input_geometry]
- out1>timeseries]
- tsk1{{masked_summary}}
- tsk2{{timeseries}}
- tsk1{{masked_summary}} -- summary/stats --> tsk2{{timeseries}}
- inp1>raster] -- raster --> tsk1{{masked_summary}}
- inp2>mask] -- mask --> tsk1{{masked_summary}}
- inp3>input_geometry] -- input_geometry --> tsk1{{masked_summary}}
- tsk2{{timeseries}} -- timeseries --> out1>timeseries]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/canopy_cover.md b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/canopy_cover.md
index 3b4f7ab2..11656a17 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/canopy_cover.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/canopy_cover.md
@@ -1,5 +1,56 @@
# farm_ai/agriculture/canopy_cover
+Estimates pixel-wise canopy cover for a region and date. The workflow retrieves the relevant Sentinel-2 products with Planetary Computer (PC) API, and computes the NDVI for each available tile and date. It applies a linear regressor trained with polynomial features (up to the 3rd degree) on top of the index raster to estimate canopy cover. The coeficients and intercept of the regressor were obtained beforehand using as ground-truth masked/annotated drone imagery, and are used for inference in this workflow.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>ndvi]
+ out2>estimated_canopy_cover]
+ out3>ndvi_timeseries]
+ out4>canopy_timeseries]
+ tsk1{{ndvi_summary}}
+ tsk2{{canopy}}
+ tsk3{{canopy_summary_timeseries}}
+ tsk1{{ndvi_summary}} -- index/indices --> tsk2{{canopy}}
+ tsk2{{canopy}} -- estimated_canopy_cover/raster --> tsk3{{canopy_summary_timeseries}}
+ tsk1{{ndvi_summary}} -- merged_cloud_mask/mask --> tsk3{{canopy_summary_timeseries}}
+ inp1>user_input] -- user_input --> tsk1{{ndvi_summary}}
+ inp1>user_input] -- input_geometry --> tsk3{{canopy_summary_timeseries}}
+ tsk1{{ndvi_summary}} -- index --> out1>ndvi]
+ tsk2{{canopy}} -- estimated_canopy_cover --> out2>estimated_canopy_cover]
+ tsk1{{ndvi_summary}} -- timeseries --> out3>ndvi_timeseries]
+ tsk3{{canopy_summary_timeseries}} -- timeseries --> out4>canopy_timeseries]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **ndvi**: NDVI raster.
+
+- **estimated_canopy_cover**: Raster with pixel-wise canopy cover estimation;
+
+- **ndvi_timeseries**: Aggregated NDVI statistics of the retrieved tiles within the input geometry and time range.
+
+- **canopy_timeseries**: Aggregated canopy cover statistics.
+
+## Parameters
+
+- **pc_key**: Optional Planetary Computer API key.
+
+## Tasks
+
+- **ndvi_summary**: Calculates NDVI statistics (mean, standard deviation, maximum and minimum) for the input geometry and time range.
+
+- **canopy**: Applies a linear regressor with pre-computed polynomial features on top of the index raster to estimate canopy cover.
+
+- **canopy_summary_timeseries**: Computes the mean, standard deviation, maximum, and minimum values of all regions of the raster considered by the mask and aggregates them into a timeseries.
+
+## Workflow Yaml
+
```yaml
name: canopy_cover
@@ -53,25 +104,4 @@ description:
pc_key: Optional Planetary Computer API key.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>ndvi]
- out2>estimated_canopy_cover]
- out3>ndvi_timeseries]
- out4>canopy_timeseries]
- tsk1{{ndvi_summary}}
- tsk2{{canopy}}
- tsk3{{canopy_summary_timeseries}}
- tsk1{{ndvi_summary}} -- index/indices --> tsk2{{canopy}}
- tsk2{{canopy}} -- estimated_canopy_cover/raster --> tsk3{{canopy_summary_timeseries}}
- tsk1{{ndvi_summary}} -- merged_cloud_mask/mask --> tsk3{{canopy_summary_timeseries}}
- inp1>user_input] -- user_input --> tsk1{{ndvi_summary}}
- inp1>user_input] -- input_geometry --> tsk3{{canopy_summary_timeseries}}
- tsk1{{ndvi_summary}} -- index --> out1>ndvi]
- tsk2{{canopy}} -- estimated_canopy_cover --> out2>estimated_canopy_cover]
- tsk1{{ndvi_summary}} -- timeseries --> out3>ndvi_timeseries]
- tsk3{{canopy_summary_timeseries}} -- timeseries --> out4>canopy_timeseries]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/change_detection.md b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/change_detection.md
index 3cb9dc1e..92b49b29 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/change_detection.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/change_detection.md
@@ -1,5 +1,71 @@
# farm_ai/agriculture/change_detection
+Identifies changes/outliers over NDVI across dates. The workflow generates SpaceEye imagery for the input region and time range and computes NDVI raster for each date. It aggregates NDVI statistics (mean, standard deviation, maximum and minimum) in time and detects outliers across dates with a single-component Gaussian Mixture Model (GMM).
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>spaceeye_raster]
+ out2>index]
+ out3>timeseries]
+ out4>segmentation]
+ out5>heatmap]
+ out6>outliers]
+ out7>mixture_means]
+ tsk1{{spaceeye}}
+ tsk2{{ndvi}}
+ tsk3{{summary_timeseries}}
+ tsk4{{outliers}}
+ tsk1{{spaceeye}} -- raster --> tsk2{{ndvi}}
+ tsk2{{ndvi}} -- index_raster/raster --> tsk3{{summary_timeseries}}
+ tsk2{{ndvi}} -- index_raster/rasters --> tsk4{{outliers}}
+ inp1>user_input] -- user_input --> tsk1{{spaceeye}}
+ inp1>user_input] -- input_geometry --> tsk3{{summary_timeseries}}
+ tsk1{{spaceeye}} -- raster --> out1>spaceeye_raster]
+ tsk2{{ndvi}} -- index_raster --> out2>index]
+ tsk3{{summary_timeseries}} -- timeseries --> out3>timeseries]
+ tsk4{{outliers}} -- segmentation --> out4>segmentation]
+ tsk4{{outliers}} -- heatmap --> out5>heatmap]
+ tsk4{{outliers}} -- outliers --> out6>outliers]
+ tsk4{{outliers}} -- mixture_means --> out7>mixture_means]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **spaceeye_raster**: SpaceEye cloud-free rasters.
+
+- **index**: NDVI rasters.
+
+- **timeseries**: Aggregated NDVI statistics over the time range.
+
+- **segmentation**: Segmentation maps based on the likelihood of each sample belonging to the GMM's single-component.
+
+- **heatmap**: Likelihood maps.
+
+- **outliers**: Outlier maps.
+
+- **mixture_means**: Means of the GMM.
+
+## Parameters
+
+- **pc_key**: PlanetaryComputer API key.
+
+## Tasks
+
+- **spaceeye**: Runs the SpaceEye cloud removal pipeline, yielding daily cloud-free images for the input geometry and time range.
+
+- **ndvi**: Computes an index from the bands of an input raster.
+
+- **summary_timeseries**: Computes the mean, standard deviation, maximum, and minimum values of all regions of the raster and aggregates them into a timeseries.
+
+- **outliers**: Fits a single-component Gaussian Mixture Model (GMM) over input data to detect outliers according to the threshold parameter.
+
+## Workflow Yaml
+
```yaml
name: change_detection
@@ -59,32 +125,4 @@ description:
pc_key: PlanetaryComputer API key.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>spaceeye_raster]
- out2>index]
- out3>timeseries]
- out4>segmentation]
- out5>heatmap]
- out6>outliers]
- out7>mixture_means]
- tsk1{{spaceeye}}
- tsk2{{ndvi}}
- tsk3{{summary_timeseries}}
- tsk4{{outliers}}
- tsk1{{spaceeye}} -- raster --> tsk2{{ndvi}}
- tsk2{{ndvi}} -- index_raster/raster --> tsk3{{summary_timeseries}}
- tsk2{{ndvi}} -- index_raster/rasters --> tsk4{{outliers}}
- inp1>user_input] -- user_input --> tsk1{{spaceeye}}
- inp1>user_input] -- input_geometry --> tsk3{{summary_timeseries}}
- tsk1{{spaceeye}} -- raster --> out1>spaceeye_raster]
- tsk2{{ndvi}} -- index_raster --> out2>index]
- tsk3{{summary_timeseries}} -- timeseries --> out3>timeseries]
- tsk4{{outliers}} -- segmentation --> out4>segmentation]
- tsk4{{outliers}} -- heatmap --> out5>heatmap]
- tsk4{{outliers}} -- outliers --> out6>outliers]
- tsk4{{outliers}} -- mixture_means --> out7>mixture_means]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/emergence_summary.md b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/emergence_summary.md
index 1f29750c..ebc26dba 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/emergence_summary.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/emergence_summary.md
@@ -1,5 +1,48 @@
# farm_ai/agriculture/emergence_summary
+Calculates emergence statistics using thresholded MSAVI (mean, standard deviation, maximum and minimum) for the input geometry and time range. The workflow retrieves Sentinel2 products with Planetary Computer (PC) API, forwards them to a cloud detection model and combines the predicted cloud mask to the mask provided by PC. It computes the MSAVI for each available tile and date, thresholds them above a certain value and summarizes each with the mean, standard deviation, maximum and minimum values for the regions not obscured by clouds. Finally, it outputs a timeseries with such statistics for all available dates, filtering out heavily-clouded tiles.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>timeseries]
+ tsk1{{s2}}
+ tsk2{{msavi}}
+ tsk3{{emergence}}
+ tsk4{{summary_timeseries}}
+ tsk1{{s2}} -- raster --> tsk2{{msavi}}
+ tsk2{{msavi}} -- index_raster/raster --> tsk3{{emergence}}
+ tsk3{{emergence}} -- thresholded_raster/raster --> tsk4{{summary_timeseries}}
+ tsk1{{s2}} -- mask --> tsk4{{summary_timeseries}}
+ inp1>user_input] -- user_input --> tsk1{{s2}}
+ inp1>user_input] -- input_geometry --> tsk4{{summary_timeseries}}
+ tsk4{{summary_timeseries}} -- timeseries --> out1>timeseries]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **timeseries**: Aggregated emergence statistics of the retrieved tiles within the input geometry and time range.
+
+## Parameters
+
+- **pc_key**: Optional Planetary Computer API key.
+
+## Tasks
+
+- **s2**: Downloads and preprocesses Sentinel-2 imagery that covers the input geometry and time range, and computes improved cloud masks using cloud and shadow segmentation models.
+
+- **msavi**: Computes an index from the bands of an input raster.
+
+- **emergence**: Thresholds values of the input raster if higher than the threshold parameter.
+
+- **summary_timeseries**: Computes the mean, standard deviation, maximum, and minimum values of all regions of the raster considered by the mask and aggregates them into a timeseries.
+
+## Workflow Yaml
+
```yaml
name: emergence_summary
@@ -59,21 +102,4 @@ description:
pc_key: Optional Planetary Computer API key.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>timeseries]
- tsk1{{s2}}
- tsk2{{msavi}}
- tsk3{{emergence}}
- tsk4{{summary_timeseries}}
- tsk1{{s2}} -- raster --> tsk2{{msavi}}
- tsk2{{msavi}} -- index_raster/raster --> tsk3{{emergence}}
- tsk3{{emergence}} -- thresholded_raster/raster --> tsk4{{summary_timeseries}}
- tsk1{{s2}} -- mask --> tsk4{{summary_timeseries}}
- inp1>user_input] -- user_input --> tsk1{{s2}}
- inp1>user_input] -- input_geometry --> tsk4{{summary_timeseries}}
- tsk4{{summary_timeseries}} -- timeseries --> out1>timeseries]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/green_house_gas_fluxes.md b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/green_house_gas_fluxes.md
index 37a25582..69a69764 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/green_house_gas_fluxes.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/green_house_gas_fluxes.md
@@ -1,5 +1,34 @@
# farm_ai/agriculture/green_house_gas_fluxes
+Computes Green House Fluxes for a region and date range The workflow follows the GHG Protocol guidelines published for Brazil (which are based on IPCC reports) to compute Green House Gas emission fluxes (sequestration versus emissions) for a given crop.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>fluxes]
+ tsk1{{ghg}}
+ inp1>user_input] -- ghg --> tsk1{{ghg}}
+ tsk1{{ghg}} -- fluxes --> out1>fluxes]
+```
+
+## Sources
+
+- **user_input**: The user-provided inputs for GHG computation.
+
+## Sinks
+
+- **fluxes**: The computed fluxes for the given area and date range considering the user input data.
+
+## Parameters
+
+- **crop_type**: The type of the crop to compute GHG emissions. Supported crops are 'wheat', 'corn', 'cotton', and 'soybeans'.
+
+## Tasks
+
+- **ghg**: Computes Green House Gas emission fluxes based on emission factors based on IPCC methodology.
+
+## Workflow Yaml
+
```yaml
name: green_house_gas_fluxes
@@ -31,13 +60,4 @@ description:
'wheat', 'corn', 'cotton', and 'soybeans'.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>fluxes]
- tsk1{{ghg}}
- inp1>user_input] -- ghg --> tsk1{{ghg}}
- tsk1{{ghg}} -- fluxes --> out1>fluxes]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/heatmap_using_classification.md b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/heatmap_using_classification.md
index c4343de2..ff5faf67 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/heatmap_using_classification.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/heatmap_using_classification.md
@@ -1,5 +1,61 @@
# farm_ai/agriculture/heatmap_using_classification
+The workflow generates a nutrient heatmap for samples provided by user by downloading the samples from user input. The samples provided are related with farm boundary and have required nutrient information to create a heatmap.
+
+```{mermaid}
+ graph TD
+ inp1>input_samples]
+ inp2>input_raster]
+ out1>result]
+ tsk1{{download_samples}}
+ tsk2{{soil_sample_heatmap_classification}}
+ tsk1{{download_samples}} -- geometry/samples --> tsk2{{soil_sample_heatmap_classification}}
+ inp1>input_samples] -- user_input --> tsk1{{download_samples}}
+ inp2>input_raster] -- input_raster --> tsk2{{soil_sample_heatmap_classification}}
+ tsk2{{soil_sample_heatmap_classification}} -- result --> out1>result]
+```
+
+## Sources
+
+- **input_raster**: Input raster for index computation.
+
+- **input_samples**: External references to sensor samples for nutrients.
+
+## Sinks
+
+- **result**: Zip file containing cluster geometries.
+
+## Parameters
+
+- **attribute_name**: Nutrient property name in sensor samples geojson file. For example CARBON (C), Nitrogen (N), Phosphorus (P) etc.,
+
+- **buffer**: Offset distance from sample to perform interpolate operations with raster.
+
+- **index**: Type of index to be used to generate heatmap. For example - evi, pri etc.,
+
+- **bins**: Possible number of groups used to move value to nearest group using [numpy histogram](https://numpy.org/doc/stable/reference/generated/numpy.histogram.html) and to pre-process the data to support model training with classification .
+
+- **simplify**: Replace small polygons in input with value of their largest neighbor after converting from raster to vector. Accepts 'simplify' or 'convex' or 'none'.
+
+- **tolerance**: All parts of a [simplified geometry](https://geopandas.org/en/stable/docs/reference/api/geopandas.GeoSeries.simplify.html) will be no more than tolerance distance from the original. It has the same units as the coordinate reference system of the GeoSeries. For example, using tolerance=100 in a projected CRS with meters as units means a distance of 100 meters in reality.
+
+- **data_scale**: Accepts True or False. Default is False. On True, it scale data using [StandardScalar] (https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.StandardScaler.html) from scikit-learn package. It Standardize features by removing the mean and scaling to unit variance.
+
+- **max_depth**: The maximum depth of the tree. If None, then nodes are expanded until all leaves are pure or until all leaves contain less than min_samples_split samples. For more details refer to (https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html)
+
+- **n_estimators**: The number of trees in the forest. For more details refer to (https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html)
+
+- **random_state**: Controls both the randomness of the bootstrapping of the samples used when building trees (if bootstrap=True) and the sampling of the features to consider when looking for the best split at each node (if max_features < n_features). For more details refer to (https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html)
+
+## Tasks
+
+- **download_samples**: Adds user geometries into the cluster storage, allowing for them to be used on workflows.
+
+- **soil_sample_heatmap_classification**: Utilizes input Sentinel-2 satellite imagery & the sensor samples as labeled data that contain nutrient information (Nitrogen, Carbon, pH, Phosphorus) to train a model using Random Forest classifier. The inference operation predicts nutrients in soil for the chosen farm boundary.
+
+
+## Workflow Yaml
+
```yaml
name: heatmap_using_classification
@@ -54,17 +110,4 @@ description:
parameters: null
-```
-
-```{mermaid}
- graph TD
- inp1>input_samples]
- inp2>input_raster]
- out1>result]
- tsk1{{download_samples}}
- tsk2{{soil_sample_heatmap_classification}}
- tsk1{{download_samples}} -- geometry/samples --> tsk2{{soil_sample_heatmap_classification}}
- inp1>input_samples] -- user_input --> tsk1{{download_samples}}
- inp2>input_raster] -- input_raster --> tsk2{{soil_sample_heatmap_classification}}
- tsk2{{soil_sample_heatmap_classification}} -- result --> out1>result]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/heatmap_using_classification_admag.md b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/heatmap_using_classification_admag.md
index 246b8094..07167ee9 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/heatmap_using_classification_admag.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/heatmap_using_classification_admag.md
@@ -1,5 +1,71 @@
# farm_ai/agriculture/heatmap_using_classification_admag
+This workflow integrate the ADMAG API to download prescriptions and generate heatmap. The prescriptions are related with farm boundary and the nutrient information. Each prescription represent a sensor sample at a location within a farm boundary.
+
+```{mermaid}
+ graph TD
+ inp1>admag_input]
+ inp2>input_raster]
+ out1>result]
+ tsk1{{prescriptions}}
+ tsk2{{soil_sample_heatmap_classification}}
+ tsk1{{prescriptions}} -- response/samples --> tsk2{{soil_sample_heatmap_classification}}
+ inp1>admag_input] -- admag_input --> tsk1{{prescriptions}}
+ inp2>input_raster] -- input_raster --> tsk2{{soil_sample_heatmap_classification}}
+ tsk2{{soil_sample_heatmap_classification}} -- result --> out1>result]
+```
+
+## Sources
+
+- **input_raster**: Input raster for index computation.
+
+- **admag_input**: Required inputs to download prescriptions from admag.
+
+## Sinks
+
+- **result**: Zip file containing cluster geometries.
+
+## Parameters
+
+- **base_url**: URL to access the registered app
+
+- **client_id**: Value uniquely identifies registered application in the Microsoft identity platform. Visit url https://learn.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app to register the app.
+
+- **client_secret**: Sometimes called an application password, a client secret is a string value your app can use in place of a certificate to identity itself.
+
+- **authority**: The endpoint URIs for your app are generated automatically when you register or configure your app. It is used by client to obtain authorization from the resource owner
+
+- **default_scope**: URL for default azure OAuth2 permissions
+
+- **attribute_name**: Nutrient property name in sensor samples geojson file. For example CARBON (C), Nitrogen (N), Phosphorus (P) etc.,
+
+- **buffer**: Offset distance from sample to perform interpolate operations with raster.
+
+- **index**: Type of index to be used to generate heatmap. For example - evi, pri etc.,
+
+- **bins**: Possible number of groups used to move value to nearest group using [numpy histogram](https://numpy.org/doc/stable/reference/generated/numpy.histogram.html) and to pre-process the data to support model training with classification .
+
+- **simplify**: Replace small polygons in input with value of their largest neighbor after converting from raster to vector. Accepts 'simplify' or 'convex' or 'none'.
+
+- **tolerance**: All parts of a [simplified geometry](https://geopandas.org/en/stable/docs/reference/api/geopandas.GeoSeries.simplify.html) will be no more than tolerance distance from the original. It has the same units as the coordinate reference system of the GeoSeries. For example, using tolerance=100 in a projected CRS with meters as units means a distance of 100 meters in reality.
+
+- **data_scale**: Accepts True or False. Default is False. On True, it scale data using [StandardScalar] (https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.StandardScaler.html) from scikit-learn package. It Standardize features by removing the mean and scaling to unit variance.
+
+- **max_depth**: The maximum depth of the tree. If None, then nodes are expanded until all leaves are pure or until all leaves contain less than min_samples_split samples. For more details refer to (https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html)
+
+- **n_estimators**: The number of trees in the forest. For more details refer to (https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html)
+
+- **random_state**: Controls both the randomness of the bootstrapping of the samples used when building trees (if bootstrap=True) and the sampling of the features to consider when looking for the best split at each node (if max_features < n_features). For more details refer to (https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html)
+
+## Tasks
+
+- **prescriptions**: Fetches prescriptions using ADMAg (Microsoft Azure Data Manager for Agriculture).
+
+- **soil_sample_heatmap_classification**: Utilizes input Sentinel-2 satellite imagery & the sensor samples as labeled data that contain nutrient information (Nitrogen, Carbon, pH, Phosphorus) to train a model using Random Forest classifier. The inference operation predicts nutrients in soil for the chosen farm boundary.
+
+
+## Workflow Yaml
+
```yaml
name: heatmap_using_classification_admag
@@ -76,17 +142,4 @@ description:
default_scope: URL for default azure OAuth2 permissions
-```
-
-```{mermaid}
- graph TD
- inp1>admag_input]
- inp2>input_raster]
- out1>result]
- tsk1{{prescriptions}}
- tsk2{{soil_sample_heatmap_classification}}
- tsk1{{prescriptions}} -- response/samples --> tsk2{{soil_sample_heatmap_classification}}
- inp1>admag_input] -- admag_input --> tsk1{{prescriptions}}
- inp2>input_raster] -- input_raster --> tsk2{{soil_sample_heatmap_classification}}
- tsk2{{soil_sample_heatmap_classification}} -- result --> out1>result]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/heatmap_using_neighboring_data_points.md b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/heatmap_using_neighboring_data_points.md
index e7fefb03..fd1ad086 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/heatmap_using_neighboring_data_points.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/heatmap_using_neighboring_data_points.md
@@ -1,5 +1,60 @@
# farm_ai/agriculture/heatmap_using_neighboring_data_points
+Creates heatmap using the neighbors by performing spatial interpolation operations. It utilizes soil information collected at optimal sensor/sample locations and downloaded sentinel satellite imagery. The optimal location of nutrient samples are identified using workflow . The quantity of samples defines the accuracy of the heatmap generation. During the research performed testing on a 100 acre farm using sample count of approximately 20, 80, 130, 600. The research concluded that a sample count of 20 provided decent results, also accuracy of nutrient information improved with increase in sample count.
+
+```{mermaid}
+ graph TD
+ inp1>input_raster]
+ inp2>input_samples]
+ inp3>input_sample_clusters]
+ out1>result]
+ tsk1{{download_samples}}
+ tsk2{{download_sample_clusters}}
+ tsk3{{soil_sample_heatmap}}
+ tsk1{{download_samples}} -- geometry/samples --> tsk3{{soil_sample_heatmap}}
+ tsk2{{download_sample_clusters}} -- geometry/samples_boundary --> tsk3{{soil_sample_heatmap}}
+ inp1>input_raster] -- raster --> tsk3{{soil_sample_heatmap}}
+ inp2>input_samples] -- user_input --> tsk1{{download_samples}}
+ inp3>input_sample_clusters] -- user_input --> tsk2{{download_sample_clusters}}
+ tsk3{{soil_sample_heatmap}} -- result --> out1>result]
+```
+
+## Sources
+
+- **input_raster**: Sentinel-2 raster.
+
+- **input_samples**: Sensor samples with nutrient information.
+
+- **input_sample_clusters**: Clusters boundaries of sensor samples locations.
+
+## Sinks
+
+- **result**: Zip file containing heatmap output as shape files.
+
+## Parameters
+
+- **attribute_name**: Nutrient property name in sensor samples geojson file. For example: CARBON (C), Nitrogen (N), Phosphorus (P) etc.,
+
+- **simplify**: Replace small polygons in input with value of their largest neighbor after converting from raster to vector. Accepts 'simplify' or 'convex' or 'none'.
+
+- **tolerance**: All parts of a [simplified geometry](https://geopandas.org/en/stable/docs/reference/api/geopandas.GeoSeries.simplify.html) will be no more than tolerance distance from the original. It has the same units as the coordinate reference system of the GeoSeries. For example, using tolerance=100 in a projected CRS with meters as units means a distance of 100 meters in reality.
+
+- **algorithm**: Algorithm used to identify nearest neighbors. Accepts 'cluster overlap' or 'nearest neighbor' or 'kriging neighbor'.
+
+- **resolution**: Defines the output resolution as the ratio of input raster resolution. For example, if resolution is 5, the output heatmap is 5 times coarser than input raster.
+
+- **bins**: it defines the number of equal-width bins in the given range.Refer to this article to learn more about bins https://numpy.org/doc/stable/reference/generated/numpy.histogram.html
+
+## Tasks
+
+- **download_samples**: Adds user geometries into the cluster storage, allowing for them to be used on workflows.
+
+- **download_sample_clusters**: Adds user geometries into the cluster storage, allowing for them to be used on workflows.
+
+- **soil_sample_heatmap**: Generate heatmap for nutrients using satellite or spaceEye imagery.
+
+## Workflow Yaml
+
```yaml
name: heatmap_using_neighboring_data_points
@@ -75,21 +130,4 @@ description:
article to learn more about bins https://numpy.org/doc/stable/reference/generated/numpy.histogram.html
-```
-
-```{mermaid}
- graph TD
- inp1>input_raster]
- inp2>input_samples]
- inp3>input_sample_clusters]
- out1>result]
- tsk1{{download_samples}}
- tsk2{{download_sample_clusters}}
- tsk3{{soil_sample_heatmap}}
- tsk1{{download_samples}} -- geometry/samples --> tsk3{{soil_sample_heatmap}}
- tsk2{{download_sample_clusters}} -- geometry/samples_boundary --> tsk3{{soil_sample_heatmap}}
- inp1>input_raster] -- raster --> tsk3{{soil_sample_heatmap}}
- inp2>input_samples] -- user_input --> tsk1{{download_samples}}
- inp3>input_sample_clusters] -- user_input --> tsk2{{download_sample_clusters}}
- tsk3{{soil_sample_heatmap}} -- result --> out1>result]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/methane_index.md b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/methane_index.md
index c6eaa654..48b02fe4 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/methane_index.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/methane_index.md
@@ -1,5 +1,51 @@
# farm_ai/agriculture/methane_index
+Computes methane index from ultra emitters for a region and date range. The workflow retrieves the relevant Sentinel-2 products with Planetary Computer (PC) API and crop the rasters for the region defined in user_input. All bands are normalized and an anti-aliasing guassian filter is applied to smooth and remove potential artifacts. An unsupervised K-Nearest Neighbor is applied to identify bands similar to band 12, and the index is computed by the difference between band 12 to the pixel-wise median of top K similar bands.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>index]
+ out2>s2_raster]
+ out3>cloud_mask]
+ tsk1{{s2}}
+ tsk2{{clip}}
+ tsk3{{methane}}
+ tsk1{{s2}} -- raster --> tsk2{{clip}}
+ tsk2{{clip}} -- clipped_raster/raster --> tsk3{{methane}}
+ inp1>user_input] -- user_input --> tsk1{{s2}}
+ inp1>user_input] -- input_geometry --> tsk2{{clip}}
+ tsk3{{methane}} -- index_raster --> out1>index]
+ tsk1{{s2}} -- raster --> out2>s2_raster]
+ tsk1{{s2}} -- mask --> out3>cloud_mask]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **index**: Methane index raster.
+
+- **s2_raster**: Sentinel-2 raster.
+
+- **cloud_mask**: Cloud mask.
+
+## Parameters
+
+- **pc_key**: Optional Planetary Computer API key.
+
+## Tasks
+
+- **s2**: Downloads and preprocesses Sentinel-2 imagery that covers the input geometry and time range, and computes improved cloud masks using cloud and shadow segmentation models.
+
+- **clip**: Performs a soft clip on an input raster based on a provided reference geometry.
+
+- **methane**: Computes an index from the bands of an input raster.
+
+## Workflow Yaml
+
```yaml
name: methane_index
@@ -50,22 +96,4 @@ description:
pc_key: Optional Planetary Computer API key.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>index]
- out2>s2_raster]
- out3>cloud_mask]
- tsk1{{s2}}
- tsk2{{clip}}
- tsk3{{methane}}
- tsk1{{s2}} -- raster --> tsk2{{clip}}
- tsk2{{clip}} -- clipped_raster/raster --> tsk3{{methane}}
- inp1>user_input] -- user_input --> tsk1{{s2}}
- inp1>user_input] -- input_geometry --> tsk2{{clip}}
- tsk3{{methane}} -- index_raster --> out1>index]
- tsk1{{s2}} -- raster --> out2>s2_raster]
- tsk1{{s2}} -- mask --> out3>cloud_mask]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/ndvi_summary.md b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/ndvi_summary.md
index c7307156..c97aabf3 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/ndvi_summary.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/ndvi_summary.md
@@ -1,5 +1,44 @@
# farm_ai/agriculture/ndvi_summary
+Calculates NDVI statistics (mean, standard deviation, maximum and minimum) for the input geometry and time range. The workflow retrieves the relevant Sentinel-2 products with Planetary Computer (PC) API, forwards them to a cloud detection model and combines the predicted cloud mask to the mask obtained from the product. The workflow computes the NDVI for each available tile and date, summarizing each with the mean, standard deviation, maximum and minimum values for the regions not obscured by clouds. Finally, it outputs a timeseries with such statistics for all available dates, ignoring heavily-clouded tiles.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>timeseries]
+ tsk1{{s2}}
+ tsk2{{compute_ndvi}}
+ tsk3{{summary_timeseries}}
+ tsk1{{s2}} -- raster --> tsk2{{compute_ndvi}}
+ tsk2{{compute_ndvi}} -- index_raster/raster --> tsk3{{summary_timeseries}}
+ tsk1{{s2}} -- mask --> tsk3{{summary_timeseries}}
+ inp1>user_input] -- user_input --> tsk1{{s2}}
+ inp1>user_input] -- input_geometry --> tsk3{{summary_timeseries}}
+ tsk3{{summary_timeseries}} -- timeseries --> out1>timeseries]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **timeseries**: Aggregated NDVI statistics of the retrieved tiles within the input geometry and time range.
+
+## Parameters
+
+- **pc_key**: Optional Planetary Computer API key.
+
+## Tasks
+
+- **s2**: Downloads and preprocesses Sentinel-2 imagery that covers the input geometry and time range, and computes improved cloud masks using cloud and shadow segmentation models.
+
+- **compute_ndvi**: Computes an index from the bands of an input raster.
+
+- **summary_timeseries**: Computes the mean, standard deviation, maximum, and minimum values of all regions of the raster considered by the mask and aggregates them into a timeseries.
+
+## Workflow Yaml
+
```yaml
name: ndvi_summary
@@ -50,19 +89,4 @@ description:
pc_key: Optional Planetary Computer API key.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>timeseries]
- tsk1{{s2}}
- tsk2{{compute_ndvi}}
- tsk3{{summary_timeseries}}
- tsk1{{s2}} -- raster --> tsk2{{compute_ndvi}}
- tsk2{{compute_ndvi}} -- index_raster/raster --> tsk3{{summary_timeseries}}
- tsk1{{s2}} -- mask --> tsk3{{summary_timeseries}}
- inp1>user_input] -- user_input --> tsk1{{s2}}
- inp1>user_input] -- input_geometry --> tsk3{{summary_timeseries}}
- tsk3{{summary_timeseries}} -- timeseries --> out1>timeseries]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/weed_detection.md b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/weed_detection.md
index 086f04da..57c16c24 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/weed_detection.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/agriculture/weed_detection.md
@@ -1,5 +1,54 @@
# farm_ai/agriculture/weed_detection
+Generates shape files for similarly colored regions in the input raster. The workflow retrieves a remote raster and trains a Gaussian Mixture Model (GMM) over a subset of the input data with a fixed number of components. The GMM is then used to cluster all images pixels. Clustered regions are converted to polygons with a minimum size threshold. These polygons are then simplified to smooth their borders. All polygons of a given cluster are written to a single shapefile. All files are then compressed and returned as a single zip archive.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>result]
+ tsk1{{download_raster}}
+ tsk2{{weed_detection}}
+ tsk1{{download_raster}} -- raster --> tsk2{{weed_detection}}
+ inp1>user_input] -- user_input --> tsk1{{download_raster}}
+ tsk2{{weed_detection}} -- result --> out1>result]
+```
+
+## Sources
+
+- **user_input**: External references to raster data.
+
+## Sinks
+
+- **result**: Zip file containing cluster geometries.
+
+## Parameters
+
+- **buffer**: Buffer size, in projected CRS, to apply to the input geometry before sampling training points. A negative number can be used to avoid sampling unwanted regions if the geometry is not very precise.
+
+- **no_data**: Value to use as nodata when reading the raster. Uses the raster's internal nodata value if not provided.
+
+- **clusters**: Number of clusters to use when segmenting the image.
+
+- **sieve_size**: Area of the minimum connected region. Smaller regions will have their class assigned to the largest adjancent region.
+
+- **simplify**: Method used to simplify the geometries. Accepts 'none', for no simplification, 'simplify', for tolerance-based simplification, and 'convex', for returning the convex hull.
+
+- **tolerance**: Tolerance for simplifcation algorithm. Only applicable if simplification method is 'simplify'.
+
+- **samples**: Number os samples to use during training.
+
+- **bands**: List of band indices to use during training and inference.
+
+- **alpha_index**: Positive index of alpha band, if used to filter out nodata values.
+
+## Tasks
+
+- **download_raster**: Adds user rasters into the cluster storage, allowing for them to be used on workflows.
+
+- **weed_detection**: Trains a Gaussian Mixture Model (GMM), cluster all images pixels, and convert clustered regions into polygons.
+
+## Workflow Yaml
+
```yaml
name: weed_detection
@@ -69,15 +118,4 @@ description:
alpha_index: Positive index of alpha band, if used to filter out nodata values.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>result]
- tsk1{{download_raster}}
- tsk2{{weed_detection}}
- tsk1{{download_raster}} -- raster --> tsk2{{weed_detection}}
- inp1>user_input] -- user_input --> tsk1{{download_raster}}
- tsk2{{weed_detection}} -- result --> out1>result]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/carbon_local/admag_carbon_integration.md b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/carbon_local/admag_carbon_integration.md
index 593c499e..ca7dddcb 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/carbon_local/admag_carbon_integration.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/carbon_local/admag_carbon_integration.md
@@ -1,5 +1,58 @@
# farm_ai/carbon_local/admag_carbon_integration
+Computes the offset amount of carbon that would be sequestered in a seasonal field using Microsoft Azure Data Manager for Agriculture (ADMAg) data. Derives carbon sequestration information. Microsoft Azure Data Manager for Agriculture (ADMAg) and the COMET-Farm API are used to obtain farming data and evaluate carbon offset. ADMAg is capable of describing important farming activities such as fertilization, tillage, and organic amendments applications, all of which are represented in the data manager. FarmVibes.AI retrieves this information from the data manager and builds SeasonalFieldInformation FarmVibes.AI objects. These objects are then used to call the COMET-Farm API and evaluate Carbon Offset Information.
+
+```{mermaid}
+ graph TD
+ inp1>baseline_admag_input]
+ inp2>scenario_admag_input]
+ out1>carbon_output]
+ tsk1{{baseline_seasonal_field_list}}
+ tsk2{{scenario_seasonal_field_list}}
+ tsk3{{admag_carbon}}
+ tsk1{{baseline_seasonal_field_list}} -- seasonal_field/baseline_seasonal_fields --> tsk3{{admag_carbon}}
+ tsk2{{scenario_seasonal_field_list}} -- seasonal_field/scenario_seasonal_fields --> tsk3{{admag_carbon}}
+ inp1>baseline_admag_input] -- admag_input --> tsk1{{baseline_seasonal_field_list}}
+ inp2>scenario_admag_input] -- admag_input --> tsk2{{scenario_seasonal_field_list}}
+ tsk3{{admag_carbon}} -- carbon_output --> out1>carbon_output]
+```
+
+## Sources
+
+- **baseline_admag_input**: List of ADMAgSeasonalFieldInput to retrieve SeasonalFieldInformation objects for baseline COMET-Farm API Carbon offset evaluation.
+
+- **scenario_admag_input**: List of ADMAgSeasonalFieldInput to retrieve SeasonalFieldInformation objects for scenarios COMET-Farm API Carbon offset evaluation.
+
+## Sinks
+
+- **carbon_output**: Carbon sequestration received for scenario information provided as input.
+
+## Parameters
+
+- **base_url**: Azure Data Manager for Agriculture host. Please visit https://aka.ms/farmvibesDMA to check how to get these credentials.
+
+- **client_id**: Azure Data Manager for Agriculture client id. Please visit https://aka.ms/farmvibesDMA to check how to get these credentials.
+
+- **client_secret**: Azure Data Manager for Agriculture client secret. Please visit https://aka.ms/farmvibesDMA to check how to get these credentials.
+
+- **authority**: Azure Data Manager for Agriculture authority. Please visit https://aka.ms/farmvibesDMA to check how to get these credentials.
+
+- **default_scope**: Azure Data Manager for Agriculture default scope. Please visit https://aka.ms/farmvibesDMA to check how to get these credentials.
+
+- **comet_support_email**: Comet support email. The email used to register for a COMET account. The requests are forwarded to comet with this email reference. This email is used by comet to share the information back to you for failed requests.
+
+- **ngrok_token**: NGROK session token. A token that FarmVibes uses to create a web_hook url that is shared with Comet in a request when running the workflow. Comet can use this link to send back a response to FarmVibes. NGROK is a service that creates temporary urls for local servers. To use NGROK, FarmVibes needs to get a token from this website, https://dashboard.ngrok.com/.
+
+## Tasks
+
+- **baseline_seasonal_field_list**: Generates SeasonalFieldInformation using ADMAg (Microsoft Azure Data Manager for Agriculture).
+
+- **scenario_seasonal_field_list**: Generates SeasonalFieldInformation using ADMAg (Microsoft Azure Data Manager for Agriculture).
+
+- **admag_carbon**: Computes the offset amount of carbon that would be sequestered in a seasonal field using the baseline (historical) and scenario (time range interested in) information.
+
+## Workflow Yaml
+
```yaml
name: admag_carbon_integration
@@ -88,19 +141,4 @@ description:
https://aka.ms/farmvibesDMA to check how to get these credentials.
-```
-
-```{mermaid}
- graph TD
- inp1>baseline_admag_input]
- inp2>scenario_admag_input]
- out1>carbon_output]
- tsk1{{baseline_seasonal_field_list}}
- tsk2{{scenario_seasonal_field_list}}
- tsk3{{admag_carbon}}
- tsk1{{baseline_seasonal_field_list}} -- seasonal_field/baseline_seasonal_fields --> tsk3{{admag_carbon}}
- tsk2{{scenario_seasonal_field_list}} -- seasonal_field/scenario_seasonal_fields --> tsk3{{admag_carbon}}
- inp1>baseline_admag_input] -- admag_input --> tsk1{{baseline_seasonal_field_list}}
- inp2>scenario_admag_input] -- admag_input --> tsk2{{scenario_seasonal_field_list}}
- tsk3{{admag_carbon}} -- carbon_output --> out1>carbon_output]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/carbon_local/carbon_whatif.md b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/carbon_local/carbon_whatif.md
index b1c3b082..67e43dc4 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/carbon_local/carbon_whatif.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/carbon_local/carbon_whatif.md
@@ -1,5 +1,40 @@
# farm_ai/carbon_local/carbon_whatif
+Computes the offset amount of carbon that would be sequestered in a seasonal field using the baseline (historical) and scenario (time range interested in) information. To derive amount of carbon, it relies on seasonal information information provided for both baseline and scenario. The baseline represents historical information of farm practices used during each season that includes fertilizers, tillage, harvest and organic amendment. Minimum 2 years of baseline information required to execute the workflow. The scenario represents future farm practices planning to do during each season that includes fertilizers, tillage, harvest and organic amendment. For the scenario information provided, the workflow compute the offset amount of carbon that would be sequestrated in a seasonal field. Minimum 2years of baseline information required to execute the workflow. The requests received by workflow are forwarded to comet api. To know more information of comet refer to https://gitlab.com/comet-api/api-docs/-/tree/master/. To understand the enumerations and information accepted by comet refer to https://gitlab.com/comet-api/api-docs/-/blob/master/COMET-Farm_API_File_Specification.xlsx The request submitted get executed with in 5 minutes to max 2 hours. If response not received from comet within this time period, check comet_support_email for information on failed requests, if no emails received check status of requests by contacting to this support email address of comet "appnrel@colostate.edu". For public use comet limits 50 requests each day. If more requests need to send contact support email address.
+
+```{mermaid}
+ graph TD
+ inp1>baseline_seasonal_fields]
+ inp2>scenario_seasonal_fields]
+ out1>carbon_output]
+ tsk1{{comet_task}}
+ inp1>baseline_seasonal_fields] -- baseline_seasonal_fields --> tsk1{{comet_task}}
+ inp2>scenario_seasonal_fields] -- scenario_seasonal_fields --> tsk1{{comet_task}}
+ tsk1{{comet_task}} -- carbon_output --> out1>carbon_output]
+```
+
+## Sources
+
+- **baseline_seasonal_fields**: List of seasonal fields that holds the historical information of farm practices such as fertilizers, tillage, harvest and organic amendment.
+
+- **scenario_seasonal_fields**: List of seasonal fields that holds the future information of farm practices such as fertilizers, tillage, harvest and organic amendment.
+
+## Sinks
+
+- **carbon_output**: Carbon sequestration received for scenario information provided as input.
+
+## Parameters
+
+- **comet_support_email**: COMET-Farm API Registered email. The requests are forwarded to comet with this email reference. This email used by comet to share the information back to you for failed requests.
+
+- **ngrok_token**: NGROK session token. FarmVibes generate web_hook url and shared url with comet along the request to receive the response from comet. It's publicly accessible url and it's unique for each session. The url gets destroyed once the session ends. To start the ngrok session a token, it is generated from this url https://dashboard.ngrok.com/
+
+## Tasks
+
+- **comet_task**: Computes the offset amount of carbon that would be sequestered in a seasonal field using the baseline (historical) and scenario (time range interested in) information.
+
+## Workflow Yaml
+
```yaml
name: carbon_whatif
@@ -61,15 +96,4 @@ description:
url https://dashboard.ngrok.com/
-```
-
-```{mermaid}
- graph TD
- inp1>baseline_seasonal_fields]
- inp2>scenario_seasonal_fields]
- out1>carbon_output]
- tsk1{{comet_task}}
- inp1>baseline_seasonal_fields] -- baseline_seasonal_fields --> tsk1{{comet_task}}
- inp2>scenario_seasonal_fields] -- scenario_seasonal_fields --> tsk1{{comet_task}}
- tsk1{{comet_task}} -- carbon_output --> out1>carbon_output]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/land_cover_mapping/conservation_practices.md b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/land_cover_mapping/conservation_practices.md
index 07ed936b..7d65b7c5 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/land_cover_mapping/conservation_practices.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/land_cover_mapping/conservation_practices.md
@@ -1,5 +1,88 @@
# farm_ai/land_cover_mapping/conservation_practices
+Identifies conservation practices (terraces and grassed waterways) using elevation data. The workflow classifies pixels in terraces or grassed waterways. It starts downloading NAIP and USGS 3DEP tiles. Then, it computes the elevation gradient using a Sobel filter. And it computes local clusters using an overlap clustering method. Then, it combines cluster and elevation tiles to compute the average elevation per cluster. Finally, it uses a CNN model to classify pixels in either terraces or grassed waterways.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>dem_raster]
+ out2>naip_raster]
+ out3>dem_gradient]
+ out4>cluster]
+ out5>average_elevation]
+ out6>practices]
+ tsk1{{naip}}
+ tsk2{{cluster}}
+ tsk3{{dem}}
+ tsk4{{gradient}}
+ tsk5{{match_grad}}
+ tsk6{{match_elev}}
+ tsk7{{avg_elev}}
+ tsk8{{practice}}
+ tsk1{{naip}} -- raster/user_input --> tsk3{{dem}}
+ tsk1{{naip}} -- raster/input_raster --> tsk2{{cluster}}
+ tsk1{{naip}} -- raster/ref_rasters --> tsk6{{match_elev}}
+ tsk1{{naip}} -- raster/ref_rasters --> tsk5{{match_grad}}
+ tsk3{{dem}} -- raster --> tsk4{{gradient}}
+ tsk3{{dem}} -- raster/rasters --> tsk6{{match_elev}}
+ tsk4{{gradient}} -- gradient/rasters --> tsk5{{match_grad}}
+ tsk2{{cluster}} -- output_raster/input_cluster_raster --> tsk7{{avg_elev}}
+ tsk6{{match_elev}} -- match_rasters/input_dem_raster --> tsk7{{avg_elev}}
+ tsk7{{avg_elev}} -- output_raster/average_elevation --> tsk8{{practice}}
+ tsk5{{match_grad}} -- match_rasters/elevation_gradient --> tsk8{{practice}}
+ inp1>user_input] -- user_input --> tsk1{{naip}}
+ tsk3{{dem}} -- raster --> out1>dem_raster]
+ tsk1{{naip}} -- raster --> out2>naip_raster]
+ tsk4{{gradient}} -- gradient --> out3>dem_gradient]
+ tsk2{{cluster}} -- output_raster --> out4>cluster]
+ tsk7{{avg_elev}} -- output_raster --> out5>average_elevation]
+ tsk8{{practice}} -- output_raster --> out6>practices]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **dem_raster**: USGS 3DEP tiles that overlap the NAIP tiles that overlap the area of interest.
+
+- **naip_raster**: NAIP tiles that overlap the area of interest.
+
+- **dem_gradient**: A copy of the USGS 3DEP tiles where the pixel values are the gradient computed using the Sobel filter.
+
+- **cluster**: A copy of the NAIP tiles with one band representing the output of the overlap clustering method. Each pixel has a value between one and four.
+
+- **average_elevation**: A combination of the dem_gradient and cluster sinks, where each pixel value is the average elevation of all pixels that fall in the same cluster.
+
+- **practices**: A copy of the NAIP tile with one band where each pixel value refers to a conservation practice (0 = none, 1 = terraces, 2 = grassed waterways).
+
+## Parameters
+
+- **clustering_iterations**: The number of iterations used in the overlap clustering method.
+
+- **pc_key**: Optional Planetary Computer API key.
+
+## Tasks
+
+- **naip**: Downloads NAIP tiles that intersect with the input geometry and time range.
+
+- **cluster**: Computes local clusters using an overlap clustering method.
+
+- **dem**: Downloads digital elevation map tiles that intersect with the input geometry and time range.
+
+- **gradient**: Computes the gradient of each band of the input raster with a Sobel operator.
+
+- **match_grad**: Resamples input rasters to the reference rasters' grid.
+
+- **match_elev**: Resamples input rasters to the reference rasters' grid.
+
+- **avg_elev**: Computes average elevation per-class in overlapping windows, combining cluster and elevation tiles.
+
+- **practice**: Classifies pixels in either terraces or grassed waterways using a CNN model.
+
+## Workflow Yaml
+
```yaml
name: conservation_practices
@@ -95,41 +178,4 @@ description:
pc_key: Optional Planetary Computer API key.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>dem_raster]
- out2>naip_raster]
- out3>dem_gradient]
- out4>cluster]
- out5>average_elevation]
- out6>practices]
- tsk1{{naip}}
- tsk2{{cluster}}
- tsk3{{dem}}
- tsk4{{gradient}}
- tsk5{{match_grad}}
- tsk6{{match_elev}}
- tsk7{{avg_elev}}
- tsk8{{practice}}
- tsk1{{naip}} -- raster/user_input --> tsk3{{dem}}
- tsk1{{naip}} -- raster/input_raster --> tsk2{{cluster}}
- tsk1{{naip}} -- raster/ref_rasters --> tsk6{{match_elev}}
- tsk1{{naip}} -- raster/ref_rasters --> tsk5{{match_grad}}
- tsk3{{dem}} -- raster --> tsk4{{gradient}}
- tsk3{{dem}} -- raster/rasters --> tsk6{{match_elev}}
- tsk4{{gradient}} -- gradient/rasters --> tsk5{{match_grad}}
- tsk2{{cluster}} -- output_raster/input_cluster_raster --> tsk7{{avg_elev}}
- tsk6{{match_elev}} -- match_rasters/input_dem_raster --> tsk7{{avg_elev}}
- tsk7{{avg_elev}} -- output_raster/average_elevation --> tsk8{{practice}}
- tsk5{{match_grad}} -- match_rasters/elevation_gradient --> tsk8{{practice}}
- inp1>user_input] -- user_input --> tsk1{{naip}}
- tsk3{{dem}} -- raster --> out1>dem_raster]
- tsk1{{naip}} -- raster --> out2>naip_raster]
- tsk4{{gradient}} -- gradient --> out3>dem_gradient]
- tsk2{{cluster}} -- output_raster --> out4>cluster]
- tsk7{{avg_elev}} -- output_raster --> out5>average_elevation]
- tsk8{{practice}} -- output_raster --> out6>practices]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/land_degradation/landsat_ndvi_trend.md b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/land_degradation/landsat_ndvi_trend.md
index 8836bf98..8d1dcb46 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/land_degradation/landsat_ndvi_trend.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/land_degradation/landsat_ndvi_trend.md
@@ -1,5 +1,42 @@
# farm_ai/land_degradation/landsat_ndvi_trend
+Estimates a linear trend over NDVI computer over LANDSAT tiles that intersect with the input geometry and time range. The workflow downloads LANDSAT data, compute NDVI over them, and estimate a linear trend over chunks of data, combining them into a final trend raster.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>ndvi]
+ out2>linear_trend]
+ tsk1{{landsat}}
+ tsk2{{trend}}
+ tsk1{{landsat}} -- raster --> tsk2{{trend}}
+ inp1>user_input] -- user_input --> tsk1{{landsat}}
+ tsk2{{trend}} -- ndvi_raster --> out1>ndvi]
+ tsk2{{trend}} -- linear_trend --> out2>linear_trend]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **ndvi**: NDVI rasters.
+
+- **linear_trend**: Raster with the trend and the test statistics.
+
+## Parameters
+
+- **pc_key**: Optional Planetary Computer API key.
+
+## Tasks
+
+- **landsat**: Downloads and preprocesses LANDSAT tiles that intersect with the input geometry and time range.
+
+- **trend**: Computes the pixel-wise NDVI linear trend over the input raster.
+
+## Workflow Yaml
+
```yaml
name: landsat_ndvi_trend
@@ -37,17 +74,4 @@ description:
pc_key: Optional Planetary Computer API key.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>ndvi]
- out2>linear_trend]
- tsk1{{landsat}}
- tsk2{{trend}}
- tsk1{{landsat}} -- raster --> tsk2{{trend}}
- inp1>user_input] -- user_input --> tsk1{{landsat}}
- tsk2{{trend}} -- ndvi_raster --> out1>ndvi]
- tsk2{{trend}} -- linear_trend --> out2>linear_trend]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/land_degradation/ndvi_linear_trend.md b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/land_degradation/ndvi_linear_trend.md
index b320cf82..85f3761a 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/land_degradation/ndvi_linear_trend.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/land_degradation/ndvi_linear_trend.md
@@ -1,5 +1,38 @@
# farm_ai/land_degradation/ndvi_linear_trend
+Computes the pixel-wise NDVI linear trend over the input raster. The workflow computes the NDVI from the input raster, calculates the linear trend over chunks of data, combining them into the final raster.
+
+```{mermaid}
+ graph TD
+ inp1>raster]
+ out1>ndvi_raster]
+ out2>linear_trend]
+ tsk1{{ndvi}}
+ tsk2{{chunked_linear_trend}}
+ tsk1{{ndvi}} -- index_raster/input_rasters --> tsk2{{chunked_linear_trend}}
+ inp1>raster] -- raster --> tsk1{{ndvi}}
+ tsk1{{ndvi}} -- index_raster --> out1>ndvi_raster]
+ tsk2{{chunked_linear_trend}} -- linear_trend_raster --> out2>linear_trend]
+```
+
+## Sources
+
+- **raster**: Input raster.
+
+## Sinks
+
+- **ndvi_raster**: NDVI raster.
+
+- **linear_trend**: Raster with the trend and the test statistics.
+
+## Tasks
+
+- **ndvi**: Computes an index from the bands of an input raster.
+
+- **chunked_linear_trend**: Computes the pixel-wise linear trend of a list of rasters (e.g. NDVI).
+
+## Workflow Yaml
+
```yaml
name: ndvi_linear_trend
@@ -34,17 +67,4 @@ description:
linear_trend: Raster with the trend and the test statistics.
-```
-
-```{mermaid}
- graph TD
- inp1>raster]
- out1>ndvi_raster]
- out2>linear_trend]
- tsk1{{ndvi}}
- tsk2{{chunked_linear_trend}}
- tsk1{{ndvi}} -- index_raster/input_rasters --> tsk2{{chunked_linear_trend}}
- inp1>raster] -- raster --> tsk1{{ndvi}}
- tsk1{{ndvi}} -- index_raster --> out1>ndvi_raster]
- tsk2{{chunked_linear_trend}} -- linear_trend_raster --> out2>linear_trend]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/segmentation/segment_basemap.md b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/segmentation/segment_basemap.md
index 513eea50..38950101 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/segmentation/segment_basemap.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/segmentation/segment_basemap.md
@@ -1,17 +1,65 @@
# farm_ai/segmentation/segment_basemap
+Downloads basemap with BingMaps API and runs Segment Anything Model (SAM) over them with points and/or bounding boxes as prompts. The workflow lists and downloads basemaps tiles with BingMaps API, and merges them into a single raster. The raster is then split into chips of 1024x1024 pixels with an overlap defined by `spatial_overlap`. Chips intersecting with prompts are processed by SAM's image encoder, followed by prompt encoder and mask decoder. Before running the workflow, make sure the model has been imported into the cluster by running `scripts/export_prompt_segmentation_models.py`. The script will download the desired model weights from SAM repository, export the image encoder and mask decoder to ONNX format, and add them to the cluster. For more information, refer to the [FarmVibes.AI troubleshooting](https://microsoft.github.io/farmvibes-ai/docfiles/markdown/TROUBLESHOOTING.html) page in the documentation.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ inp2>prompts]
+ out1>basemap]
+ out2>segmentation_mask]
+ tsk1{{basemap_download}}
+ tsk2{{basemap_segmentation}}
+ tsk1{{basemap_download}} -- merged_basemap/input_raster --> tsk2{{basemap_segmentation}}
+ inp1>user_input] -- input_geometry --> tsk1{{basemap_download}}
+ inp1>user_input] -- input_geometry --> tsk2{{basemap_segmentation}}
+ inp2>prompts] -- input_prompts --> tsk2{{basemap_segmentation}}
+ tsk1{{basemap_download}} -- merged_basemap --> out1>basemap]
+ tsk2{{basemap_segmentation}} -- segmentation_mask --> out2>segmentation_mask]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+- **prompts**: ExternalReferences to the point and/or bounding box prompts. These are GeoJSON with coordinates, label (foreground/background) and prompt id (in case, the raster contains multiple entities that should be segmented in a single workflow run).
+
+## Sinks
+
+- **basemap**: Merged basemap used as input to the segmentation.
+
+- **segmentation_mask**: Output segmentation masks.
+
+## Parameters
+
+- **bingmaps_api_key**: Required BingMaps API key.
+
+- **basemap_zoom_level**: Zoom level of interest, ranging from 0 to 20. For instance, a zoom level of 1 corresponds to a resolution of 78271.52 m/pixel, a zoom level of 10 corresponds to 152.9 m/pixel, and a zoom level of 19 corresponds to 0.3 m/pixel. For more information on zoom levels and their corresponding scale and resolution, please refer to the BingMaps API documentation at https://learn.microsoft.com/en-us/bingmaps/articles/understanding-scale-and-resolution
+
+- **model_type**: SAM's image encoder backbone architecture, among 'vit_h', 'vit_l', or 'vit_b'. Before running the workflow, make sure the desired model has been exported to the cluster by running `scripts/export_sam_models.py`. For more information, refer to the FarmVibes.AI troubleshooting page in the documentation.
+
+- **spatial_overlap**: Percentage of spatial overlap between chips in the range of [0.0, 1.0).
+
+## Tasks
+
+- **basemap_download**: Downloads Bing Maps basemap tiles and merges them into a single raster.
+
+- **basemap_segmentation**: Runs Segment Anything Model (SAM) over BingMaps basemap rasters with points and/or bounding boxes as prompts.
+
+## Workflow Yaml
+
```yaml
name: segment_basemap
sources:
user_input:
- basemap_download.input_geometry
- - sam_inference.input_geometry
+ - basemap_segmentation.input_geometry
prompts:
- - ingest_points.user_input
+ - basemap_segmentation.input_prompts
sinks:
basemap: basemap_download.merged_basemap
- segmentation_mask: sam_inference.segmentation_mask
+ segmentation_mask: basemap_segmentation.segmentation_mask
parameters:
bingmaps_api_key: null
basemap_zoom_level: 14
@@ -23,21 +71,15 @@ tasks:
parameters:
api_key: '@from(bingmaps_api_key)'
zoom_level: '@from(basemap_zoom_level)'
- ingest_points:
- workflow: data_ingestion/user_data/ingest_geometry
- sam_inference:
- op: basemap_prompt_segmentation
- op_dir: segment_anything
+ basemap_segmentation:
+ workflow: ml/segment_anything/basemap_prompt_segmentation
parameters:
model_type: '@from(model_type)'
spatial_overlap: '@from(spatial_overlap)'
edges:
- origin: basemap_download.merged_basemap
destination:
- - sam_inference.input_raster
-- origin: ingest_points.geometry
- destination:
- - sam_inference.input_prompts
+ - basemap_segmentation.input_raster
description:
short_description: Downloads basemap with BingMaps API and runs Segment Anything
Model (SAM) over them with points and/or bounding boxes as prompts.
@@ -62,22 +104,4 @@ description:
segmentation_mask: Output segmentation masks.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- inp2>prompts]
- out1>basemap]
- out2>segmentation_mask]
- tsk1{{basemap_download}}
- tsk2{{ingest_points}}
- tsk3{{sam_inference}}
- tsk1{{basemap_download}} -- merged_basemap/input_raster --> tsk3{{sam_inference}}
- tsk2{{ingest_points}} -- geometry/input_prompts --> tsk3{{sam_inference}}
- inp1>user_input] -- input_geometry --> tsk1{{basemap_download}}
- inp1>user_input] -- input_geometry --> tsk3{{sam_inference}}
- inp2>prompts] -- user_input --> tsk2{{ingest_points}}
- tsk1{{basemap_download}} -- merged_basemap --> out1>basemap]
- tsk3{{sam_inference}} -- segmentation_mask --> out2>segmentation_mask]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/segmentation/segment_s2.md b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/segmentation/segment_s2.md
index a5c8a115..6cac4be5 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/segmentation/segment_s2.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/segmentation/segment_s2.md
@@ -1,17 +1,63 @@
# farm_ai/segmentation/segment_s2
+Downloads Sentinel-2 imagery and runs Segment Anything Model (SAM) over them with points and/or bounding boxes as prompts. The workflow retrieves the relevant Sentinel-2 products with the Planetary Computer (PC) API, and splits the input rasters into chips of 1024x1024 pixels with an overlap defined by `spatial_overlap`. Chips intersecting with prompts are processed by SAM's image encoder, followed by prompt encoder and mask decoder. Before running the workflow, make sure the model has been imported into the cluster by running `scripts/export_prompt_segmentation_models.py`. The script will download the desired model weights from SAM repository, export the image encoder and mask decoder to ONNX format, and add them to the cluster. For more information, refer to the [FarmVibes.AI troubleshooting](https://microsoft.github.io/farmvibes-ai/docfiles/markdown/TROUBLESHOOTING.html) page in the documentation.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ inp2>prompts]
+ out1>s2_raster]
+ out2>segmentation_mask]
+ tsk1{{preprocess_s2}}
+ tsk2{{s2_segmentation}}
+ tsk1{{preprocess_s2}} -- raster/input_raster --> tsk2{{s2_segmentation}}
+ inp1>user_input] -- user_input --> tsk1{{preprocess_s2}}
+ inp1>user_input] -- input_geometry --> tsk2{{s2_segmentation}}
+ inp2>prompts] -- input_prompts --> tsk2{{s2_segmentation}}
+ tsk1{{preprocess_s2}} -- raster --> out1>s2_raster]
+ tsk2{{s2_segmentation}} -- segmentation_mask --> out2>segmentation_mask]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+- **prompts**: ExternalReferences to the point and/or bounding box prompts. These are GeoJSON with coordinates, label (foreground/background) and prompt id (in case, the raster contains multiple entities that should be segmented in a single workflow run).
+
+## Sinks
+
+- **s2_raster**: Sentinel-2 rasters used as input for the segmentation.
+
+- **segmentation_mask**: Output segmentation masks.
+
+## Parameters
+
+- **model_type**: SAM's image encoder backbone architecture, among 'vit_h', 'vit_l', or 'vit_b'. Before running the workflow, make sure the desired model has been exported to the cluster by running `scripts/export_sam_models.py`. For more information, refer to the FarmVibes.AI troubleshooting page in the documentation.
+
+- **spatial_overlap**: Percentage of spatial overlap between chips in the range of [0.0, 1.0).
+
+- **pc_key**: Optional Planetary Computer API key.
+
+## Tasks
+
+- **preprocess_s2**: Downloads and preprocesses Sentinel-2 imagery that covers the input geometry and time range.
+
+- **s2_segmentation**: Runs Segment Anything Model (SAM) over Sentinel-2 rasters with points and/or bounding boxes as prompts.
+
+## Workflow Yaml
+
```yaml
name: segment_s2
sources:
user_input:
- preprocess_s2.user_input
- - sam_inference.input_geometry
+ - s2_segmentation.input_geometry
prompts:
- - ingest_points.user_input
+ - s2_segmentation.input_prompts
sinks:
s2_raster: preprocess_s2.raster
- segmentation_mask: sam_inference.segmentation_mask
+ segmentation_mask: s2_segmentation.segmentation_mask
parameters:
model_type: vit_b
spatial_overlap: 0.5
@@ -21,21 +67,15 @@ tasks:
workflow: data_ingestion/sentinel2/preprocess_s2
parameters:
pc_key: '@from(pc_key)'
- ingest_points:
- workflow: data_ingestion/user_data/ingest_geometry
- sam_inference:
- op: s2_prompt_segmentation
- op_dir: segment_anything
+ s2_segmentation:
+ workflow: ml/segment_anything/s2_prompt_segmentation
parameters:
model_type: '@from(model_type)'
spatial_overlap: '@from(spatial_overlap)'
edges:
- origin: preprocess_s2.raster
destination:
- - sam_inference.input_raster
-- origin: ingest_points.geometry
- destination:
- - sam_inference.input_prompts
+ - s2_segmentation.input_raster
description:
short_description: Downloads Sentinel-2 imagery and runs Segment Anything Model
(SAM) over them with points and/or bounding boxes as prompts.
@@ -60,22 +100,4 @@ description:
segmentation_mask: Output segmentation masks.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- inp2>prompts]
- out1>s2_raster]
- out2>segmentation_mask]
- tsk1{{preprocess_s2}}
- tsk2{{ingest_points}}
- tsk3{{sam_inference}}
- tsk1{{preprocess_s2}} -- raster/input_raster --> tsk3{{sam_inference}}
- tsk2{{ingest_points}} -- geometry/input_prompts --> tsk3{{sam_inference}}
- inp1>user_input] -- user_input --> tsk1{{preprocess_s2}}
- inp1>user_input] -- input_geometry --> tsk3{{sam_inference}}
- inp2>prompts] -- user_input --> tsk2{{ingest_points}}
- tsk1{{preprocess_s2}} -- raster --> out1>s2_raster]
- tsk3{{sam_inference}} -- segmentation_mask --> out2>segmentation_mask]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/sensor/optimal_locations.md b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/sensor/optimal_locations.md
index 0b0ededd..354e86c1 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/sensor/optimal_locations.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/sensor/optimal_locations.md
@@ -1,5 +1,63 @@
# farm_ai/sensor/optimal_locations
+Identify optimal locations by performing clustering operation using Gaussian Mixture model on computed raster indices. The clustering operation separate computed raster indices values into n groups of equal variance, each group assigned a location and that location is considered as a
+optimal locations. The sample locations generated provide information of latitude and longitude. The optimal location can be utilized to install sensors and collect
+soil information. The index parameter used as input to run the computed index workflow internally using the input raster submitted. The selection of index parameter varies
+based on requirement. The workflow supports all the indices supported by spyndex library (https://github.com/awesome-spectral-indices/awesome-spectral-indices#vegetation).
+Below provided various indices that are used to identify optimal locations and generated a nutrients heatmap.
+Enhanced Vegetation Index (EVI) - EVI is designed to minimize the influence of soil brightness and atmospheric conditions on vegetation assessment. It is calculated
+using the red, blue, and near-infrared (NIR) bands. EVI is particularly useful for monitoring vegetation in regions with high canopy cover and in areas where atmospheric
+interference is significant. This indices also used in notebook (notebooks/heatmaps/nutrients_using_neighbors.ipynb) that derive nutrient information for Carbon, Nitrogen,
+and Phosphorus.
+Photochemical Reflectance Index (PRI) - It is a vegetation index used to assess the light-use efficiency of plants in terms of photosynthesis and their response to
+changes in light conditions, particularly variations in the blue and red parts of the electromagnetic spectrum. This index also used in notebook
+(notebooks/heatmaps/nutrients_using_neighbors.ipynb) that derive nutrient information for pH.
+The number of sample locations generated depend on input parameters submitted. Tune n_clusters and sieve_size parameters to generate more or less location data points.
+For a 100 acre farm,
+- 20 sample locations are generated using n_clusters=5 and sieve_size=10.
+- 30 sample locations are generated using n_clusters=5 and sieve_size=20.
+- 80 sample locations are generated using n_clusters=5 and sieve_size=5.
+- 130 sample locations are generated using n_clusters=8 and sieve_size=5.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ inp2>input_raster]
+ out1>result]
+ tsk1{{compute_index}}
+ tsk2{{find_samples}}
+ tsk1{{compute_index}} -- index_raster/raster --> tsk2{{find_samples}}
+ inp1>user_input] -- user_input --> tsk2{{find_samples}}
+ inp2>input_raster] -- raster --> tsk1{{compute_index}}
+ tsk2{{find_samples}} -- locations --> out1>result]
+```
+
+## Sources
+
+- **input_raster**: List of computed raster indices generated using the sentinel 2 satellite imagery.
+
+- **user_input**: DataVibe with time range information.
+
+## Sinks
+
+- **result**: Zip file containing sample locations in a shape file (.shp) format.
+
+## Parameters
+
+- **n_clusters**: number of clusters used to generate sample locations.
+
+- **sieve_size**: Group the nearest neighbor pixel values.
+
+- **index**: Index used to generate sample locations.
+
+## Tasks
+
+- **compute_index**: Computes an index from the bands of an input raster.
+
+- **find_samples**: Find minimum soil sample locations by grouping indices values that are derived from satellite or spaceEye imagery bands.
+
+## Workflow Yaml
+
```yaml
name: optimal_locations
@@ -71,17 +129,4 @@ description:
index: Index used to generate sample locations.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- inp2>input_raster]
- out1>result]
- tsk1{{compute_index}}
- tsk2{{find_samples}}
- tsk1{{compute_index}} -- index_raster/raster --> tsk2{{find_samples}}
- inp1>user_input] -- user_input --> tsk2{{find_samples}}
- inp2>input_raster] -- raster --> tsk1{{compute_index}}
- tsk2{{find_samples}} -- locations --> out1>result]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/water/irrigation_classification.md b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/water/irrigation_classification.md
index db666502..4ff91326 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/farm_ai/water/irrigation_classification.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/farm_ai/water/irrigation_classification.md
@@ -1,5 +1,131 @@
# farm_ai/water/irrigation_classification
+Develops 30m pixel-wise irrigation probability map. The workflow retrieves LANDSAT 8 Surface Reflectance (SR) image tile and land surface elevation DEM data, and runs four ops to compute irrigation probability map. The land surface elevation data source are 10m USGS DEM, or 30m Copernicus DEM; but Copernicus DEM is set as the default source in the workflow. Landsat Op compute_cloud_water_mask utilizes the qa_pixel band of image and NDVI index to generate mask of cloud cover and water bodies. Op compute_evaporative_fraction utilizes NDVI index, land surface temperature (LST), green and near infra-red bands, and DEM data to estimate evaporative flux (ETRF). Op compute_ngi_egi_layers utilizes NDVI index, ETRF estimates, green and near infra-red bands to generate NGI and EGI irrigation layers. Lastly op compute_irrigation_probability uses NGI and EGI layers along with LST band; and applies optimized logistic regression model to compute 30m pixel-wise irrigation probability map. The coeficients and intercept of the model were obtained beforehand using as ground-truth data from Nebraska state, USA for the year 2015.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>landsat_bands]
+ out2>ndvi]
+ out3>cloud_water_mask]
+ out4>dem]
+ out5>evaporative_fraction]
+ out6>ngi]
+ out7>egi]
+ out8>lst]
+ out9>irrigation_probability]
+ tsk1{{landsat}}
+ tsk2{{ndvi}}
+ tsk3{{merge_geom}}
+ tsk4{{merge_geom_time_range}}
+ tsk5{{cloud_water_mask}}
+ tsk6{{dem}}
+ tsk7{{match_dem}}
+ tsk8{{evaporative_fraction}}
+ tsk9{{ngi_egi_layers}}
+ tsk10{{irrigation_probability}}
+ tsk1{{landsat}} -- raster/items --> tsk3{{merge_geom}}
+ tsk1{{landsat}} -- raster --> tsk2{{ndvi}}
+ tsk1{{landsat}} -- raster/landsat_raster --> tsk5{{cloud_water_mask}}
+ tsk1{{landsat}} -- raster/ref_rasters --> tsk7{{match_dem}}
+ tsk1{{landsat}} -- raster/landsat_raster --> tsk8{{evaporative_fraction}}
+ tsk1{{landsat}} -- raster/landsat_raster --> tsk9{{ngi_egi_layers}}
+ tsk1{{landsat}} -- raster/landsat_raster --> tsk10{{irrigation_probability}}
+ tsk2{{ndvi}} -- index/ndvi_raster --> tsk5{{cloud_water_mask}}
+ tsk2{{ndvi}} -- index/ndvi_raster --> tsk8{{evaporative_fraction}}
+ tsk2{{ndvi}} -- index/ndvi_raster --> tsk9{{ngi_egi_layers}}
+ tsk3{{merge_geom}} -- merged/geometry --> tsk4{{merge_geom_time_range}}
+ tsk4{{merge_geom_time_range}} -- merged/user_input --> tsk6{{dem}}
+ tsk6{{dem}} -- raster/rasters --> tsk7{{match_dem}}
+ tsk7{{match_dem}} -- match_rasters/dem_raster --> tsk8{{evaporative_fraction}}
+ tsk8{{evaporative_fraction}} -- evaporative_fraction --> tsk9{{ngi_egi_layers}}
+ tsk5{{cloud_water_mask}} -- cloud_water_mask/cloud_water_mask_raster --> tsk8{{evaporative_fraction}}
+ tsk5{{cloud_water_mask}} -- cloud_water_mask/cloud_water_mask_raster --> tsk9{{ngi_egi_layers}}
+ tsk5{{cloud_water_mask}} -- cloud_water_mask/cloud_water_mask_raster --> tsk10{{irrigation_probability}}
+ tsk9{{ngi_egi_layers}} -- ngi --> tsk10{{irrigation_probability}}
+ tsk9{{ngi_egi_layers}} -- egi --> tsk10{{irrigation_probability}}
+ tsk9{{ngi_egi_layers}} -- lst --> tsk10{{irrigation_probability}}
+ inp1>user_input] -- user_input --> tsk1{{landsat}}
+ inp1>user_input] -- time_range --> tsk4{{merge_geom_time_range}}
+ tsk1{{landsat}} -- raster --> out1>landsat_bands]
+ tsk2{{ndvi}} -- index --> out2>ndvi]
+ tsk5{{cloud_water_mask}} -- cloud_water_mask --> out3>cloud_water_mask]
+ tsk7{{match_dem}} -- match_rasters --> out4>dem]
+ tsk8{{evaporative_fraction}} -- evaporative_fraction --> out5>evaporative_fraction]
+ tsk9{{ngi_egi_layers}} -- ngi --> out6>ngi]
+ tsk9{{ngi_egi_layers}} -- egi --> out7>egi]
+ tsk9{{ngi_egi_layers}} -- lst --> out8>lst]
+ tsk10{{irrigation_probability}} -- irrigation_probability --> out9>irrigation_probability]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **landsat_bands**: Raster of Landsat bands.
+
+- **ndvi**: NDVI raster.
+
+- **cloud_water_mask**: Mask of cloud cover and water bodies.
+
+- **dem**: DEM raster. Options are CopernicusDEM30 and USGS3DEP.
+
+- **evaporative_fraction**: Raster with estimates of evaporative fraction flux.
+
+- **ngi**: Raster of NGI irrigation layer.
+
+- **egi**: Raster of EGI irrigation layer.
+
+- **lst**: Raster of land surface temperature.
+
+- **irrigation_probability**: Raster of irrigation probability map in 30m resolution.
+
+## Parameters
+
+- **ndvi_threshold**: NDVI index threshold value for masking water bodies.
+
+- **ndvi_hot_threshold**: Maximum NDVI index threshold value for selecting hot pixel.
+
+- **coef_ngi**: Coefficient of NGI layer in optimized logistic regression model.
+
+- **coef_egi**: Coefficient of EGI layer in optimized logistic regression model.
+
+- **coef_lst**: Coefficient of land surface temperature band in optimized logistic regression model.
+
+- **intercept**: Intercept value of optimized logistic regression model.
+
+- **dem_resolution**: Spatial resolution of the DEM. 10m and 30m are available.
+
+- **dem_provider**: Provider of the DEM. "USGS3DEP" and "CopernicusDEM30" are available.
+
+- **pc_key**: Optional Planetary Computer API key.
+
+## Tasks
+
+- **landsat**: Downloads and preprocesses LANDSAT tiles that intersect with the input geometry and time range.
+
+- **ndvi**: Computes `index` over the input raster.
+
+- **merge_geom**: Create item with merged geometry from item list.
+
+- **merge_geom_time_range**: Create item that contains the geometry from one item and the time range from another.
+
+- **cloud_water_mask**: Merges landsat cloud mask and NDVI-based mask to produce a cloud water mask.
+
+- **dem**: Downloads digital elevation map tiles that intersect with the input geometry and time range.
+
+- **match_dem**: Resamples input rasters to the reference rasters' grid.
+
+- **evaporative_fraction**: Computes evaporative fraction layer based on the percentile values of lst_dem (created by treating land surface temperature with dem) and ndvi layers. The source of constants used is "Senay, G.B.; Bohms, S.; Singh, R.K.; Gowda, P.H.; Velpuri, N.M.; Alemu, H.; Verdin, J.P. Operational Evapotranspiration Mapping Using Remote Sensing and Weather Datasets - A New Parameterization for the SSEB Approach. JAWRA J. Am. Water Resour. Assoc. 2013, 49, 577–591. The land surface elevation data source are 10m USGS DEM, and 30m Copernicus DEM; but Copernicus DEM is set as default source in the workflow.
+
+- **ngi_egi_layers**: Computes NGI, EGI, and LST layers from landsat bands, ndvi layer, cloud water mask layer and evaporative fraction layer
+
+- **irrigation_probability**: Computes irrigation probability values for each pixel in raster using optimized logistic regression model with ngi, egi, and lst rasters as input
+
+## Workflow Yaml
+
```yaml
name: irrigation_classification
@@ -145,60 +271,4 @@ description:
pc_key: Optional Planetary Computer API key.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>landsat_bands]
- out2>ndvi]
- out3>cloud_water_mask]
- out4>dem]
- out5>evaporative_fraction]
- out6>ngi]
- out7>egi]
- out8>lst]
- out9>irrigation_probability]
- tsk1{{landsat}}
- tsk2{{ndvi}}
- tsk3{{merge_geom}}
- tsk4{{merge_geom_time_range}}
- tsk5{{cloud_water_mask}}
- tsk6{{dem}}
- tsk7{{match_dem}}
- tsk8{{evaporative_fraction}}
- tsk9{{ngi_egi_layers}}
- tsk10{{irrigation_probability}}
- tsk1{{landsat}} -- raster/items --> tsk3{{merge_geom}}
- tsk1{{landsat}} -- raster --> tsk2{{ndvi}}
- tsk1{{landsat}} -- raster/landsat_raster --> tsk5{{cloud_water_mask}}
- tsk1{{landsat}} -- raster/ref_rasters --> tsk7{{match_dem}}
- tsk1{{landsat}} -- raster/landsat_raster --> tsk8{{evaporative_fraction}}
- tsk1{{landsat}} -- raster/landsat_raster --> tsk9{{ngi_egi_layers}}
- tsk1{{landsat}} -- raster/landsat_raster --> tsk10{{irrigation_probability}}
- tsk2{{ndvi}} -- index/ndvi_raster --> tsk5{{cloud_water_mask}}
- tsk2{{ndvi}} -- index/ndvi_raster --> tsk8{{evaporative_fraction}}
- tsk2{{ndvi}} -- index/ndvi_raster --> tsk9{{ngi_egi_layers}}
- tsk3{{merge_geom}} -- merged/geometry --> tsk4{{merge_geom_time_range}}
- tsk4{{merge_geom_time_range}} -- merged/user_input --> tsk6{{dem}}
- tsk6{{dem}} -- raster/rasters --> tsk7{{match_dem}}
- tsk7{{match_dem}} -- match_rasters/dem_raster --> tsk8{{evaporative_fraction}}
- tsk8{{evaporative_fraction}} -- evaporative_fraction --> tsk9{{ngi_egi_layers}}
- tsk5{{cloud_water_mask}} -- cloud_water_mask/cloud_water_mask_raster --> tsk8{{evaporative_fraction}}
- tsk5{{cloud_water_mask}} -- cloud_water_mask/cloud_water_mask_raster --> tsk9{{ngi_egi_layers}}
- tsk5{{cloud_water_mask}} -- cloud_water_mask/cloud_water_mask_raster --> tsk10{{irrigation_probability}}
- tsk9{{ngi_egi_layers}} -- ngi --> tsk10{{irrigation_probability}}
- tsk9{{ngi_egi_layers}} -- egi --> tsk10{{irrigation_probability}}
- tsk9{{ngi_egi_layers}} -- lst --> tsk10{{irrigation_probability}}
- inp1>user_input] -- user_input --> tsk1{{landsat}}
- inp1>user_input] -- time_range --> tsk4{{merge_geom_time_range}}
- tsk1{{landsat}} -- raster --> out1>landsat_bands]
- tsk2{{ndvi}} -- index --> out2>ndvi]
- tsk5{{cloud_water_mask}} -- cloud_water_mask --> out3>cloud_water_mask]
- tsk7{{match_dem}} -- match_rasters --> out4>dem]
- tsk8{{evaporative_fraction}} -- evaporative_fraction --> out5>evaporative_fraction]
- tsk9{{ngi_egi_layers}} -- ngi --> out6>ngi]
- tsk9{{ngi_egi_layers}} -- egi --> out7>egi]
- tsk9{{ngi_egi_layers}} -- lst --> out8>lst]
- tsk10{{irrigation_probability}} -- irrigation_probability --> out9>irrigation_probability]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/forest_ai/deforestation/alos_trend_detection.md b/docs/source/docfiles/markdown/workflow_yaml/forest_ai/deforestation/alos_trend_detection.md
new file mode 100644
index 00000000..56cd8468
--- /dev/null
+++ b/docs/source/docfiles/markdown/workflow_yaml/forest_ai/deforestation/alos_trend_detection.md
@@ -0,0 +1,134 @@
+# forest_ai/deforestation/alos_trend_detection
+
+Detects increase/decrease trends in forest pixel levels over the user-input geometry and time range for the ALOS forest map. This workflow combines the alos_forest_extent_download_merge and ordinal_trend_detection workflows to detect increase/decrease trends in the forest pixel levels over the user-provided geometry and time range for the ALOS forest map. The ALOS PALSAR 2.1 Forest/Non-Forest Maps are downloaded in the alos_forest_extent_download_merge workflow. Then the ordinal_trend_detection workflow clips the ordinal raster to the user-provided geometry and time range and determines if there is an increasing or decreasing trend in the forest pixel levels over them. alos_trend_detection uses the Cochran-Armitage test to detect trends in the forest levels over the years. The null hypothesis is that there is no trend in the pixel levels over the list of rasters. The alternative hypothesis is that there is a trend in the forest pixel levels over the list of rasters (one for each year). It returns a p-value and a z-score. If the p-value is less than some significance level, the null hypothesis is rejected and the alternative hypothesis is accepted. If the z-score is positive, the trend is increasing. If the z-score is negative, the trend is decreasing.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>merged_raster]
+ out2>categorical_raster]
+ out3>recoded_raster]
+ out4>clipped_raster]
+ out5>trend_test_result]
+ tsk1{{alos_forest_extent_download_merge}}
+ tsk2{{ordinal_trend_detection}}
+ tsk1{{alos_forest_extent_download_merge}} -- merged_raster/raster --> tsk2{{ordinal_trend_detection}}
+ inp1>user_input] -- user_input --> tsk1{{alos_forest_extent_download_merge}}
+ inp1>user_input] -- input_geometry --> tsk2{{ordinal_trend_detection}}
+ tsk1{{alos_forest_extent_download_merge}} -- merged_raster --> out1>merged_raster]
+ tsk1{{alos_forest_extent_download_merge}} -- categorical_raster --> out2>categorical_raster]
+ tsk2{{ordinal_trend_detection}} -- recoded_raster --> out3>recoded_raster]
+ tsk2{{ordinal_trend_detection}} -- clipped_raster --> out4>clipped_raster]
+ tsk2{{ordinal_trend_detection}} -- trend_test_result --> out5>trend_test_result]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **merged_raster**: Merged raster of the ALOS PALSAR 2.1 Forest/Non-Forest Map for the user-provided geometry and time range.
+
+- **categorical_raster**: Categorical raster of the ALOS PALSAR 2.1 Forest/Non-Forest Map for the user-provided geometry and time range before the merge operation.
+
+- **recoded_raster**: Recoded raster of the ALOS PALSAR 2.1 Forest/Non-Forest Map for the user-provided geometry and time range.
+
+- **clipped_raster**: Clipped ordinal raster for the user-provided geometry and time range.
+
+- **trend_test_result**: Cochran-armitage test results composed of p-value and z-score.
+
+## Parameters
+
+- **pc_key**: Planetary Computer API key.
+
+- **from_values**: Values to recode from.
+
+- **to_values**: Values to recode to.
+
+## Tasks
+
+- **alos_forest_extent_download_merge**: Downloads Advanced Land Observing Satellite (ALOS) forest/non-forest classification map and merges it into a single raster.
+
+- **ordinal_trend_detection**: Detects increase/decrease trends in the pixel levels over the user-input geometry and time range.
+
+## Workflow Yaml
+
+```yaml
+
+name: alos_trend_detection
+sources:
+ user_input:
+ - alos_forest_extent_download_merge.user_input
+ - ordinal_trend_detection.input_geometry
+sinks:
+ merged_raster: alos_forest_extent_download_merge.merged_raster
+ categorical_raster: alos_forest_extent_download_merge.categorical_raster
+ recoded_raster: ordinal_trend_detection.recoded_raster
+ clipped_raster: ordinal_trend_detection.clipped_raster
+ trend_test_result: ordinal_trend_detection.trend_test_result
+parameters:
+ pc_key: null
+ from_values:
+ - 4
+ - 3
+ - 0
+ - 2
+ - 1
+ to_values:
+ - 0
+ - 0
+ - 0
+ - 1
+ - 1
+tasks:
+ alos_forest_extent_download_merge:
+ workflow: data_ingestion/alos/alos_forest_extent_download_merge
+ parameters:
+ pc_key: '@from(pc_key)'
+ ordinal_trend_detection:
+ workflow: forest_ai/deforestation/ordinal_trend_detection
+ parameters:
+ from_values: '@from(from_values)'
+ to_values: '@from(to_values)'
+edges:
+- origin: alos_forest_extent_download_merge.merged_raster
+ destination:
+ - ordinal_trend_detection.raster
+description:
+ short_description: Detects increase/decrease trends in forest pixel levels over
+ the user-input geometry and time range for the ALOS forest map.
+ long_description: This workflow combines the alos_forest_extent_download_merge and
+ ordinal_trend_detection workflows to detect increase/decrease trends in the forest
+ pixel levels over the user-provided geometry and time range for the ALOS forest
+ map. The ALOS PALSAR 2.1 Forest/Non-Forest Maps are downloaded in the alos_forest_extent_download_merge
+ workflow. Then the ordinal_trend_detection workflow clips the ordinal raster
+ to the user-provided geometry and time range and determines if there is an increasing
+ or decreasing trend in the forest pixel levels over them. alos_trend_detection
+ uses the Cochran-Armitage test to detect trends in the forest levels over the
+ years. The null hypothesis is that there is no trend in the pixel levels over
+ the list of rasters. The alternative hypothesis is that there is a trend in the
+ forest pixel levels over the list of rasters (one for each year). It returns a
+ p-value and a z-score. If the p-value is less than some significance level, the
+ null hypothesis is rejected and the alternative hypothesis is accepted. If the
+ z-score is positive, the trend is increasing. If the z-score is negative, the
+ trend is decreasing.
+ sources:
+ user_input: Time range and geometry of interest.
+ sinks:
+ merged_raster: Merged raster of the ALOS PALSAR 2.1 Forest/Non-Forest Map for
+ the user-provided geometry and time range.
+ categorical_raster: Categorical raster of the ALOS PALSAR 2.1 Forest/Non-Forest
+ Map for the user-provided geometry and time range before the merge operation.
+ recoded_raster: Recoded raster of the ALOS PALSAR 2.1 Forest/Non-Forest Map for
+ the user-provided geometry and time range.
+ clipped_raster: Clipped ordinal raster for the user-provided geometry and time
+ range.
+ trend_test_result: Cochran-armitage test results composed of p-value and z-score.
+ parameters:
+ pc_key: Planetary Computer API key.
+ from_values: Values to recode from.
+ to_values: Values to recode to.
+
+
+```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/forest_ai/deforestation/ordinal_trend_detection.md b/docs/source/docfiles/markdown/workflow_yaml/forest_ai/deforestation/ordinal_trend_detection.md
new file mode 100644
index 00000000..bb347a72
--- /dev/null
+++ b/docs/source/docfiles/markdown/workflow_yaml/forest_ai/deforestation/ordinal_trend_detection.md
@@ -0,0 +1,123 @@
+# forest_ai/deforestation/ordinal_trend_detection
+
+Detects increase/decrease trends in the pixel levels over the user-input geometry and time range. This workflow prepares rasters to perform the Cochran-Armitage trend test over a user-provided geometry and time range. Initially, it recodes the input raster according to the 'from_values' and 'to_values' parameters. For example, if the original raster has values (2, 1, 3, 4, 5) and the default values of 'from_values' and 'to_values' are respectively [1, 2, 3, 4, 5] and [6, 7, 8, 9, 10], the recoded raster will have values (7, 6, 8, 9, 10). The workflow then clips the user-provided geometries and computes an ordinal raster. It also counts each unique pixel present in the recoded rasters to create a pixel frequency contingency table. This data is used to determine if there is an increasing or decreasing trend in pixel levels. The Cochran-Armitage test is a non-parametric test used to ascertain this trend. The null hypothesis assumes no trend in pixel levels, while the alternative hypothesis assumes a trend exists. The test returns a p-value and a z-score. If the p-value is less than some significance level, the null hypothesis is rejected in favor of the alternative. A positive z-score indicates an increasing trend, while a negative one indicates a decreasing trend.
+
+```{mermaid}
+ graph TD
+ inp1>raster]
+ inp2>input_geometry]
+ out1>recoded_raster]
+ out2>trend_test_result]
+ out3>clipped_raster]
+ tsk1{{recode_raster}}
+ tsk2{{clip}}
+ tsk3{{compute_pixel_count}}
+ tsk4{{trend_test}}
+ tsk1{{recode_raster}} -- recoded_raster/raster --> tsk2{{clip}}
+ tsk2{{clip}} -- clipped_raster/raster --> tsk3{{compute_pixel_count}}
+ tsk3{{compute_pixel_count}} -- pixel_count --> tsk4{{trend_test}}
+ inp1>raster] -- raster --> tsk1{{recode_raster}}
+ inp2>input_geometry] -- input_geometry --> tsk2{{clip}}
+ tsk1{{recode_raster}} -- recoded_raster --> out1>recoded_raster]
+ tsk4{{trend_test}} -- ordinal_trend_result --> out2>trend_test_result]
+ tsk2{{clip}} -- clipped_raster --> out3>clipped_raster]
+```
+
+## Sources
+
+- **raster**: Raster to be processed and tested for trends.
+
+- **input_geometry**: Reference geometry.
+
+## Sinks
+
+- **recoded_raster**: Recoded raster for the user-provided geometry and time range.
+
+- **trend_test_result**: Cochran-armitage test results composed of p-value and z-score.
+
+- **clipped_raster**: Clipped ordinal raster for the user-provided geometry and time range.
+
+## Parameters
+
+- **from_values**: List of values to recode from.
+
+- **to_values**: List of values to recode to.
+
+## Tasks
+
+- **recode_raster**: Recodes values of the input raster.
+
+- **clip**: Performs a soft clip on an input raster based on a provided reference geometry.
+
+- **compute_pixel_count**: Counts the pixel values in the input raster.
+
+- **trend_test**: Detects increase/decrease trends over a list of Rasters.
+
+## Workflow Yaml
+
+```yaml
+
+name: ordinal_trend_detection
+sources:
+ raster:
+ - recode_raster.raster
+ input_geometry:
+ - clip.input_geometry
+sinks:
+ recoded_raster: recode_raster.recoded_raster
+ trend_test_result: trend_test.ordinal_trend_result
+ clipped_raster: clip.clipped_raster
+parameters:
+ from_values: []
+ to_values: []
+tasks:
+ recode_raster:
+ op: recode_raster
+ parameters:
+ from_values: '@from(from_values)'
+ to_values: '@from(to_values)'
+ clip:
+ workflow: data_processing/clip/clip
+ compute_pixel_count:
+ op: compute_pixel_count
+ trend_test:
+ op: ordinal_trend_test
+edges:
+- origin: recode_raster.recoded_raster
+ destination:
+ - clip.raster
+- origin: clip.clipped_raster
+ destination:
+ - compute_pixel_count.raster
+- origin: compute_pixel_count.pixel_count
+ destination:
+ - trend_test.pixel_count
+description:
+ short_description: Detects increase/decrease trends in the pixel levels over the
+ user-input geometry and time range.
+ long_description: This workflow prepares rasters to perform the Cochran-Armitage
+ trend test over a user-provided geometry and time range. Initially, it recodes
+ the input raster according to the 'from_values' and 'to_values' parameters. For
+ example, if the original raster has values (2, 1, 3, 4, 5) and the default values
+ of 'from_values' and 'to_values' are respectively [1, 2, 3, 4, 5] and [6, 7, 8,
+ 9, 10], the recoded raster will have values (7, 6, 8, 9, 10). The workflow then
+ clips the user-provided geometries and computes an ordinal raster. It also counts
+ each unique pixel present in the recoded rasters to create a pixel frequency contingency
+ table. This data is used to determine if there is an increasing or decreasing
+ trend in pixel levels. The Cochran-Armitage test is a non-parametric test used
+ to ascertain this trend. The null hypothesis assumes no trend in pixel levels,
+ while the alternative hypothesis assumes a trend exists. The test returns a p-value
+ and a z-score. If the p-value is less than some significance level, the null hypothesis
+ is rejected in favor of the alternative. A positive z-score indicates an increasing
+ trend, while a negative one indicates a decreasing trend.
+ sources:
+ raster: Raster to be processed and tested for trends.
+ input_geometry: Reference geometry.
+ sinks:
+ recoded_raster: Recoded raster for the user-provided geometry and time range.
+ trend_test_result: Cochran-armitage test results composed of p-value and z-score.
+ clipped_raster: Clipped ordinal raster for the user-provided geometry and time
+ range.
+
+
+```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/helloworld.md b/docs/source/docfiles/markdown/workflow_yaml/helloworld.md
index e8c6f4eb..9dc52e08 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/helloworld.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/helloworld.md
@@ -1,5 +1,30 @@
# helloworld
+Hello world! Small test workflow that generates an image of the Earth with countries that intersect with the input geometry highlighted in orange.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>raster]
+ tsk1{{hello}}
+ inp1>user_input] -- user_input --> tsk1{{hello}}
+ tsk1{{hello}} -- raster --> out1>raster]
+```
+
+## Sources
+
+- **user_input**: Input geometry.
+
+## Sinks
+
+- **raster**: Raster with highlighted countries.
+
+## Tasks
+
+- **hello**: Test op that generates an image of the Earth with countries that intersect with the input geometry highlighted in orange.
+
+## Workflow Yaml
+
```yaml
name: helloworld
@@ -21,13 +46,4 @@ description:
raster: Raster with highlighted countries.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>raster]
- tsk1{{hello}}
- inp1>user_input] -- user_input --> tsk1{{hello}}
- tsk1{{hello}} -- raster --> out1>raster]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/ml/crop_segmentation.md b/docs/source/docfiles/markdown/workflow_yaml/ml/crop_segmentation.md
index 7f6b0232..20f17572 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/ml/crop_segmentation.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/ml/crop_segmentation.md
@@ -1,5 +1,50 @@
# ml/crop_segmentation
+Runs a crop segmentation model based on NDVI from SpaceEye imagery along the year. The workflow generates SpaceEye cloud-free data for the input region and time range and computes NDVI over those. NDVI values sampled regularly along the year are stacked as bands and used as input to the crop segmentation model.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>segmentation]
+ tsk1{{spaceeye}}
+ tsk2{{ndvi}}
+ tsk3{{group}}
+ tsk4{{inference}}
+ tsk1{{spaceeye}} -- raster --> tsk2{{ndvi}}
+ tsk2{{ndvi}} -- index_raster/rasters --> tsk3{{group}}
+ tsk3{{group}} -- sequence/input_raster --> tsk4{{inference}}
+ inp1>user_input] -- user_input --> tsk1{{spaceeye}}
+ tsk4{{inference}} -- output_raster --> out1>segmentation]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **segmentation**: Crop segmentation map at 10m resolution.
+
+## Parameters
+
+- **pc_key**: Optional Planetary Computer API key.
+
+- **model_file**: Path to the ONNX file containing the model architecture and weights.
+
+- **model_bands**: Number of NDVI bands to stack as the model input.
+
+## Tasks
+
+- **spaceeye**: Runs the SpaceEye cloud removal pipeline using an interpolation-based algorithm, yielding daily cloud-free images for the input geometry and time range.
+
+- **ndvi**: Computes an index from the bands of an input raster.
+
+- **group**: Selects "num" entries from a Raster list so that the output sequence has a fixed length.
+
+- **inference**: Processes a sequence of rasters with an ONNX model.
+
+## Workflow Yaml
+
```yaml
name: crop_segmentation
@@ -62,19 +107,4 @@ description:
model_bands: Number of NDVI bands to stack as the model input.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>segmentation]
- tsk1{{spaceeye}}
- tsk2{{ndvi}}
- tsk3{{group}}
- tsk4{{inference}}
- tsk1{{spaceeye}} -- raster --> tsk2{{ndvi}}
- tsk2{{ndvi}} -- index_raster/rasters --> tsk3{{group}}
- tsk3{{group}} -- sequence/input_raster --> tsk4{{inference}}
- inp1>user_input] -- user_input --> tsk1{{spaceeye}}
- tsk4{{inference}} -- output_raster --> out1>segmentation]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/ml/dataset_generation/datagen_crop_segmentation.md b/docs/source/docfiles/markdown/workflow_yaml/ml/dataset_generation/datagen_crop_segmentation.md
index f494620b..7617ebdd 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/ml/dataset_generation/datagen_crop_segmentation.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/ml/dataset_generation/datagen_crop_segmentation.md
@@ -1,5 +1,46 @@
# ml/dataset_generation/datagen_crop_segmentation
+Generates a dataset for crop segmentation, based on NDVI raster and Crop Data Layer (CDL) maps. The workflow generates SpaceEye cloud-free data for the input region and time range and computes NDVI over those. It also downloads CDL maps for the years comprised in the time range.
+
+```{mermaid}
+ graph TD
+ inp1>user_input]
+ out1>ndvi]
+ out2>cdl]
+ tsk1{{spaceeye}}
+ tsk2{{ndvi}}
+ tsk3{{cdl}}
+ tsk1{{spaceeye}} -- raster --> tsk2{{ndvi}}
+ inp1>user_input] -- user_input --> tsk1{{spaceeye}}
+ inp1>user_input] -- user_input --> tsk3{{cdl}}
+ tsk2{{ndvi}} -- index_raster --> out1>ndvi]
+ tsk3{{cdl}} -- raster --> out2>cdl]
+```
+
+## Sources
+
+- **user_input**: Time range and geometry of interest.
+
+## Sinks
+
+- **ndvi**: NDVI rasters.
+
+- **cdl**: CDL map for the years comprised in the input time range.
+
+## Parameters
+
+- **pc_key**: Optional Planetary Computer API key.
+
+## Tasks
+
+- **spaceeye**: Runs the SpaceEye cloud removal pipeline using an interpolation-based algorithm, yielding daily cloud-free images for the input geometry and time range.
+
+- **ndvi**: Computes an index from the bands of an input raster.
+
+- **cdl**: Downloads crop classes maps in the continental USA for the input time range.
+
+## Workflow Yaml
+
```yaml
name: datagen_crop_segmentation
@@ -42,19 +83,4 @@ description:
pc_key: Optional Planetary Computer API key.
-```
-
-```{mermaid}
- graph TD
- inp1>user_input]
- out1>ndvi]
- out2>cdl]
- tsk1{{spaceeye}}
- tsk2{{ndvi}}
- tsk3{{cdl}}
- tsk1{{spaceeye}} -- raster --> tsk2{{ndvi}}
- inp1>user_input] -- user_input --> tsk1{{spaceeye}}
- inp1>user_input] -- user_input --> tsk3{{cdl}}
- tsk2{{ndvi}} -- index_raster --> out1>ndvi]
- tsk3{{cdl}} -- raster --> out2>cdl]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/ml/driveway_detection.md b/docs/source/docfiles/markdown/workflow_yaml/ml/driveway_detection.md
index 4e90413e..d7ee4456 100644
--- a/docs/source/docfiles/markdown/workflow_yaml/ml/driveway_detection.md
+++ b/docs/source/docfiles/markdown/workflow_yaml/ml/driveway_detection.md
@@ -1,5 +1,60 @@
# ml/driveway_detection
+Detects driveways in front of houses. The workflow downloads road geometry from Open Street Maps and segments the front of houses in the input image using a machine learning model. It then uses the input image, segmentation map, road geometry, and input property boundaries to detect the presence of driveways in the front of each house.
+
+```{mermaid}
+ graph TD
+ inp1>input_raster]
+ inp2>property_boundaries]
+ out1>properties]
+ out2>driveways]
+ tsk1{{segment}}
+ tsk2{{osm}}
+ tsk3{{detect}}
+ tsk1{{segment}} -- segmentation_raster --> tsk3{{detect}}
+ tsk2{{osm}} -- roads --> tsk3{{detect}}
+ inp1>input_raster] -- input_raster --> tsk1{{segment}}
+ inp1>input_raster] -- input_raster --> tsk3{{detect}}
+ inp1>input_raster] -- user_input --> tsk2{{osm}}
+ inp2>property_boundaries] -- property_boundaries --> tsk3{{detect}}
+ tsk3{{detect}} -- properties_with_driveways --> out1>properties]
+ tsk3{{detect}} -- driveways --> out2>driveways]
+```
+
+## Sources
+
+- **input_raster**: Aerial imagery of the region of interest with RBG + NIR bands.
+
+- **property_boundaries**: Property boundary information for the region of interest.
+
+## Sinks
+
+- **properties**: Boundaries of properties that contain a driveway.
+
+- **driveways**: Regions of each property boundary where a driveway was detected.
+
+## Parameters
+
+- **min_region_area**: Minimum contiguous region that will be considered as a potential driveway, in meters.
+
+- **ndvi_thr**: Only areas under this NDVI threshold will be considered for driveways.
+
+- **car_size**: Expected size of a car, in pixels, defined as [height, width].
+
+- **num_kernels**: Number of rotated kernels to try to fit a car inside a potential driveway region.
+
+- **car_thr**: Ratio of pixels of a kernel that have to be inside a region in order to consider it a parkable spot.
+
+## Tasks
+
+- **segment**: Segments the front of houses in the input raster using a machine learning model.
+
+- **osm**: Downloads road geometry for input region from Open Street Maps.
+
+- **detect**: Detects driveways in the front of each house, using the input image, segmentation map, road geometry, and input property boundaries.
+
+## Workflow Yaml
+
```yaml
name: driveway_detection
@@ -65,23 +120,4 @@ description:
to consider it a parkable spot.
-```
-
-```{mermaid}
- graph TD
- inp1>input_raster]
- inp2>property_boundaries]
- out1>properties]
- out2>driveways]
- tsk1{{segment}}
- tsk2{{osm}}
- tsk3{{detect}}
- tsk1{{segment}} -- segmentation_raster --> tsk3{{detect}}
- tsk2{{osm}} -- roads --> tsk3{{detect}}
- inp1>input_raster] -- input_raster --> tsk1{{segment}}
- inp1>input_raster] -- input_raster --> tsk3{{detect}}
- inp1>input_raster] -- user_input --> tsk2{{osm}}
- inp2>property_boundaries] -- property_boundaries --> tsk3{{detect}}
- tsk3{{detect}} -- properties_with_driveways --> out1>properties]
- tsk3{{detect}} -- driveways --> out2>driveways]
```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/ml/segment_anything/basemap_prompt_segmentation.md b/docs/source/docfiles/markdown/workflow_yaml/ml/segment_anything/basemap_prompt_segmentation.md
new file mode 100644
index 00000000..67fa8bae
--- /dev/null
+++ b/docs/source/docfiles/markdown/workflow_yaml/ml/segment_anything/basemap_prompt_segmentation.md
@@ -0,0 +1,97 @@
+# ml/segment_anything/basemap_prompt_segmentation
+
+Runs Segment Anything Model (SAM) over BingMaps basemap rasters with points and/or bounding boxes as prompts. The workflow splits the input BingMaps basemap rasters into chips of 1024x1024 pixels with an overlap defined by `spatial_overlap`. Chips intersecting with prompts are processed by SAM's image encoder, followed by prompt encoder and mask decoder. Before running the workflow, make sure the model has been imported into the cluster by running `scripts/export_prompt_segmentation_models.py`. The script will download the desired model weights from SAM repository, export the image encoder and mask decoder to ONNX format, and add them to the cluster. For more information, refer to the [FarmVibes.AI troubleshooting](https://microsoft.github.io/farmvibes-ai/docfiles/markdown/TROUBLESHOOTING.html) page in the documentation.
+
+```{mermaid}
+ graph TD
+ inp1>input_raster]
+ inp2>input_geometry]
+ inp3>input_prompts]
+ out1>segmentation_mask]
+ tsk1{{ingest_points}}
+ tsk2{{sam_inference}}
+ tsk1{{ingest_points}} -- geometry/input_prompts --> tsk2{{sam_inference}}
+ inp1>input_raster] -- input_raster --> tsk2{{sam_inference}}
+ inp2>input_geometry] -- input_geometry --> tsk2{{sam_inference}}
+ inp3>input_prompts] -- user_input --> tsk1{{ingest_points}}
+ tsk2{{sam_inference}} -- segmentation_mask --> out1>segmentation_mask]
+```
+
+## Sources
+
+- **input_geometry**: Geometry of interest within the raster for the segmentation.
+
+- **input_raster**: BingMaps basemap rasters used as input for the segmentation.
+
+- **input_prompts**: ExternalReferences to the point and/or bounding box prompts. These are GeoJSON with coordinates, label (foreground/background) and prompt id (in case, the raster contains multiple entities that should be segmented in a single workflow run).
+
+## Sinks
+
+- **segmentation_mask**: Output segmentation masks.
+
+## Parameters
+
+- **model_type**: SAM's image encoder backbone architecture, among 'vit_h', 'vit_l', or 'vit_b'. Before running the workflow, make sure the desired model has been exported to the cluster by running `scripts/export_sam_models.py`. For more information, refer to the FarmVibes.AI troubleshooting page in the documentation.
+
+- **spatial_overlap**: Percentage of spatial overlap between chips in the range of [0.0, 1.0).
+
+## Tasks
+
+- **ingest_points**: Adds user geometries into the cluster storage, allowing for them to be used on workflows.
+
+- **sam_inference**: Runs SAM over the input BingMaps basemap raster with points and bounding boxes as prompts.
+
+## Workflow Yaml
+
+```yaml
+
+name: basemap_prompt_segmentation
+sources:
+ input_raster:
+ - sam_inference.input_raster
+ input_geometry:
+ - sam_inference.input_geometry
+ input_prompts:
+ - ingest_points.user_input
+sinks:
+ segmentation_mask: sam_inference.segmentation_mask
+parameters:
+ model_type: vit_b
+ spatial_overlap: 0.5
+tasks:
+ ingest_points:
+ workflow: data_ingestion/user_data/ingest_geometry
+ sam_inference:
+ op: basemap_prompt_segmentation
+ op_dir: segment_anything
+ parameters:
+ model_type: '@from(model_type)'
+ spatial_overlap: '@from(spatial_overlap)'
+edges:
+- origin: ingest_points.geometry
+ destination:
+ - sam_inference.input_prompts
+description:
+ short_description: Runs Segment Anything Model (SAM) over BingMaps basemap rasters
+ with points and/or bounding boxes as prompts.
+ long_description: The workflow splits the input BingMaps basemap rasters into chips
+ of 1024x1024 pixels with an overlap defined by `spatial_overlap`. Chips intersecting
+ with prompts are processed by SAM's image encoder, followed by prompt encoder
+ and mask decoder. Before running the workflow, make sure the model has been imported
+ into the cluster by running `scripts/export_prompt_segmentation_models.py`. The
+ script will download the desired model weights from SAM repository, export the
+ image encoder and mask decoder to ONNX format, and add them to the cluster. For
+ more information, refer to the [FarmVibes.AI troubleshooting](https://microsoft.github.io/farmvibes-ai/docfiles/markdown/TROUBLESHOOTING.html)
+ page in the documentation.
+ sources:
+ input_geometry: Geometry of interest within the raster for the segmentation.
+ input_raster: BingMaps basemap rasters used as input for the segmentation.
+ input_prompts: ExternalReferences to the point and/or bounding box prompts. These
+ are GeoJSON with coordinates, label (foreground/background) and prompt id (in
+ case, the raster contains multiple entities that should be segmented in a single
+ workflow run).
+ sinks:
+ segmentation_mask: Output segmentation masks.
+
+
+```
\ No newline at end of file
diff --git a/docs/source/docfiles/markdown/workflow_yaml/ml/segment_anything/s2_prompt_segmentation.md b/docs/source/docfiles/markdown/workflow_yaml/ml/segment_anything/s2_prompt_segmentation.md
new file mode 100644
index 00000000..2428583f
--- /dev/null
+++ b/docs/source/docfiles/markdown/workflow_yaml/ml/segment_anything/s2_prompt_segmentation.md
@@ -0,0 +1,97 @@
+# ml/segment_anything/s2_prompt_segmentation
+
+Runs Segment Anything Model (SAM) over Sentinel-2 rasters with points and/or bounding boxes as prompts. The workflow splits the input Sentinel-2 rasters into chips of 1024x1024 pixels with an overlap defined by `spatial_overlap`. Chips intersecting with prompts are processed by SAM's image encoder, followed by prompt encoder and mask decoder. Before running the workflow, make sure the model has been imported into the cluster by running `scripts/export_prompt_segmentation_models.py`. The script will download the desired model weights from SAM repository, export the image encoder and mask decoder to ONNX format, and add them to the cluster. For more information, refer to the [FarmVibes.AI troubleshooting](https://microsoft.github.io/farmvibes-ai/docfiles/markdown/TROUBLESHOOTING.html) page in the documentation.
+
+```{mermaid}
+ graph TD
+ inp1>input_raster]
+ inp2>input_geometry]
+ inp3>input_prompts]
+ out1>segmentation_mask]
+ tsk1{{ingest_points}}
+ tsk2{{sam_inference}}
+ tsk1{{ingest_points}} -- geometry/input_prompts --> tsk2{{sam_inference}}
+ inp1>input_raster] -- input_raster --> tsk2{{sam_inference}}
+ inp2>input_geometry] -- input_geometry --> tsk2{{sam_inference}}
+ inp3>input_prompts] -- user_input --> tsk1{{ingest_points}}
+ tsk2{{sam_inference}} -- segmentation_mask --> out1>segmentation_mask]
+```
+
+## Sources
+
+- **input_geometry**: Geometry of interest within the raster for the segmentation.
+
+- **input_raster**: Sentinel-2 rasters used as input for the segmentation.
+
+- **input_prompts**: ExternalReferences to the point and/or bounding box prompts. These are GeoJSON with coordinates, label (foreground/background) and prompt id (in case, the raster contains multiple entities that should be segmented in a single workflow run).
+
+## Sinks
+
+- **segmentation_mask**: Output segmentation masks.
+
+## Parameters
+
+- **model_type**: SAM's image encoder backbone architecture, among 'vit_h', 'vit_l', or 'vit_b'. Before running the workflow, make sure the desired model has been exported to the cluster by running `scripts/export_sam_models.py`. For more information, refer to the FarmVibes.AI troubleshooting page in the documentation.
+
+- **spatial_overlap**: Percentage of spatial overlap between chips in the range of [0.0, 1.0).
+
+## Tasks
+
+- **ingest_points**: Adds user geometries into the cluster storage, allowing for them to be used on workflows.
+
+- **sam_inference**: Runs SAM over the input Sentinel-2 raster with points and bounding boxes as prompts.
+
+## Workflow Yaml
+
+```yaml
+
+name: s2_prompt_segmentation
+sources:
+ input_raster:
+ - sam_inference.input_raster
+ input_geometry:
+ - sam_inference.input_geometry
+ input_prompts:
+ - ingest_points.user_input
+sinks:
+ segmentation_mask: sam_inference.segmentation_mask
+parameters:
+ model_type: vit_b
+ spatial_overlap: 0.5
+tasks:
+ ingest_points:
+ workflow: data_ingestion/user_data/ingest_geometry
+ sam_inference:
+ op: s2_prompt_segmentation
+ op_dir: segment_anything
+ parameters:
+ model_type: '@from(model_type)'
+ spatial_overlap: '@from(spatial_overlap)'
+edges:
+- origin: ingest_points.geometry
+ destination:
+ - sam_inference.input_prompts
+description:
+ short_description: Runs Segment Anything Model (SAM) over Sentinel-2 rasters with
+ points and/or bounding boxes as prompts.
+ long_description: The workflow splits the input Sentinel-2 rasters into chips of
+ 1024x1024 pixels with an overlap defined by `spatial_overlap`. Chips intersecting
+ with prompts are processed by SAM's image encoder, followed by prompt encoder
+ and mask decoder. Before running the workflow, make sure the model has been imported
+ into the cluster by running `scripts/export_prompt_segmentation_models.py`. The
+ script will download the desired model weights from SAM repository, export the
+ image encoder and mask decoder to ONNX format, and add them to the cluster. For
+ more information, refer to the [FarmVibes.AI troubleshooting](https://microsoft.github.io/farmvibes-ai/docfiles/markdown/TROUBLESHOOTING.html)
+ page in the documentation.
+ sources:
+ input_geometry: Geometry of interest within the raster for the segmentation.
+ input_raster: Sentinel-2 rasters used as input for the segmentation.
+ input_prompts: ExternalReferences to the point and/or bounding box prompts. These
+ are GeoJSON with coordinates, label (foreground/background) and prompt id (in
+ case, the raster contains multiple entities that should be segmented in a single
+ workflow run).
+ sinks:
+ segmentation_mask: Output segmentation masks.
+
+
+```
\ No newline at end of file
diff --git a/docs/source/index.md b/docs/source/index.md
index 465cc703..e22ffdb8 100644
--- a/docs/source/index.md
+++ b/docs/source/index.md
@@ -35,6 +35,7 @@ Additionally, the following user guides and links may be helpful:
docfiles/markdown/CLIENT
docfiles/markdown/WORKFLOWS
docfiles/markdown/NOTEBOOK_LIST
+ docfiles/markdown/REST_API
docfiles/markdown/CACHE
docfiles/markdown/SECRETS
docfiles/markdown/TROUBLESHOOTING
diff --git a/notebooks/admag/azure_data_manager_for_agriculture_and_comet_farm_api_example.ipynb b/notebooks/admag/azure_data_manager_for_agriculture_and_comet_farm_api_example.ipynb
index 30296b19..e20c3428 100644
--- a/notebooks/admag/azure_data_manager_for_agriculture_and_comet_farm_api_example.ipynb
+++ b/notebooks/admag/azure_data_manager_for_agriculture_and_comet_farm_api_example.ipynb
@@ -16,7 +16,7 @@
"\n",
"This notebook shows how to use [Microsoft Azure Data Manager for Agriculture](https://aka.ms/farmvibesDMA) (ADMAg) and the [COMET-Farm API](https://gitlab.com/comet-api/api-docs/-/tree/master/) to derive carbon sequestration information for agricultural fields. The idea is to obtain farming data from Microsoft Azure Data Manager for Agriculture and input this data directly into the COMET-Farm API. In this notebook, we use a single workflow to calculate soil carbon sequestration using ADMAg ids. The steps executed by the `farm_ai/carbon_local/admag_carbon_integration` are the following:\n",
"\n",
- "1. FarmVibes.AI needs the farmer_id, boundary_id, and a seasonal_field_id (`ADMAgSeasonalFieldInput`), to retrieve farming data from Azure Data Manager for Agriculture. \n",
+ "1. FarmVibes.AI needs the party_id, and a seasonal_field_id (`ADMAgSeasonalFieldInput`), to retrieve farming data from Azure Data Manager for Agriculture. \n",
"\n",
"2. The information is sent back to FarmVibes.AI (Tillage, Fertilization, Organic Amendments, Planting, Harvest, …).\n",
"\n",
@@ -53,17 +53,14 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 1,
"id": "3685ff85",
"metadata": {},
"outputs": [],
"source": [
- "import os\n",
"from typing import List\n",
- "from datetime import datetime, timezone\n",
"\n",
- "from vibe_core.datamodel import RunStatus\n",
- "from vibe_core.client import FarmvibesAiClient, get_default_vibe_client, get_local_service_url\n",
+ "from vibe_core.client import FarmvibesAiClient, get_default_vibe_client\n",
"from vibe_core.data import ADMAgSeasonalFieldInput"
]
},
@@ -80,7 +77,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 2,
"id": "1698286c",
"metadata": {},
"outputs": [],
@@ -90,15 +87,13 @@
"# ADMAg client id\n",
"CLIENT_ID = \"\"\n",
"# ADMAg client secret\n",
- "CLIENT_SECRET = \"@SECRET(eywa-secrets, data-manager-ag-secret)\"\n",
+ "CLIENT_SECRET = \"\"\n",
"# ADMAg authority\n",
"AUTHORITY = \"\"\n",
"# ADMAg default scope\n",
"DEFAULT_SCOPE = \"\"\n",
- "# Farmer ADMAg ID\n",
- "FARMER_ID = \"\"\n",
- "# Boundary ADMAg ID\n",
- "BOUNDARY_ID = \"\"\n",
+ "# Party ADMAg ID\n",
+ "PARTY_ID = \"\"\n",
"# A list of seasonal field scenarios ids from ADMAg\n",
"SCENARIO_IDS = []\n",
"# A list of baseline seasonal field ids from ADMAg\n",
@@ -113,27 +108,25 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 3,
"id": "be3373e2",
"metadata": {},
"outputs": [],
"source": [
"def get_seasonal_field_inputs(\n",
- " farmer_id: str,\n",
- " boundary_id: str,\n",
+ " party_id: str,\n",
" seasonal_field_ids: List[str]\n",
") -> List[ADMAgSeasonalFieldInput]:\n",
" return [\n",
" ADMAgSeasonalFieldInput(\n",
- " farmer_id=FARMER_ID,\n",
- " boundary_id=BOUNDARY_ID,\n",
+ " party_id=party_id,\n",
" seasonal_field_id=seasonal_field_id\n",
" )\n",
" for seasonal_field_id in seasonal_field_ids\n",
" ]\n",
"\n",
- "baseline_admag_inputs = get_seasonal_field_inputs(FARMER_ID, BOUNDARY_ID, BASELINE_IDS)\n",
- "scenario_admag_inputs = get_seasonal_field_inputs(FARMER_ID, BOUNDARY_ID, SCENARIO_IDS)"
+ "baseline_admag_inputs = get_seasonal_field_inputs(PARTY_ID, BASELINE_IDS)\n",
+ "scenario_admag_inputs = get_seasonal_field_inputs(PARTY_ID, SCENARIO_IDS)"
]
},
{
@@ -147,7 +140,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 4,
"id": "0b299fb0",
"metadata": {},
"outputs": [],
@@ -167,7 +160,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 5,
"id": "69526660",
"metadata": {},
"outputs": [],
@@ -177,27 +170,381 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 6,
"id": "a00f3b59",
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n"
+ ],
+ "text/plain": []
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
Computes the offset amount of carbon that would be sequestered in a seasonal field using \n",
+ " Microsoft Azure Data Manager for Agriculture (ADMAg) data. Derives carbon sequestration \n",
+ " information. Microsoft Azure Data Manager for Agriculture (ADMAg) and the COMET-Farm API are \n",
+ " used to obtain farming data and evaluate carbon offset. ADMAg is capable of describing \n",
+ " important farming activities such as fertilization, tillage, and organic amendments \n",
+ " applications, all of which are represented in the data manager. FarmVibes.AI retrieves this \n",
+ " information from the data manager and builds SeasonalFieldInformation FarmVibes.AI objects. \n",
+ " These objects are then used to call the COMET-Farm API and evaluate Carbon Offset Information. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " Computes the offset amount of carbon that would be sequestered in a seasonal field using \n",
+ " Microsoft Azure Data Manager for Agriculture (ADMAg) data. Derives carbon sequestration \n",
+ " information. Microsoft Azure Data Manager for Agriculture (ADMAg) and the COMET-Farm API are \n",
+ " used to obtain farming data and evaluate carbon offset. ADMAg is capable of describing \n",
+ " important farming activities such as fertilization, tillage, and organic amendments \n",
+ " applications, all of which are represented in the data manager. FarmVibes.AI retrieves this \n",
+ " information from the data manager and builds SeasonalFieldInformation FarmVibes.AI objects. \n",
+ " These objects are then used to call the COMET-Farm API and evaluate Carbon Offset Information. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- carbon_output (vibe_core.data.core_types.CarbonOffsetInfo): Carbon sequestration received for \n",
+ " scenario information provided as input. \n",
+ "
- base_url (default: None): Azure Data Manager for Agriculture host. Please visit \n",
+ " https://aka.ms/farmvibesDMA to check how to get these credentials. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1mbase_url\u001b[0m (\u001b[34mdefault: None\u001b[0m): Azure Data Manager for Agriculture host. Please visit \n",
+ " https://aka.ms/farmvibesDMA to check how to get these credentials. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- client_id (default: None): Azure Data Manager for Agriculture client id. Please visit \n",
+ " https://aka.ms/farmvibesDMA to check how to get these credentials. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1mclient_id\u001b[0m (\u001b[34mdefault: None\u001b[0m): Azure Data Manager for Agriculture client id. Please visit \n",
+ " https://aka.ms/farmvibesDMA to check how to get these credentials. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- client_secret (default: None): Azure Data Manager for Agriculture client secret. Please visit \n",
+ " https://aka.ms/farmvibesDMA to check how to get these credentials. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1mclient_secret\u001b[0m (\u001b[34mdefault: None\u001b[0m): Azure Data Manager for Agriculture client secret. Please visit \n",
+ " https://aka.ms/farmvibesDMA to check how to get these credentials. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- authority (default: None): Azure Data Manager for Agriculture authority. Please visit \n",
+ " https://aka.ms/farmvibesDMA to check how to get these credentials. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1mauthority\u001b[0m (\u001b[34mdefault: None\u001b[0m): Azure Data Manager for Agriculture authority. Please visit \n",
+ " https://aka.ms/farmvibesDMA to check how to get these credentials. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- default_scope (default: None): Azure Data Manager for Agriculture default scope. Please visit \n",
+ " https://aka.ms/farmvibesDMA to check how to get these credentials. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1mdefault_scope\u001b[0m (\u001b[34mdefault: None\u001b[0m): Azure Data Manager for Agriculture default scope. Please visit \n",
+ " https://aka.ms/farmvibesDMA to check how to get these credentials. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- comet_support_email (default: None): Comet support email. The email used to register for a \n",
+ " COMET account. The requests are forwarded to comet with this email reference. This email is \n",
+ " used by comet to share the information back to you for failed requests. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1mcomet_support_email\u001b[0m (\u001b[34mdefault: None\u001b[0m): Comet support email. The email used to register for a \n",
+ " COMET account. The requests are forwarded to comet with this email reference. This email is \n",
+ " used by comet to share the information back to you for failed requests. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- ngrok_token (default: None): NGROK session token. A token that FarmVibes uses to create a \n",
+ " web_hook url that is shared with Comet in a request when running the workflow. Comet can use \n",
+ " this link to send back a response to FarmVibes. NGROK is a service that creates temporary urls \n",
+ " for local servers. To use NGROK, FarmVibes needs to get a token from this website, \n",
+ " https://dashboard.ngrok.com/. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1mngrok_token\u001b[0m (\u001b[34mdefault: None\u001b[0m): NGROK session token. A token that FarmVibes uses to create a \n",
+ " web_hook url that is shared with Comet in a request when running the workflow. Comet can use \n",
+ " this link to send back a response to FarmVibes. NGROK is a service that creates temporary urls \n",
+ " for local servers. To use NGROK, FarmVibes needs to get a token from this website, \n",
+ " https://dashboard.ngrok.com/. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- admag_carbon: Computes the offset amount of carbon that would be sequestered in a seasonal \n",
+ " field using the baseline (historical) and scenario (time range interested in) information. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1madmag_carbon\u001b[0m: Computes the offset amount of carbon that would be sequestered in a seasonal \n",
+ " field using the baseline (historical) and scenario (time range interested in) information. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
"source": [
"client.document_workflow(CARBON_WORKFLOW)"
]
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 7,
"id": "e8bdefac",
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n"
+ ],
+ "text/plain": []
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n"
+ ],
+ "text/plain": []
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
"source": [
"run = client.run(\n",
" CARBON_WORKFLOW,\n",
" \"Carbon what-if scenario\",\n",
" input_data={\n",
- " \"baseline_admag_input\": baseline_admag_inputs,\n",
- " \"scenario_admag_input\": scenario_admag_inputs,\n",
+ " \"baseline_admag_input\": baseline_admag_inputs, \n",
+ " \"scenario_admag_input\": scenario_admag_inputs, # type: ignore\n",
" },\n",
" parameters={\n",
" \"base_url\": BASE_URL,\n",
@@ -224,12 +571,23 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 8,
"id": "16345f43",
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'-0.074 Mg Co2e/year'"
+ ]
+ },
+ "execution_count": 8,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
"source": [
- "run.output['carbon_output'][0].carbon"
+ "run.output['carbon_output'][0].carbon # type: ignore"
]
}
],
diff --git a/notebooks/admag/azure_data_manager_for_agriculture_example.ipynb b/notebooks/admag/azure_data_manager_for_agriculture_example.ipynb
index d8be84ca..be50e40f 100644
--- a/notebooks/admag/azure_data_manager_for_agriculture_example.ipynb
+++ b/notebooks/admag/azure_data_manager_for_agriculture_example.ipynb
@@ -8,7 +8,7 @@
"source": [
"# Microsoft Azure Data Manager for Agriculture and NDVI summary workflows into a single custom workflow\n",
"\n",
- "In this notebook, we will explain how to connect FarmVibes.AI with [Microsoft Azure Data Manager for Agriculture](https://aka.ms/farmvibesDMA), and provide an example of how to leverage the FarmVibes.AI workflows using ADMAg inputs. We will demonstrate how to compose the ADMAg and NDVI summary workflows into a single custom workflow, and check the results for the user's agriculture field."
+ "In this notebook, we will explain how to connect FarmVibes.AI with [Microsoft Azure Data Manager for Agriculture](https://aka.ms/farmvibesDMA), and provide an example of how to leverage the FarmVibes.AI workflows using [ADMAg for Agri](https://learn.microsoft.com/en-us/rest/api/data-manager-for-agri/). We will demonstrate how to compose the ADMAg and NDVI summary workflows into a single custom workflow, and check the results for the user's agriculture field. The Notebook use ADMAg version 2023-11-01-preview for demonstration."
]
},
{
@@ -37,7 +37,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 1,
"id": "b2e34591",
"metadata": {},
"outputs": [],
@@ -59,7 +59,7 @@
"source": [
"## Define Azure Data Manager for Agriculture entities\n",
"\n",
- "We will start by providing the parameters that specify the Azure Data Manager for Agriculture connection (e.g., seasonal field, boundary, and farmer identifiers). Please, check Microsoft Azure Data Manager for Agriculture [documentation](https://aka.ms/farmvibesDMA) to check how to obtain these fields.\n",
+ "We will start by providing the parameters that specify the Azure Data Manager for Agriculture connection (e.g., seasonal field, and farmer identifiers). Please, check Microsoft Azure Data Manager for Agriculture [documentation](https://learn.microsoft.com/en-us/rest/api/data-manager-for-agri/) to check how to obtain these fields.\n",
"\n",
"In the next cell, we retrieve the `CLIENT_SECRET` variable from the `data-manager-ag-secret` registered on the FarmVibes.AI cluster. To create a new key on the cluster you may want to use the following command on project's root folder:\n",
"\n",
@@ -75,22 +75,21 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 2,
"id": "4afec3ab",
"metadata": {},
"outputs": [],
"source": [
"WORKFLOW_NAME = \"data_ingestion/admag/admag_seasonal_field\"\n",
"\n",
- "BASE_URL = \"\"\n",
- "CLIENT_ID = \"\"\n",
- "CLIENT_SECRET = \"@SECRET(eywa-secrets, data-manager-ag-secret)\"\n",
- "AUTHORITY = \"\"\n",
- "DEFAULT_SCOPE = \"\"\n",
+ "BASE_URL = \"\"\n",
+ "CLIENT_ID = \"\"\n",
+ "CLIENT_SECRET = \"\"\n",
+ "AUTHORITY = \"\"\n",
+ "DEFAULT_SCOPE = \"\"\n",
"\n",
- "FARMER_ID = \"\"\n",
- "SEASONAL_FIELD_ID=\"\"\n",
- "BOUNDARY_ID=\"\""
+ "PARTY_ID = \"\"\n",
+ "SEASONAL_FIELD_ID=\"\""
]
},
{
@@ -101,20 +100,19 @@
"source": [
"## Create Seasonal Field input\n",
"\n",
- "Azure Data Manager for Agriculture uses `farmer_id`, `seasonal_field_id`, and `boundary_id` to identify a crop during a given season. This triple will be used to create a DataVibe subclass `SeasonalFieldInformation` that contains farm-related operations (e.g., fertilization, harvest, tillage, planting, crop name) that is used as input to the workflow (`data_ingestion/admag/admag_seasonal_field`). "
+ "Azure Data Manager for Agriculture uses `party_id` and `seasonal_field_id` to identify a crop during a given season. This triple will be used to create a DataVibe subclass `SeasonalFieldInformation` that contains farm-related operations (e.g., fertilization, harvest, tillage, planting, crop name) that is used as input to the workflow (`data_ingestion/admag/admag_seasonal_field`). "
]
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 3,
"id": "f63c1b1b",
"metadata": {},
"outputs": [],
"source": [
"input_data = ADMAgSeasonalFieldInput(\n",
- " farmer_id=FARMER_ID,\n",
+ " party_id=PARTY_ID,\n",
" seasonal_field_id=SEASONAL_FIELD_ID,\n",
- " boundary_id=BOUNDARY_ID,\n",
")"
]
},
@@ -131,7 +129,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 4,
"id": "ea8f8112",
"metadata": {},
"outputs": [],
@@ -141,20 +139,279 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 5,
"id": "6ad9225c",
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n"
+ ],
+ "text/plain": []
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- base_url (default: None): Azure Data Manager for Agriculture host. Please visit \n",
+ " https://aka.ms/farmvibesDMA to check how to get these credentials. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1mbase_url\u001b[0m (\u001b[34mdefault: None\u001b[0m): Azure Data Manager for Agriculture host. Please visit \n",
+ " https://aka.ms/farmvibesDMA to check how to get these credentials. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- client_id (default: None): Azure Data Manager for Agriculture client id. Please visit \n",
+ " https://aka.ms/farmvibesDMA to check how to get these credentials. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1mclient_id\u001b[0m (\u001b[34mdefault: None\u001b[0m): Azure Data Manager for Agriculture client id. Please visit \n",
+ " https://aka.ms/farmvibesDMA to check how to get these credentials. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- client_secret (default: None): Azure Data Manager for Agriculture client secret. Please visit \n",
+ " https://aka.ms/farmvibesDMA to check how to get these credentials. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1mclient_secret\u001b[0m (\u001b[34mdefault: None\u001b[0m): Azure Data Manager for Agriculture client secret. Please visit \n",
+ " https://aka.ms/farmvibesDMA to check how to get these credentials. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- authority (default: None): Azure Data Manager for Agriculture authority. Please visit \n",
+ " https://aka.ms/farmvibesDMA to check how to get these credentials. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1mauthority\u001b[0m (\u001b[34mdefault: None\u001b[0m): Azure Data Manager for Agriculture authority. Please visit \n",
+ " https://aka.ms/farmvibesDMA to check how to get these credentials. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- default_scope (default: None): Azure Data Manager for Agriculture default scope. Please visit \n",
+ " https://aka.ms/farmvibesDMA to check how to get these credentials. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1mdefault_scope\u001b[0m (\u001b[34mdefault: None\u001b[0m): Azure Data Manager for Agriculture default scope. Please visit \n",
+ " https://aka.ms/farmvibesDMA to check how to get these credentials. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
Calculates NDVI statistics (mean, standard deviation, maximum and minimum) for the input \n",
+ " geometry and time range. The workflow retrieves the relevant Sentinel-2 products with Planetary \n",
+ " Computer (PC) API, forwards them to a cloud detection model and combines the predicted cloud \n",
+ " mask to the mask obtained from the product. The workflow computes the NDVI for each available \n",
+ " tile and date, summarizing each with the mean, standard deviation, maximum and minimum values \n",
+ " for the regions not obscured by clouds. Finally, it outputs a timeseries with such statistics \n",
+ " for all available dates, ignoring heavily-clouded tiles. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " Calculates NDVI statistics (mean, standard deviation, maximum and minimum) for the input \n",
+ " geometry and time range. The workflow retrieves the relevant Sentinel-2 products with Planetary \n",
+ " Computer (PC) API, forwards them to a cloud detection model and combines the predicted cloud \n",
+ " mask to the mask obtained from the product. The workflow computes the NDVI for each available \n",
+ " tile and date, summarizing each with the mean, standard deviation, maximum and minimum values \n",
+ " for the regions not obscured by clouds. Finally, it outputs a timeseries with such statistics \n",
+ " for all available dates, ignoring heavily-clouded tiles. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- timeseries (List[vibe_core.data.core_types.TimeSeries]): Aggregated NDVI statistics of the \n",
+ " retrieved tiles within the input geometry and time range. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1mtimeseries\u001b[0m (\u001b[34mList[vibe_core.data.core_types.TimeSeries]\u001b[0m): Aggregated NDVI statistics of the \n",
+ " retrieved tiles within the input geometry and time range. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- s2: Downloads and preprocesses Sentinel-2 imagery that covers the input geometry and time \n",
+ " range, and computes improved cloud masks using cloud and shadow segmentation models. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1ms2\u001b[0m: Downloads and preprocesses Sentinel-2 imagery that covers the input geometry and time \n",
+ " range, and computes improved cloud masks using cloud and shadow segmentation models. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- compute_ndvi: Computes an index from the bands of an input raster. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1mcompute_ndvi\u001b[0m: Computes an index from the bands of an input raster. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- summary_timeseries: Computes the mean, standard deviation, maximum, and minimum values of all \n",
+ " regions of the raster considered by the mask and aggregates them into a timeseries. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1msummary_timeseries\u001b[0m: Computes the mean, standard deviation, maximum, and minimum values of all \n",
+ " regions of the raster considered by the mask and aggregates them into a timeseries. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
"source": [
"client.document_workflow(\"farm_ai/agriculture/ndvi_summary\")"
]
@@ -255,7 +750,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 10,
"id": "ede223f5",
"metadata": {},
"outputs": [],
@@ -296,7 +791,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 11,
"id": "250cb567",
"metadata": {},
"outputs": [],
@@ -328,7 +823,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 12,
"id": "2b797c26",
"metadata": {},
"outputs": [],
@@ -348,7 +843,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 13,
"id": "98d061af",
"metadata": {},
"outputs": [],
@@ -371,7 +866,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 14,
"id": "9ab5e280",
"metadata": {},
"outputs": [],
diff --git a/notebooks/crop_cycles/env.yaml b/notebooks/crop_cycles/env.yaml
index bf4e4221..9b1606d5 100644
--- a/notebooks/crop_cycles/env.yaml
+++ b/notebooks/crop_cycles/env.yaml
@@ -10,6 +10,7 @@ dependencies:
- tf2onnx=1.9.3
- rioxarray=0.3.1
- ipykernel=6.15.2
+ - ipywidgets~=8.0.2
- yaml=0.2.5
- matplotlib=3.5.3
- pip~=21.2.4
diff --git a/notebooks/forest/download_alos_forest_map.ipynb b/notebooks/forest/download_alos_forest_map.ipynb
new file mode 100644
index 00000000..b5834c38
--- /dev/null
+++ b/notebooks/forest/download_alos_forest_map.ipynb
@@ -0,0 +1,497 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Download ALOS Forest Extent Dataset\n",
+ "In this notebook, we download an [ALOS Forest Extent](https://planetarycomputer.microsoft.com/dataset/alos-fnf-mosaic) map using FarmVibes.AI, and visualize it.\n",
+ "\n",
+ "The ALOS PALSAR/PALSAR-2 Annual Mosaic is a dataset that provides annual observations of forest extent produced by JAXA's ALOS and ALOS-2 satellites. The dataset spans from 2015 to 2020 and covers the whole globe. Each dataset (provided as Rasters) contains the following categories:\n",
+ "```txt\n",
+ "0 - No data\n",
+ "1 - Forest (>90% canopy cover)\n",
+ "2 - Forest (10-90% canopy cover)\n",
+ "3 - Non-forest\n",
+ "4 - Water\n",
+ "```\n",
+ "\n",
+ "The download process involves the following steps:\n",
+ "1. Listing of the products that intersect with the user-provided geometry and time range.\n",
+ "2. Downloading each raster listed in the previous step.\n",
+ "\n",
+ "The output is provided as the categorical rasters listed in step 1.\n",
+ "\n",
+ "NOTE: To install the required packages used in this notebook, see [this README file](../README.md)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from vibe_core.client import get_default_vibe_client"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Create Vibe client and document the ALOS download workflow\n",
+ "\n",
+ "Before executing the [workflow](https://microsoft.github.io/farmvibes-ai/docfiles/markdown/WORKFLOWS.html), let's observe its documentation using a FarmVibes.AI python client."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n"
+ ],
+ "text/plain": []
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
Downloads Advanced Land Observing Satellite (ALOS) forest/non-forest classification map and \n",
+ " merges it into a single raster. The workflow lists the ALOS forest/non-forest classification \n",
+ " products that intersect with the input geometry and time range (available range 2015-2020), and \n",
+ " downloads the filtered products. The workflow processes the downloaded products and merge them \n",
+ " into a single raster. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " Downloads Advanced Land Observing Satellite (ALOS) forest/non-forest classification map and \n",
+ " merges it into a single raster. The workflow lists the ALOS forest/non-forest classification \n",
+ " products that intersect with the input geometry and time range (available range 2015-2020), and \n",
+ " downloads the filtered products. The workflow processes the downloaded products and merge them \n",
+ " into a single raster. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- user_input (vibe_core.data.core_types.DataVibe): Geometry of interest for which to download \n",
+ " the ALOS forest/non-forest classification map. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1muser_input\u001b[0m (\u001b[34mvibe_core.data.core_types.DataVibe\u001b[0m): Geometry of interest for which to download \n",
+ " the ALOS forest/non-forest classification map. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
Downloads Global Land Analysis (GLAD) forest extent data and merges them into a single raster. \n",
+ " The workflow lists the GLAD forest products that intersect with the input geometry and time \n",
+ " range, and downloads the filtered products. The downloaded products are merged into a single \n",
+ " raster and classified. The result tiles have pixel values categorized into two classes - 0 \n",
+ " (non-forest) and 1 (forest). This workflow uses the same forest definition as the Food and \n",
+ " Agriculture Organization of the United Nations (FAO). \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " Downloads Global Land Analysis (GLAD) forest extent data and merges them into a single raster. \n",
+ " The workflow lists the GLAD forest products that intersect with the input geometry and time \n",
+ " range, and downloads the filtered products. The downloaded products are merged into a single \n",
+ " raster and classified. The result tiles have pixel values categorized into two classes - 0 \n",
+ " (non-forest) and 1 (forest). This workflow uses the same forest definition as the Food and \n",
+ " Agriculture Organization of the United Nations (FAO). \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- group_rasters_by_time: This op groups rasters in time according to 'criterion'. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1mgroup_rasters_by_time\u001b[0m: This op groups rasters in time according to 'criterion'. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- merge: Merges rasters in a sequence to a single raster. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1mmerge\u001b[0m: Merges rasters in a sequence to a single raster. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "client = get_default_vibe_client()\n",
+ "\n",
+ "WORKFLOW_NAME = \"data_ingestion/glad/glad_forest_extent_download_merge\"\n",
+ "client.document_workflow(WORKFLOW_NAME)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "696f2717",
+ "metadata": {},
+ "source": [
+ "### Create sample geometry and time range\n",
+ "\n",
+ "Like most FarmVibes.AI workflows, the user input involves a geometry and a time-range. The following cell creates a `shapely` geometry and a time range object to be provided as the workflow input."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "id": "868a2720",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from shapely import geometry as shpg\n",
+ "from datetime import datetime\n",
+ "\n",
+ "# GeoJSON data\n",
+ "geo_json = {\n",
+ " \"type\": \"Feature\",\n",
+ " \"geometry\": {\n",
+ " \"type\": \"Polygon\",\n",
+ " \"coordinates\": [\n",
+ " [\n",
+ " [-86.773827, 14.575498],\n",
+ " [-86.770459, 14.579301],\n",
+ " [-86.764283, 14.575102],\n",
+ " [-86.769591, 14.567595],\n",
+ " [-86.773827, 14.575497],\n",
+ " ]\n",
+ " ],\n",
+ " },\n",
+ " \"properties\": {},\n",
+ "}\n",
+ "\n",
+ "geom = shpg.shape(geo_json[\"geometry\"])\n",
+ "time_range = datetime(2020, 1, 1), datetime(2020, 1, 2)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c2604106",
+ "metadata": {},
+ "source": [
+ "### Execute FarmVibes.AI to download the GLAD Forest extent tiles"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "bc8ffe3a",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n"
+ ],
+ "text/plain": []
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "9e1e6f1ff0bd4d87828384a5e88db045",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "Output()"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n"
+ ],
+ "text/plain": []
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "run = client.run(\n",
+ " WORKFLOW_NAME,\n",
+ " \"Download GLAD Forest Map\",\n",
+ " geometry=geom,\n",
+ " time_range=time_range,\n",
+ ")\n",
+ "run.monitor()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "57a4c441",
+ "metadata": {},
+ "source": [
+ "### Visualize the Resulting data cropped to the input geometry with some Buffer\n",
+ "\n",
+ "In the next cell, we use the user-provided geometry to read the output raster and create some buffer around it. Next, we plot the raster image and the user geometry in red."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "id": "4ccd5569",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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06YNEhISkJCQgMzMTAQHB2vcdFuex/I816R9+/bo3bu30gsAHjx4AADw9/ev9J4WLVogPT0deXl5Sstf7rV77949MMbQtGlTpQsMDw8P3Lp1C2lpaYr3zZo1Czt27IC7uztCQkKwadMmxf1OXRg+fDi+//57JCQk4MKFCyqbbAHgxo0bGDRoEJycnCCRSODh4YGRI0cCQKU8eXt7V+oY1axZMwCosmmbWDbqbUssipOTE+rWrYv4+PhK68rvgdbmRFkeIDt37lzl+vv376NRo0ZqpVWexyZNmmidH21VrLEBgFwuVzyzKhQKK21fsSa/evVqjBkzBj///DN+//13REREYMWKFYiOjkb9+vVrnbewsDAsWLAAEyZMgJubG958880qt8vKykL37t0hkUiwdOlSNG7cGDY2Nrh69SrmzZsHuVxe67wQy0XBk1icfv36YceOHfj777/Rvn17naWblJSECxcuYNq0aejevbvSOrlcjvDwcBw4cKDSc4qq7Nu3DwAQEhJSq3yVP6t6586dSutu374Nd3f3Gh9Fady4MRhjaNiwoaI2Vp1WrVqhVatWWLhwoaJj0tatWxWDHHAcp0VJyjRo0ACdO3fGmTNnMGXKFIhEVZ/Gzpw5g4yMDPz000/o1q2bYnlSUlKV2z958qTSYzl3794FAPj5+WmdX2KeqNmWWJy5c+fCzs4O48aNw7NnzyqtZ//25NRUea1z7ty5eOedd5ReQ4cORffu3dVuuj1w4AB27NiBjh07olevXlrlp1zdunURFBSEPXv2KHq8AmU1299//x19+/atMY3BgwdDKBRiyZIllY4PYwwZGRkAyu4py2QypfWtWrWCQCBQGjHJ3t5eKS+a+uyzz7B48WJMnz5d5TblNeSK+S0uLsbmzZur3F4mk2Hbtm1K227btg0eHh5o166d1nkl5olqnsTiNG3aFAcOHEBYWBj8/f0VIwwxxpCUlIQDBw5AIBBU2cS4Zs2aSp1TBAIB/u///g/79+9HUFCQyudDBwwYgOnTp+Pq1ato27atYvkPP/wABwcHFBcXK0YYOn/+PAIDA/H999/rpMxffvklQkND0bFjR4wfPx4FBQXYsGEDnJycEBkZWeP7GzdujM8++wwLFixAcnIyBg4cCEdHRyQlJeHw4cOYOHEiZs+ejdOnT2PatGl499130axZM8hkMuzbtw9C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",
+ "text/plain": [
+ "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "from shapely.geometry import box\n",
+ "import matplotlib.pyplot as plt\n",
+ "from vibe_core.data import CategoricalRaster, Raster\n",
+ "from typing import cast\n",
+ "\n",
+ "import sys\n",
+ "sys.path.append(\"../\")\n",
+ "from shared_nb_lib.raster import read_raster\n",
+ "\n",
+ "\n",
+ "# Define your geometry\n",
+ "bounding_box = box(* geom.buffer(0.01).bounds)\n",
+ "\n",
+ "# Get the bounds of the geometry\n",
+ "minx, miny, maxx, maxy = bounding_box.bounds\n",
+ "\n",
+ "merged_raster = cast(Raster, run.output[\"merged_product\"][0])\n",
+ "categories = cast(CategoricalRaster, run.output[\"categorical_raster\"][0]).categories\n",
+ "\n",
+ "out_image = read_raster(merged_raster, bounding_box)[0]\n",
+ "\n",
+ "cmap = plt.get_cmap(\"Greens\", len(categories))\n",
+ "\n",
+ "# Plot the cropped image with latitude and longitude in the axes\n",
+ "plt.imshow(out_image[0], cmap=cmap, extent=[minx, maxx, miny, maxy])\n",
+ "\n",
+ "# Add a legend\n",
+ "legend = plt.legend(\n",
+ " handles=[\n",
+ " plt.Rectangle((0, 0), 1, 1, color=cmap(0)),\n",
+ " plt.Rectangle((0, 0), 1, 1, color=cmap(1)),\n",
+ " ],\n",
+ " labels=categories,\n",
+ ")\n",
+ "\n",
+ "# Plot geom on top of the cropped image\n",
+ "plt.plot(*geom.exterior.xy, color=\"red\")\n",
+ "\n",
+ "plt.title(\"GLAD Forest Map\")\n",
+ "plt.xlabel(\"Longitude\")\n",
+ "plt.ylabel(\"Latitude\")\n",
+ "\n",
+ "\n",
+ "plt.show()"
+ ]
+ }
+ ],
+ "metadata": {
+ "description": "This notebook downloads the Global Land Analysis (GLAD) forest extent maps.",
+ "disk_space": "",
+ "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.8.15"
+ },
+ "name": "Download Glad Forest Map",
+ "running_time": "",
+ "tags": [
+ "Remote Sensing",
+ "Deforestation",
+ "Sustainability"
+ ]
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
\ No newline at end of file
diff --git a/notebooks/forest/download_hansen_forest_map.ipynb b/notebooks/forest/download_hansen_forest_map.ipynb
new file mode 100644
index 00000000..00e44fc3
--- /dev/null
+++ b/notebooks/forest/download_hansen_forest_map.ipynb
@@ -0,0 +1,939 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "3c0a2457",
+ "metadata": {},
+ "source": [
+ "# Download Hansen Forest Change\n",
+ "\n",
+ "In this notebook, we download layers from the [Hansen Dataset](https://storage.googleapis.com/earthenginepartners-hansen/GFC-2022-v1.10/download.html) using FarmVibes.AI and visualize it. The data, distributed under the [Creative Commons Attribution 4.0 International License](http://creativecommons.org/licenses/by/4.0/), illustrates how forests changed from 2000 to 2022 with 30 meter resolution and covers the whole planet. Users can use this dataset to observe how the forest changed over time in a region of interest.\n",
+ "\n",
+ "This global dataset, split into 10x10 degree tiles, contains seven files per tile. Each file has unsigned 8-bit values and a resolution of around 30 meters per pixel at the equator. The dataset includes the following layers:\n",
+ "\n",
+ " - `treecover2000`: Tree cover in the year 2000, defined as canopy closure for all vegetation taller than 5m in height. Encoded as a percentage per output grid cell, in the range 0–100.\n",
+ " - `gain`: Forest gain during the period 2000-2012, defined as the inverse of loss, or a non-forest to forest change entirely within the study period. Encoded as either 1 (gain) or 0 (no gain).\n",
+ " - `lossyear`: Forest loss during the period 2000-2022, defined as a stand-replacement disturbance, or a change from a forest to non-forest state. Encoded as either 0 (no loss) or else a value in the range 1-22, representing loss detected primarily in the year 2001-2022, respectively.\n",
+ " - `datamask`: Three values representing areas of no data (0), mapped land surface (1), and persistent water bodies (2) based on 2000-2012.\n",
+ " - `first`: Circa year 2000 Landsat 7 cloud-free image composite (first).\n",
+ "Reference multispectral imagery from the first available year, typically 2000. \n",
+ " - `last`: cloud-free image composites for the last year in the series (e.g., 2022). \n",
+ " \n",
+ " Only the 'lossyear' and 'last' categories are updated annually. The reflectance values in the imagery are scaled to an 8-bit data range.\n",
+ "\n",
+ "Dataset Reference:\n",
+ "\n",
+ "Hansen, M. C., P. V. Potapov, R. Moore, M. Hancher, S. A. Turubanova, A. Tyukavina, D. Thau, S. V. Stehman, S. J. Goetz, T. R. Loveland, A. Kommareddy, A. Egorov, L. Chini, C. O. Justice, and J. R. G. Townshend. 2013. High-Resolution Global Maps of 21st-Century Forest Cover Change. Science 342 (15 November): 850-53. Data available on-line from: https://glad.earthengine.app/view/global-forest-change."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "bfa4f4fc",
+ "metadata": {},
+ "source": [
+ "### Micromamba environment setup\n",
+ "To install the required packages, see [this README file](../README.md). You can activate the environment with the following command:\n",
+ "\n",
+ "\n",
+ "```bash\n",
+ "$ micromamba activate farmvibes-ai\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "447dcf5f",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from shapely import geometry as shpg\n",
+ "from datetime import datetime\n",
+ "\n",
+ "from matplotlib.ticker import MaxNLocator\n",
+ "\n",
+ "import numpy as np\n",
+ "import pandas as pd\n",
+ "\n",
+ "import matplotlib.pyplot as plt\n",
+ "import matplotlib.colors as mcolors\n",
+ "import matplotlib.ticker as ticker\n",
+ "\n",
+ "from shapely.geometry import box\n",
+ "import matplotlib.pyplot as plt\n",
+ "from typing import cast\n",
+ "\n",
+ "import sys\n",
+ "\n",
+ "sys.path.append(\"../\")\n",
+ "from shared_nb_lib.raster import read_raster\n",
+ "\n",
+ "from vibe_core.data import DataVibe, Raster\n",
+ "from vibe_core.client import get_default_vibe_client"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "3a8aff46",
+ "metadata": {},
+ "source": [
+ "### Create Vibe client and document the hansen download workflow\n",
+ "\n",
+ "Before executing the [workflow](https://microsoft.github.io/farmvibes-ai/docfiles/markdown/WORKFLOWS.html), let's observe its documentation using a FarmVibes.AI python client."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "99fa0c54",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n"
+ ],
+ "text/plain": []
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
Downloads and merges Global Forest Change (Hansen) rasters that intersect the user-provided \n",
+ " geometry/time range. The workflow lists Global Forest Change (Hansen) products that intersect \n",
+ " the user-provided geometry/time range, downloads the data for each of them, and merges the \n",
+ " rasters. The dataset is available at 30m resolution and is updated annually. The data contains \n",
+ " information on forest cover, loss, and gain. The default dataset version is GFC-2022-v1.10 and \n",
+ " is passed to the workflow as the parameter tiles_folder_url. For the default version, the \n",
+ " dataset is available from 2000 to 2022. Dataset details can be found at \n",
+ " https://storage.googleapis.com/earthenginepartners-hansen/GFC-2022-v1.10/download.html. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " Downloads and merges Global Forest Change (Hansen) rasters that intersect the user-provided \n",
+ " geometry/time range. The workflow lists Global Forest Change (Hansen) products that intersect \n",
+ " the user-provided geometry/time range, downloads the data for each of them, and merges the \n",
+ " rasters. The dataset is available at 30m resolution and is updated annually. The data contains \n",
+ " information on forest cover, loss, and gain. The default dataset version is GFC-2022-v1.10 and \n",
+ " is passed to the workflow as the parameter tiles_folder_url. For the default version, the \n",
+ " dataset is available from 2000 to 2022. Dataset details can be found at \n",
+ " https://storage.googleapis.com/earthenginepartners-hansen/GFC-2022-v1.10/download.html. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- layer_name (default: None): Name of the Global Forest Change (Hansen) layer. Can be any of the\n",
+ " following names 'treecover2000', 'loss', 'gain', 'lossyear', 'datamask', 'first', 'last'. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1mlayer_name\u001b[0m (\u001b[34mdefault: None\u001b[0m): Name of the Global Forest Change (Hansen) layer. Can be any of the\n",
+ " following names 'treecover2000', 'loss', 'gain', 'lossyear', 'datamask', 'first', 'last'. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- tiles_folder_url (default: \n",
+ " https://storage.googleapis.com/earthenginepartners-hansen/GFC-2022-v1.10/): URL to the Global \n",
+ " Forest Change (Hansen) dataset. It specifies the dataset version and is used to download the \n",
+ " data. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1mtiles_folder_url\u001b[0m (\u001b[34mdefault: \u001b[0m \n",
+ " \u001b[34mhttps://storage.googleapis.com/earthenginepartners-hansen/GFC-2022-v1.10/\u001b[0m): URL to the Global \n",
+ " Forest Change (Hansen) dataset. It specifies the dataset version and is used to download the \n",
+ " data. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- group: This op groups rasters in time according to 'criterion'. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1mgroup\u001b[0m: This op groups rasters in time according to 'criterion'. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- merge: Merges rasters in a sequence to a single raster. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1mmerge\u001b[0m: Merges rasters in a sequence to a single raster. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "client = get_default_vibe_client()\n",
+ "\n",
+ "WORKFLOW_NAME = \"data_ingestion/hansen/hansen_forest_change_download\"\n",
+ "client.document_workflow(WORKFLOW_NAME)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b894d84c",
+ "metadata": {},
+ "source": [
+ "### Setting up Input Geometry and Time Frame\n",
+ " \n",
+ "Now, we will establish the desired geometry and time frame for downloading the Hansen products. The workflow will fetch and merge all the tiles that intersect with the given input."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "f12b5333",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# GeoJSON definition of a polygon over the potential forest area\n",
+ "geo_json = {\n",
+ " \"type\": \"Feature\",\n",
+ " \"geometry\": {\n",
+ " \"type\": \"Polygon\",\n",
+ " \"coordinates\": [\n",
+ " [\n",
+ " [-86.773827, 14.575496],\n",
+ " [-86.770459, 14.579302],\n",
+ " [-86.764283, 14.575102],\n",
+ " [-86.769591, 14.567595],\n",
+ " [-86.773827, 14.575496],\n",
+ " ]\n",
+ " ],\n",
+ " },\n",
+ " \"properties\": {},\n",
+ "}\n",
+ "\n",
+ "geom = shpg.shape(geo_json[\"geometry\"])\n",
+ "time_range = datetime(2000, 1, 1), datetime(2022, 1, 2)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "fb34e580",
+ "metadata": {},
+ "source": [
+ "### Run FarmVibes.AI Workflow\n",
+ "\n",
+ "To execute the workflow users need to provide the geometry of interest (`geom`), time range (`time_range`), and the name of the layer to be downloaded as a workflow parameter (`layer_name`). The layer can be any value from the set (`treecover2000`, `gain`, `lossyear`, `datamask`, `first`, `last`).\n",
+ "\n",
+ "In the next cell, we initiate two `runs` for the `treecover2000` and `lossyear` layers, and then wait for both workflows to complete (`client.monitor(runs)`)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "76b0f81f",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n"
+ ],
+ "text/plain": []
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "a2e39cd7cde14fa4b96039434eed5ede",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "Output()"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n"
+ ],
+ "text/plain": []
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "runs = []\n",
+ "\n",
+ "for layer_name in [\"treecover2000\", \"lossyear\"]:\n",
+ " run = client.run(\n",
+ " WORKFLOW_NAME,\n",
+ " \"Hansen dataset download\",\n",
+ " geometry=geom,\n",
+ " time_range=time_range,\n",
+ " parameters={\"layer_name\": layer_name},\n",
+ " )\n",
+ " runs.append(run)\n",
+ "\n",
+ "client.monitor(runs)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "4b517e3c",
+ "metadata": {},
+ "source": [
+ "### Visualizing Dataset Details\n",
+ " \n",
+ "In the upcoming cells, we will depict the changes in the forest over the years within the user's region of interest. Following that, we will examine the division of the area in terms of forest and non-forest proportions. Finally, we will assess how the percentage of forest pixels has evolved over time."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "44e97e42",
+ "metadata": {},
+ "source": [
+ "#### Plot forest loss over time\n",
+ "\n",
+ "In the following cell, we create a plot function that reads the `treecover2000` layer and the `lossyear`. The `treecover2000` layer has pixel values ranging from 0 to 100 that represents the percentage of tree cover in the area. Here, we use a black to green colormap. Then, we plot the `lossyear` with pixel values enconded as 0 (no loss) or else a value in the range 1-20, representing loss detected primarily in the year 2001-2022, respectively. The second layer is depicted using a yellow to red colormap. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "e9c3e812",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def plot_hansen_map(treecover2000: Raster, lossyear: Raster, geom: shpg.Polygon, first_year: int):\n",
+ "\n",
+ " colors = [\"black\", \"green\"]\n",
+ " cmap = mcolors.LinearSegmentedColormap.from_list(\"mycmap\", colors)\n",
+ "\n",
+ " # Define your geometry\n",
+ " bounding_box = box(*geom.buffer(0.01).bounds)\n",
+ "\n",
+ " # Get the bounds of the geometry\n",
+ " minx, miny, maxx, maxy = bounding_box.bounds\n",
+ "\n",
+ " merged_raster = cast(Raster, treecover2000)\n",
+ " out_image = read_raster(merged_raster, bounding_box)[0]\n",
+ "\n",
+ " loss_image = read_raster(cast(Raster, lossyear), bounding_box)[0][0]\n",
+ " \n",
+ " # Create a masked array where the mask is True for zero values\n",
+ " masked_loss = np.ma.masked_where(loss_image == 0, loss_image)\n",
+ "\n",
+ " # Set data type to float\n",
+ " masked_loss = masked_loss.astype(float)\n",
+ " masked_loss += first_year\n",
+ "\n",
+ " # Plot the cropped image with latitude and longitude in the axes\n",
+ " plt.imshow(out_image[0], cmap=cmap, extent=[minx, maxx, miny, maxy])\n",
+ "\n",
+ " loss_cmap = plt.cm.get_cmap(\"YlOrRd\").copy()\n",
+ " loss_cmap.set_bad(color=\"none\")\n",
+ "\n",
+ " # Plot the loss image on top of the cropped image\n",
+ " plt.imshow(masked_loss, cmap=loss_cmap, alpha=0.8, extent=[minx, maxx, miny, maxy])\n",
+ "\n",
+ " # Plot geom on top of the cropped image\n",
+ " plt.plot(*geom.exterior.xy, color=\"blue\")\n",
+ "\n",
+ " # Add a legend for the loss_image\n",
+ " cbar = plt.colorbar()\n",
+ " tick_locator = ticker.MaxNLocator(nbins=max(loss_image.flatten()))\n",
+ " cbar.locator = tick_locator\n",
+ " cbar.update_ticks()\n",
+ "\n",
+ " cbar.set_label(\"Year for the forest to non-forest transition\")\n",
+ "\n",
+ " plt.title(\"Forest Extent\")\n",
+ " plt.xlabel(\"Longitude\")\n",
+ " plt.ylabel(\"Latitude\")\n",
+ "\n",
+ " plt.text(\n",
+ " 0.11,\n",
+ " -0.005,\n",
+ " \"Source: Hansen/UMD/Google/USGS/NASA\",\n",
+ " fontsize=7,\n",
+ " transform=plt.gcf().transFigure,\n",
+ " )\n",
+ "\n",
+ " plt.show()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b334b0e1",
+ "metadata": {},
+ "source": [
+ "### Plot the `treecover2000` and `lossyear` rasters"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "223eef5c",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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7bfXX3+dXr16ZQ35/cIXvoDrugwaNMhqIjJx8MEHc/DBB/Ozn/2M5557jhEjRvCHP/yBX/3qV4AXAbOzMH36dF577TWuu+46/vu//5vLL7+cm2++OXCM7fq9e/emvLyc1tbWnDQyuWJntjVChAg7D1FIcIQIOwF77bUXhxxyCHfccQebN29O7V+1ahX//Oc/rQnJTIwdO5Z4PM6VV16ZpgVwXZePPvoIgC1bttDS0hL4/eCDDyYWi9HY2JjaV1ZWFqhLR2HFihVcd911XHrppfzwhz/kRz/6EbfccgtLly4NHGe7fjweZ9y4cdx7772sWrUqrexNmza1qU5lZWWpcOgIESLsOYg0JREi7CT89re/5aSTTqK6upqJEyemQoIrKyuZMWNG1vO/8IUv8Ktf/Yqf/OQnrF+/njFjxlBeXs66detYsGABF198MZdddhlPPPEEkyZN4owzzuCAAw6gpaWFv/zlLymBLxg2bBiPPfYYv/vd7+jXrx+DBg3iiCOOyFiHF198kbvuustat+rqahoaGjjvvPP44he/yK9//WsArrzySh588EEuuOACXn311ZRGKOz6v/nNb3jyySc54ogjuOiiizjooIP4+OOPefHFF3nsscf4+OOP8+h1v6333HMP06ZNY/jw4XTt2pVTTjkl73IiRIjQydiFkT8RIuyxkJDg559/PuNxjz32mDtixAi3tLTUraiocE855RR39erVgWMkJHjTpk3WMu699173qKOOcsvKytyysjJ38ODBbk1NjbtmzRrXdV33rbfecidMmOB+4QtfcEtKStwePXq4xx57rPvYY48Fynn99dfdkSNHuqWlpS6QMTw4W0iwnDt16lQ3Ho+7K1asCJz/wgsvuAUFBe73v//9nK5fV1fn1tTUuPvss49bWFjoVlVVuccff7z7xz/+MXWMhASboc9S19tvvz21b+vWre7ZZ5/tduvWzQWi8OAIEfYQOK4beYdFiBAhQoQIEXY9Ip+SCBEiRIgQIcJugYiURIgQIUKECBF2C0SkJEKECBEiRIiwWyAiJREiRIgQIcJuhJkzZzJ8+HDKy8vp06cPY8aMSct51NDQQE1NDT179qRr166MGzeOurq61O8vv/wyZ511Fvvssw+lpaUMGTKEm266Ke1aS5Ys4bDDDqO4uJj999+fOXPm7OzmZURESiJEiBAhQoTdCEuX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yI033sj69etzKqdbt24ccMABrF27FoA333yTW265hVWrVqVMQl/5yld4+umnmTVrFn/4wx+s5RxxxBG0tLSwfv16DjzwQOsxua74GCFCWxB3oCCeIOZAS8LxImciRIiw05Grm1Om8z+L+OCDDzjzzDNZsmRJKt395s2bOfbYY/nb3/5G79698y5ztyUl55xzTmBZZoBRo0ZxzjnncMEFF+RcztatW3nzzTc555xzAC+tLmBNrZspre5LL71ELBajT58+OV87wp6P6n3X23/Q9vhORDwGZ57xfnCnzuzUxvpMHLcxGN4rav1Mzr4RIlhw22t7p/toiQlHR2fp/QJ9nsLEkzbCc8CanVnzCPli8uTJfPrpp/zf//0fQ4YMAWD16tWcd955TJkyhb/+9a95l7lL85Rs3bqVl156KZX6dt26dbz00ku888479OzZk6FDhwa2wsJCqqqqApqK448/nltuuSX1/2WXXcbSpUtZv349zz33HN/61reIx+OcddZZAAwePJj999+f733ve/zrX//izTff5Prrr+fRRx9NOe0sW7aMG2+8kZdffpm33nqLu+++m6lTp/Ld736X7t27t62xLfhOXuLwJ85fYPeCN7dMgseWV8OM7pFyEvgrFm9L1mkLvh+KuZnoSAdCnfRNe/BvxfNvqcePuBG7crP61PleJBeGWX+zvmF9K/ZvnbJf++DIZuaI0PvFn0dtzY1OMsQ22eSEQ2tycxwoiEMs5uLYIlEMtNruj+67tqrMdS4J8QsQx0L9PEkf6b7SjtTii6O3IjzfgRJ8XxntdK3boT/zifCy+WfkC/OZ6Ezo50/73Ggn0LDR2vZM6nsGQQdvnRtJ3iUZDyRJopSh3w19j0vxHFXFIXY76WH2An1tIbphCTzCnMa1r1IBnpN7r5D+2AlwcNu95YOZM2cyfPhwysvL6dOnD2PGjGHNmiAja2hooKamhp49e9K1a1fGjRtHXV3QbDtlyhSGDRtGcXExhxxySNp1bHm9HMehrKwsp3ouXryYW2+9NUVIwEthP2vWLB5++OG82izYpZqSF154gWOPPTb1v/hjnHfeecyZMyenMt58800+/PDD1P/vvvsuZ511Fh999BG9e/fmqKOOYvny5Sk1UmFhIQ899BCXX345p5xyClu3bmX//ffnjjvuYPTo0YBnhvnb3/7GjBkzaGxsZNCg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TJw4EWlpaUhJScGECROQk5ODnj17AgAGDRqEjh07YuTIkXjiiSdQUFCABx98ELm5uTYJzYYNGwAApaWlOHDgADZs2ICYmJhTRlw0URdWQi5OCxQXFyM1NRVFRUVn/C7s19JWt51nFn4t7QR+XW1VEU7dP3v2bIwZMwaADJ5211134c0330RFRQUGDx6M559/3qaa+fHHHzFu3DisWLECiYmJGD16NP7xj3/A57PkEU5ltW7dGrt27arVNkULV1LiwkRsbCwmT55crZ7zTMCvpa1uO88s/FraCfy62qoiGllBXFwcpk+fjunTp4dN07p1a3z44YcnXFZdwpWUuHDhwoULFy7qBdzgaS5cuHDhwoWLegGXlLhw4cKFCxcu6gVcUuLChQsXLly4qBdwSYkLFy5cuHDhol7AJSVnEL777jtceeWVaNy4MVJSUtC7d28sX748JN2cOXNw3nnnIS4uDunp6cjNzQ2b565du6BpmuNn3jwrlP26devQv39/NGjQAA0bNsTgwYOxcaM9TPTXX3+Niy++GHFxcWjZsiWeeOKJ06qdc+bMCZumsLAQALBixQrH34/nrIn63E5q6/nnn4/Y2Fj85je/wZw5c2rcxlPd1mjyDZfPmjVrzqh2Aqf/OwrA8fe33nrL/L0231EXJwHCxRmDc845R1x22WVi48aN4rvvvhO33nqrSEhIEPv27TPT/POf/xTNmjUTb7zxhvj+++/Fxo0bxYIFC8LmGQgExL59+2yfhx9+WCQlJYmSkhIhhBAlJSUiLS1NjBkzRmzdulVs3rxZDB8+XGRkZIjKykohhBBFRUUiIyNDXHfddWLz5s3izTffFPHx8eLFF188bdpZVlYWkmbw4MHikksuMfNZvny5ACC2bdtmSxcMBs+odv7www8iISFBTJw4UWzZskX861//El6vVyxatKjG7TyVbY0m3507dwoAYunSpba8aGyfKe08E95RIYQAIGbPnm1Ld+zYMfP32nxHXdQ+XFJyhuDAgQMCgPj000/Na8XFxQKAWLJkiRBCiEOHDon4+HixdOnSEyorKytL3Hjjjeb3devWCQBi9+7d5rWvv/5aABDbt28XQgjx/PPPi4YNG4qKigozzaRJk0S7du1qVPapbKeKwsJC4ff7xWuvvWZeownv8OHDJ1R2fW/nvffeKzp16mRLd80114jBgwfXuPxT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",
+ "text/plain": [
+ "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "treecover2000 = runs[0].output[\"merged_raster\"][0]\n",
+ "lossyear = runs[1].output[\"merged_raster\"][0]\n",
+ "\n",
+ "plot_hansen_map(treecover2000, lossyear, geom, time_range[0].year)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "a8be22b0",
+ "metadata": {},
+ "source": [
+ "### Plot the proportion of forest/non-forest pixels\n",
+ "\n",
+ "In the next cell, the proportion of forest/non-forest pixels is plotted. This is done by analyzing the `lossyear` raster, which represents forest loss over time. The cell calculates the ratio of forest pixels to total pixels within the specified geometry. Then, a pie chart is created using matplotlib to visually compare the proportion of forest and non-forest pixels. Finally, we show the loss table over time in case the user wants to access the values used to plot the graphs."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "id": "b1be08cf",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def read_loss_dict(lossyear: Raster, geom: shpg.Polygon, nodata: int = 255):\n",
+ " # Read the raster\n",
+ " loss_image = read_raster(cast(Raster, lossyear), geom, nodata=nodata)[0][0]\n",
+ "\n",
+ " # Count the frequency of each value in the loss_image\n",
+ " unique, counts = np.unique(loss_image, return_counts=True)\n",
+ "\n",
+ " loss_dict = {uni: count for uni, count in zip(unique, counts)}\n",
+ "\n",
+ " # Delete 255 from the dictionary\n",
+ " del loss_dict[nodata]\n",
+ "\n",
+ " return loss_dict\n",
+ "\n",
+ "\n",
+ "def plot_forest_ratio(lossyear: Raster, geom: shpg.Polygon):\n",
+ " # Read the raster\n",
+ "\n",
+ " loss_dict = read_loss_dict(lossyear, geom)\n",
+ "\n",
+ " # Amount of pixels\n",
+ " total_pixels = sum(loss_dict.values())\n",
+ "\n",
+ " # Forest Pixels\n",
+ " forest_pixels = loss_dict[0]\n",
+ " forest_ratio = forest_pixels / total_pixels\n",
+ "\n",
+ " # Plot a matplotlib pie chart comparing forest and not forest pixels\n",
+ " labels = \"Forest\", \"Not Forest\"\n",
+ "\n",
+ " sizes = [forest_ratio, 1 - forest_ratio]\n",
+ "\n",
+ " fig1, ax1 = plt.subplots()\n",
+ " ax1.pie(sizes, labels=labels, autopct=\"%1.1f%%\", startangle=90)\n",
+ " ax1.axis(\"equal\")\n",
+ "\n",
+ " # Title\n",
+ " plt.title(\"Forest/Non-Forest pixels proportion\")\n",
+ "\n",
+ " plt.show()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "7e73283c",
+ "metadata": {},
+ "source": [
+ "### Plot the Forest/Non-Forest pie chart"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "id": "7fe9a3ef",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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",
+ "text/plain": [
+ "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "plot_forest_ratio(lossyear, geom)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "ec65f6ba",
+ "metadata": {},
+ "source": [
+ "### Plot the Forest change over time\n",
+ "\n",
+ "In the next cell, we create a `plot_forest_loss` function that takes a raster image of forest loss per year and a polygon geometry as inputs. It reads the raster data and calculates the percentage of total forest loss for each year. The function then generates a bar plot with the percentage of forest loss for each year in the dataset."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "id": "0354f1bb",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def plot_forest_loss(lossyear: Raster, geom: shpg.Polygon):\n",
+ " # Read the raster\n",
+ " loss_dict = read_loss_dict(lossyear, geom)\n",
+ "\n",
+ " # Amount of pixels\n",
+ " total_pixels = sum(loss_dict.values())\n",
+ "\n",
+ " # Pixel count\n",
+ " new_values = {key: 100 * loss_dict[key] / total_pixels for key in loss_dict}\n",
+ "\n",
+ " # Remove the key 0 as it is not a loss. It is the forest \n",
+ " # pixels that have not been lost.\n",
+ " new_values.pop(0, None)\n",
+ "\n",
+ " # Create lists of the years and pixel counts\n",
+ " years = list(new_values.keys())\n",
+ " years = [year + time_range[0].year for year in years]\n",
+ "\n",
+ " pixel_counts = list(new_values.values())\n",
+ "\n",
+ " # Create a bar plot\n",
+ " plt.bar(years, pixel_counts)\n",
+ "\n",
+ " # Set the labels for the x and y axes\n",
+ " plt.xlabel(\"Year\")\n",
+ " plt.ylabel(\"Forest Loss (%)\")\n",
+ " plt.title(\"Forest to Non-Forest affected area (%) over time\")\n",
+ "\n",
+ " # Set x-axis to only use integer values\n",
+ " ax = plt.gca()\n",
+ " ax.xaxis.set_major_locator(MaxNLocator(integer=True))\n",
+ "\n",
+ " plt.xticks(range(min(years), max(years) + 1), rotation=90)\n",
+ "\n",
+ " # Show the plot\n",
+ " plt.show()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "id": "0065c6e2",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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RESFilmDTHJyMqpUqYJ58+Zlmvfw4UOcOnUKY8aMwalTp7Bx40ZER0ejbdu2ZuiUiIiICiNLcz55q1at0KpVqyzn6fV67Nq1y2BaREQEAgMDcfXqVZQqVepltEhERESFmFmDTF4lJSVBo9HA2dk522VSUlKQkpKijN+7d+8ldEZERETmoJqTfR8/foyPP/4Yb775JpycnLJdbsqUKdDr9crg5eX1ErskIiKil0kVQebp06fo0qULRAQLFizIcdmRI0ciKSlJGeLj419Sl0RERPSyFfpDSxkh5sqVK9i7d2+Oe2MAQKfTQafTvaTuiIiIyJwKdZDJCDExMTGIjIyEq6uruVsiIiKiQsSsQebBgwe4dOmSMh4bG4szZ86gSJEi8PDwQOfOnXHq1Cls3boVaWlpSEhIAAAUKVIE1tbW5mqbiIiICgmzBpkTJ06gcePGyviQIUMAAL169cK4cePw448/AgCqVq1q8LjIyEg0atToZbVJREREhZRZg0yjRo0gItnOz2keERERkSquWiIiIiLKCoMMERERqRaDDBEREakWgwwRERGpFoMMERERqRaDDBEREakWgwwRERGpFoMMERERqRaDDBEREakWgwwRERGpFoMMERERqRaDDBEREakWgwwRERGpFoMMERERqRaDDBEREakWgwwRERGpFoMMERERqRaDDBEREakWgwwRERGpFoMMERERqRaDDBEREakWgwwRERGpFoMMERERqRaDDBEREakWgwwRERGpFoMMERERqRaDDBEREakWgwwRERGpFoMMERERqRaDDBEREakWgwwRERGpFoMMERERqRaDDBEREakWgwwRERGpFoMMERERqRaDDBEREakWgwwRERGpFoMMERERqZaluRsgIiIyNZ8RP5msVtzUUJPVItPjHhkiIiJSLQYZIiIiUi2zBpkDBw6gTZs28PT0hEajwebNmw3miwjGjh0LDw8P2NraolmzZoiJiTFPs0RERFTomDXIJCcno0qVKpg3b16W8z///HN8+eWXWLhwIX755RfY29sjJCQEjx8/fsmdEhERUWFk1pN9W7VqhVatWmU5T0Qwe/ZsjB49Gu3atQMArFixAm5ubti8eTO6dev2MlslIiKiQqjQniMTGxuLhIQENGvWTJmm1+tRu3ZtHD16NNvHpaSk4N69ewYDERERvZoKbZBJSEgAALi5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4tatW6hevboyLS0tDQcOHEBERARSUlJgYWFh8BidTgedTveyWyUiIiIzKNRBpmnTpvjjjz8MpvXp0wd+fn74+OOPM4UYIiIi+m8p1EHG0dERAQEBBtPs7e3h6uqaaToRERH99xTqIENERFQY+Yz4yWS14qaGmqzWf5Hqgsy+ffvM3QIREREVEoX6qiUiIiKinDDIEBERkWoxyBAREZFqMcgQERGRajHIEBERkWoxyBAREZFqMcgQERGRajHIEBERkWoxyBAREZFqMcgQERGRajHIEBERkWoxyBAREZFqMcgQERGRajHIEBERkWoxyBAREZFqMcgQERGRajHIEBERkWoxyBAREZFqMcgQERGRajHIEBERkWoxyBAREZFqMcgQERGRajHIEBERkWoxyBAREZFqMcgQERGRajHIEBERkWoxyBAREZFqMcgQERGRajHIEBERkWoxyBAREZFqMcgQERGRajHIEBERkWoxyBAREZFqMcgQERGRajHIEBERkWoxyBAREZFqMcgQERGRajHIEBERkWoxyBAREZFqMcgQERGRajHIEBERkWoV6iAzZcoU1KpVC46OjihevDjat2+P6Ohoc7dFREREhUShDjL79+/HoEGDcOzYMezatQtPnz5FixYtkJycbO7WiIiIqBCwNHcDOdm+fbvB+LJly1C8eHGcPHkSDRo0MFNXREREVFgU6iDzvKSkJABAkSJFsl0mJSUFKSkpyvi9e/cKvC8iIiIyj0J9aOlZ6enpCA8PR3BwMAICArJdbsqUKdDr9crg5eX1ErskIiKil0k1QWbQoEE4e/YsvvvuuxyXGzlyJJKSkpQhPj7+JXVIREREL5sqDi2999572Lp1Kw4cOICSJUvmuKxOp4NOp3tJnREREZE5FeogIyJ4//33sWnTJuzbtw++vr7mbomIiIgKkUIdZAYNGoQ1a9bghx9+gKOjIxISEgAAer0etra2Zu6OiIiIzK1QnyOzYMECJCUloVGjRvDw8FCGtWvXmrs1IiIiKgQK9R4ZETF3C0RERFSIFeo9MkREREQ5YZAhIiIi1WKQISIiItVikCEiIiLVKtQn+xIRUdZ8RvxkslpxU0NNVovoZeMeGSIiIlItBhkiIiJSLQYZIiIiUi0GGSIiIlItBhkiIiJSLQYZIiIiUi1efp0Pprz8EeAlkERERHnFPTJERESkWgwy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",
+ "text/plain": [
+ "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "plot_forest_loss(lossyear, geom)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b7e20044",
+ "metadata": {},
+ "source": [
+ "### Displaying the Forest Loss Pixels Over Time\n",
+ "\n",
+ "Finally, create a function that reads the forest loss data for each year and plots a table showing the pixel count for each respective year."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "id": "eb6bc18d",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def show_loss_table(lossyear: Raster, geom: shpg.Polygon):\n",
+ " # Read the raster\n",
+ " loss_dict = read_loss_dict(lossyear, geom)\n",
+ "\n",
+ " # Create a dictionary with the loss_dict values\n",
+ " data = {\n",
+ " \"Year\": [year + time_range[0].year for year in list(loss_dict.keys())[1:]],\n",
+ " \"#Pixels\": list(loss_dict.values())[1:],\n",
+ " }\n",
+ "\n",
+ " # Create a dataframe from the dictionary\n",
+ " df_loss = pd.DataFrame(data)\n",
+ "\n",
+ " # Sort the dataframe by the 'Year' column\n",
+ " df_loss = df_loss.sort_values(\"Year\")\n",
+ "\n",
+ " # Reset the index of the dataframe\n",
+ " df_loss = df_loss.reset_index(drop=True)\n",
+ "\n",
+ " # Amount of pixels\n",
+ " total_pixels = sum(loss_dict.values())\n",
+ " \n",
+ " return total_pixels, df_loss"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "id": "01810a05",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Total pixels: 1786\n",
+ "\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
\n",
+ "
Year
\n",
+ "
#Pixels
\n",
+ "
\n",
+ " \n",
+ " \n",
+ "
\n",
+ "
0
\n",
+ "
2001
\n",
+ "
23
\n",
+ "
\n",
+ "
\n",
+ "
1
\n",
+ "
2004
\n",
+ "
3
\n",
+ "
\n",
+ "
\n",
+ "
2
\n",
+ "
2007
\n",
+ "
98
\n",
+ "
\n",
+ "
\n",
+ "
3
\n",
+ "
2008
\n",
+ "
2
\n",
+ "
\n",
+ "
\n",
+ "
4
\n",
+ "
2011
\n",
+ "
2
\n",
+ "
\n",
+ "
\n",
+ "
5
\n",
+ "
2012
\n",
+ "
244
\n",
+ "
\n",
+ "
\n",
+ "
6
\n",
+ "
2013
\n",
+ "
28
\n",
+ "
\n",
+ "
\n",
+ "
7
\n",
+ "
2016
\n",
+ "
183
\n",
+ "
\n",
+ "
\n",
+ "
8
\n",
+ "
2017
\n",
+ "
68
\n",
+ "
\n",
+ "
\n",
+ "
9
\n",
+ "
2019
\n",
+ "
1
\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " Year #Pixels\n",
+ "0 2001 23\n",
+ "1 2004 3\n",
+ "2 2007 98\n",
+ "3 2008 2\n",
+ "4 2011 2\n",
+ "5 2012 244\n",
+ "6 2013 28\n",
+ "7 2016 183\n",
+ "8 2017 68\n",
+ "9 2019 1"
+ ]
+ },
+ "execution_count": 14,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "total_pixels, df_loss = show_loss_table(lossyear, geom)\n",
+ "\n",
+ "print(f\"Total pixels: {total_pixels}\\n\")\n",
+ "\n",
+ "# Print the dataframe without the indexes\n",
+ "df_loss"
+ ]
+ }
+ ],
+ "metadata": {
+ "description": "This notebook contains functions to download and process the Global Forest Change (Hansen) maps.",
+ "disk_space": "",
+ "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.8.18"
+ },
+ "name": "Download Global Forest Change (Hansen) maps.",
+ "running_time": "",
+ "tags": [
+ "Remote Sensing",
+ "Deforestation",
+ "Sustainability"
+ ]
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
\ No newline at end of file
diff --git a/notebooks/forest/forest_change_detection.ipynb b/notebooks/forest/forest_change_detection.ipynb
new file mode 100644
index 00000000..6261efde
--- /dev/null
+++ b/notebooks/forest/forest_change_detection.ipynb
@@ -0,0 +1,901 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Detecting Forest Changes with FarmVibes.AI\n",
+ "\n",
+ "This notebook demonstrates how to detect forest changes in ALOS PALSAR 2.1 Forest/Non-Forest maps using FarmVibes.AI. The reader can check [this notebook](./download_alos_forest_map.ipynb) to see how to download and visualize ALOS forest maps.\n",
+ "\n",
+ "This notebook is divided into the following sections:\n",
+ "\n",
+ "1. **Workflow setup**: It checks the workflow documentation using the FarmVibes.AI python client, and define the evaluation geometry and time range.\n",
+ "2. **Running the workflow**: The section shows how to execute the forest changes workflow and provide its parameters.\n",
+ "3. **Interpreting the results**. Finally, we will visualize and discuss the results."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Micromamba environment setup\n",
+ "To install the required packages, see [this README file](../README.md). You can activate the environment with the following command:\n",
+ "```bash\n",
+ "$ micromamba activate farmvibes-ai\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 1. Workflow setup\n",
+ "\n",
+ "In this Jupyter notebook, we are going to run the `forest_ai/deforestation/forest_change_detection` workflow in FarmVibes.AI, \n",
+ "designed to analyze changes in forest coverage over a specific time range within a user-defined geographical area. \n",
+ "The next cells will document the workflow using the FarmVibes.AI default client and define the user input (geometry + time-range).\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n"
+ ],
+ "text/plain": []
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
Detects increase/decrease trends in forest pixel levels over the user-input geometry and time \n",
+ " range for the ALOS forest map. This workflow combines the alos_forest_extent_download_merge and \n",
+ " ordinal_trend_detection workflows to detect increase/decrease trends in the forest pixel levels \n",
+ " over the user-provided geometry and time range for the ALOS forest map. The ALOS PALSAR 2.1 \n",
+ " Forest/Non-Forest Maps are downloaded in the alos_forest_extent_download_merge workflow. Then \n",
+ " the ordinal_trend_detection workflow clips the ordinal raster to the user-provided geometry and \n",
+ " time range and determines if there is an increasing or decreasing trend in the forest pixel \n",
+ " levels over them. alos_trend_detection uses the Cochran-Armitage test to detect trends in the \n",
+ " forest levels over the years. The null hypothesis is that there is no trend in the pixel levels\n",
+ " over the list of rasters. The alternative hypothesis is that there is a trend in the forest \n",
+ " pixel levels over the list of rasters (one for each year). It returns a p-value and a z-score. \n",
+ " If the p-value is less than some significance level, the null hypothesis is rejected and the \n",
+ " alternative hypothesis is accepted. If the z-score is positive, the trend is increasing. If the\n",
+ " z-score is negative, the trend is decreasing. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " Detects increase/decrease trends in forest pixel levels over the user-input geometry and time \n",
+ " range for the ALOS forest map. This workflow combines the alos_forest_extent_download_merge and \n",
+ " ordinal_trend_detection workflows to detect increase/decrease trends in the forest pixel levels \n",
+ " over the user-provided geometry and time range for the ALOS forest map. The ALOS PALSAR 2.1 \n",
+ " Forest/Non-Forest Maps are downloaded in the alos_forest_extent_download_merge workflow. Then \n",
+ " the ordinal_trend_detection workflow clips the ordinal raster to the user-provided geometry and \n",
+ " time range and determines if there is an increasing or decreasing trend in the forest pixel \n",
+ " levels over them. alos_trend_detection uses the Cochran-Armitage test to detect trends in the \n",
+ " forest levels over the years. The null hypothesis is that there is no trend in the pixel levels\n",
+ " over the list of rasters. The alternative hypothesis is that there is a trend in the forest \n",
+ " pixel levels over the list of rasters (one for each year). It returns a p-value and a z-score. \n",
+ " If the p-value is less than some significance level, the null hypothesis is rejected and the \n",
+ " alternative hypothesis is accepted. If the z-score is positive, the trend is increasing. If the\n",
+ " z-score is negative, the trend is decreasing. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- merged_raster (vibe_core.data.rasters.Raster): Merged raster of the ALOS PALSAR 2.1 \n",
+ " Forest/Non-Forest Map for the user-provided geometry and time range. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1mmerged_raster\u001b[0m (\u001b[34mvibe_core.data.rasters.Raster\u001b[0m): Merged raster of the ALOS PALSAR 2.1 \n",
+ " Forest/Non-Forest Map for the user-provided geometry and time range. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- categorical_raster (vibe_core.data.rasters.CategoricalRaster): Categorical raster of the ALOS \n",
+ " PALSAR 2.1 Forest/Non-Forest Map for the user-provided geometry and time range before the merge \n",
+ " operation. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1mcategorical_raster\u001b[0m (\u001b[34mvibe_core.data.rasters.CategoricalRaster\u001b[0m): Categorical raster of the ALOS \n",
+ " PALSAR 2.1 Forest/Non-Forest Map for the user-provided geometry and time range before the merge \n",
+ " operation. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- recoded_raster (vibe_core.data.rasters.Raster): Recoded raster of the ALOS PALSAR 2.1 \n",
+ " Forest/Non-Forest Map for the user-provided geometry and time range. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1mrecoded_raster\u001b[0m (\u001b[34mvibe_core.data.rasters.Raster\u001b[0m): Recoded raster of the ALOS PALSAR 2.1 \n",
+ " Forest/Non-Forest Map for the user-provided geometry and time range. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- clipped_raster (vibe_core.data.rasters.Raster): Clipped ordinal raster for the user-provided \n",
+ " geometry and time range. \n",
+ "
- alos_forest_extent_download_merge: Downloads Advanced Land Observing Satellite (ALOS) \n",
+ " forest/non-forest classification map and merges it into a single raster. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1malos_forest_extent_download_merge\u001b[0m: Downloads Advanced Land Observing Satellite (ALOS) \n",
+ " forest/non-forest classification map and merges it into a single raster. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
- ordinal_trend_detection: Detects increase/decrease trends in the pixel levels over the \n",
+ " user-input geometry and time range. \n",
+ "
\n"
+ ],
+ "text/plain": [
+ " - \u001b[1mordinal_trend_detection\u001b[0m: Detects increase/decrease trends in the pixel levels over the \n",
+ " user-input geometry and time range. \n"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "import sys\n",
+ "\n",
+ "from shapely.geometry import box\n",
+ "from shapely import geometry as shpg\n",
+ "from typing import cast\n",
+ "from datetime import datetime\n",
+ "\n",
+ "import pandas as pd\n",
+ "import numpy as np\n",
+ "\n",
+ "from matplotlib import pyplot as plt\n",
+ "from typing import Optional, List\n",
+ "\n",
+ "from vibe_core.client import get_default_vibe_client\n",
+ "from vibe_core.data import CategoricalRaster, Raster\n",
+ "from vibe_core.data import CategoricalRaster\n",
+ "\n",
+ "sys.path.append(\"../\")\n",
+ "from shared_nb_lib.raster import read_raster\n",
+ "from shared_nb_lib.plot import plot_categorical_maps\n",
+ "\n",
+ "\n",
+ "# Create the FarmVibes.AI default client\n",
+ "client = get_default_vibe_client()\n",
+ "\n",
+ "WORKFLOW_NAME = \"forest_ai/deforestation/alos_trend_detection\"\n",
+ "client.document_workflow(WORKFLOW_NAME)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Workflow discussion\n",
+ "\n",
+ "The workflow tasks involve downloading Advanced Land Observing Satellite (ALOS) forest/non-forest classification maps and merging them into a single raster (`alos_forest_extent_download_merge`). The ALOS products are clipped to the user's geometry (`clip`), and the result is used to calculate the cochran-armitage trend test (`trend_rest_result`). This test checks if there has been a statistically significant trend in the forest distribution over time.\n",
+ "\n",
+ "The `user_input` is composed of a geometry and a time-range, which will be defined in the next cell.\n",
+ "\n",
+ "As output (`sinks`), the workflow provides a list of merged rasters (`merged_raster`) that encompass the user input geometry for each year defined in the time-range, the products are available in the [Planetary Computer dataset](https://planetarycomputer.microsoft.com/dataset/alos-fnf-mosaic).\n",
+ "It also produces a list of categorical rasters (`categorical_raster`) that intersect with the user-provided geometry and time range. The distinction between `categorical_raster` and `merged_raster` is that multiple `categorical_raster` tiles can be combined to form the `merged_raster` if the user's geometry intersects with more than one forest tile. The `recoded_raster` contains the rasters with the recoded values for the forest maps. Finally, `trend_rest_result` determines whether the pixel distribution has changed over time. This is useful for determining if there is statistical evidence of trend in the forest area over time. This change could represent either an increase or decrease in the frequency of forest pixels (i.e., only changes are detected).\n",
+ "\n",
+ "The workflow parameters are the Planetary Computer API key (`pc_key`) and the recode rasters parameters that are used to map the values from the dataset raster to the recoded raster. For example, if the original raster has values `(2, 1, 3, 4, 5)` and assuming the default values of `from_values` and `to_values` are respectively `[1, 2, 3, 4, 5]` and `[6, 7, 8, 9, 10`], the recoded raster will have values `(7, 6, 8, 9, 10)`.\n",
+ "\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "geo_json = {\n",
+ " \"type\": \"Feature\",\n",
+ " \"geometry\": {\n",
+ " \"type\": \"Polygon\",\n",
+ " \"coordinates\": [\n",
+ " [\n",
+ " [-86.783827, 14.565498],\n",
+ " [-86.780459, 14.569303],\n",
+ " [-86.774283, 14.565106],\n",
+ " [-86.779591, 14.557595],\n",
+ " [-86.783827, 14.565498],\n",
+ " ]\n",
+ " ],\n",
+ " },\n",
+ " \"properties\": {},\n",
+ "}\n",
+ "\n",
+ "\n",
+ "geom = shpg.shape(geo_json[\"geometry\"])\n",
+ "time_range = ((datetime(2017, 1, 1), datetime(2020, 12, 31)))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 2. Running the Workflow\n",
+ "\n",
+ "Observe that we pass the geometry and time-range as workflow inputs, along with the following parameters:\n",
+ "\n",
+ "* `pc_key`: This corresponds to the Planetary Computer API key, which is useful for downloading planetary computer imagery.\n",
+ "* `from_values`: Values to recode from, for the ALOS dataset the default value is `[4, 3, 0, 2, 1]`.\n",
+ "* `to_values`: Values to recode to, `[0, 0, 0, 1, 1]` are the default values for the ALOS dataset.\n",
+ "\n",
+ "\n",
+ "For this particular case, we are mapping the forest values from [ALOS dataset](https://planetarycomputer.microsoft.com/dataset/alos-fnf-mosaic) to `1` and `2` depending on the canopy cover level and everything else to `0`.\n",
+ "\n",
+ "| Encoded Value | Description | Recoded Value (Forest-Level) | Recoded Value Semantics |\n",
+ "| ------------- | ----------- | ----------- | --------------|\n",
+ "| 0 | No data | 0 | Non-Forest |\n",
+ "| 1 | Forest (>90% canopy cover) | 2 | Dense-Forest |\n",
+ "| 2 | Forest (10-90% canopy cover) | 1 | Forest |\n",
+ "| 3 | Non-forest | 0 | Non-Forest |\n",
+ "| 4 | Water | 0 | Non-Forest |\n",
+ "\n",
+ "Please check the [workflow documentation page](https://microsoft.github.io/farmvibes-ai/docfiles/markdown/WORKFLOWS.html) to see how parameters are provided. Also, refer to the [SECRETS documentation](https://microsoft.github.io/farmvibes-ai/docfiles/markdown/SECRETS.html) to learn how a secret can be added to the FarmVibes.AI cluster.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n"
+ ],
+ "text/plain": []
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "671c2cedcf4a49b2a183d9014f732178",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "Output()"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n"
+ ],
+ "text/plain": []
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "# Execute the workflow\n",
+ "run = client.run(\n",
+ " WORKFLOW_NAME,\n",
+ " \"Forest Change Detection\",\n",
+ " geometry=geom,\n",
+ " time_range=time_range,\n",
+ " parameters={\n",
+ " \"pc_key\": \"@SECRET(eywa-secrets, pc-sub-key)\",\n",
+ " \"from_values\": [4, 3, 0, 2, 1],\n",
+ " \"to_values\": [0, 0, 0, 1, 2]\n",
+ " },\n",
+ ")\n",
+ "run.monitor()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 3. Interpreting the results\n",
+ "\n",
+ "The `trend_rest_result` is an output from the workflow run, which performs a trend test (Cochan-Armitage). The result of this test includes a `csv` file containing a contingency table, which shows the distribution of pixel counts across the different categories of land cover for each year. This allows us to observe how the distribution of pixel categories has changed over time."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "