Spectral Operations

This page provides an overview of spectral operations available in openEO for manipulating and analysing geospatial data cubes. Users can use these operations to focus on specific bands, calculate indices like NDVI, and create new derived bands.

The buttons above let you filter processes supported by different backends. Selecting or deselecting a backend will show or hide the relevant sections in the documentation. However, please note that it is based on the latest documentation rendering. Thus, please refer to the openEO Hub for the most up-to-date information.

Select only the required bands

A collection can contain multiple spectral bands, but often only a subset is relevant for a specific analysis. Selecting only the required bands helps reduce data volume and simplifies subsequent processing steps. User can choose bands based on their analysis needs, such as focusing on red and near-infrared bands for vegetation indices by simply filtering using filter_bands process.

filter_bands reduces a cube to named bands or to a wavelength range. Selecting bands early makes a process graph clearer and can substantially reduce processing volume.

import openeo

connection = openeo.connect("openeofed.dataspace.copernicus.eu").authenticate_oidc()
sentinel2 = connection.load_collection(
    "SENTINEL2_L2A",
    spatial_extent={"west": 4.30, "east": 4.55, "south": 50.80, "north": 50.98},
    temporal_extent=["2024-06-01", "2024-06-30"],
)
red_nir = sentinel2.filter_bands(["B04", "B08"])
library(openeo)

connection <- openeo::connect("openeofed.dataspace.copernicus.eu") %>% openeo::authenticate_oidc()
sentinel2 <- connection$load_collection(
    "SENTINEL2_L2A",
    spatial_extent=list(west=4.30, east=4.55, south=50.80, north=50.98),
    temporal_extent=c("2024-06-01", "2024-06-30")
)
red_nir <- sentinel2$filter_bands(c("B04", "B08"))
import OpenEO from "openeo";

const connection = await OpenEO.connect("openeofed.dataspace.copernicus.eu").authenticate_oidc();
const sentinel2 = connection.load_collection(
    "SENTINEL2_L2A",
    {
        spatial_extent: {west: 4.30, east: 4.55, south: 50.80, north: 50.98},
        temporal_extent: ["2024-06-01", "2024-06-30"]
    }
);
const red_nir = sentinel2.filter_bands(["B04", "B08"]);

Please note that the band names used in the examples above are specific to the Sentinel-2 collection in CDSE backend and may differ for other collections or backends. Check the collection metadata for the correct band names.

Alternatively, user can specify the band names directly when using load_collection by providing the bands parameter. The filter_bands step can be handy when you have multiple bands generated in your analysis resulting a different datacube and want to focus on a subset for subsequent processing.

Calculate NDVI

One of the most commonly used vegetation indices in the EO domain is the Normalized Difference Vegetation Index (NDVI). The NDVI is a metric for quantifying the health and density of vegetation using sensor data. Thus, to make it easier for the openEO users, it is defined within the API as a dedicated process.The ndvi process computes and adds an NDVI band while retaining the cube’s spatial and temporal dimensions.

ndvi = red_nir.ndvi(nir="B08", red="B04", target_band="NDVI")
ndvi.download("ndvi.tif", format="GTiff")
ndvi <- red_nir$ndvi(nir="B08", red="B04", target_band="NDVI")
ndvi$download("ndvi.tif", format="GTiff")
const ndvi = red_nir.ndvi({nir: "B08", red: "B04", target_band: "NDVI"});
await ndvi.download("ndvi.tif", {format: "GTiff"});

For indices without a dedicated process, use normalized_difference or an apply_dimension callback with arithmetic processes. For example, normalized difference is suitable for NDWI and NBR when you supply the appropriate pair of input bands.

Spectral and temporal operations are often chained together. For example, one might want to analyse the vegetation health over time using NDVI. In such cases, the NDVI is first calculated for each acquisition and then aggregated over time using aggregate_temporal_period to observe trends and patterns. For more details, see the Temporal Operations page.

Calculate a normalized difference

The normalized_difference process defined in the openEO API calculates the normalized difference between two specified bands. It is useful for creating indices like NDWI and NBR when no dedicated process exists. For example, it can create NDWI from green and near-infrared bands or NBR from near-infrared and shortwave-infrared bands.

ndwi = cube.normalized_difference(first_band="B03", second_band="B08")
ndwi <- cube$normalized_difference(first_band="B03", second_band="B08")
const ndwi = cube.normalized_difference({first_band: "B03", second_band: "B08"});

Apply a process across bands

The apply_dimension process can be used to apply a process(custom functions also known as UDF in openEO) across a specified dimension(such as “time” or “bands”) to produce a new series of values for that dimension. In this page, we showcase how to apply a process across the band dimension.

To apply a custom operation across the band dimension, use apply_dimension with dimension="bands". This will return a new series of values for the band dimension based on the specified process.

derived = cube.apply_dimension(
  dimension="bands",
  process=lambda bands: bands[1] / bands[0],
)
derived <- cube$apply_dimension(
  dimension="bands",
  process=function(bands) { bands[2] / bands[1] }
)
const derived = cube.apply_dimension({
  dimension: "bands",
  process: bands => bands[1] / bands[0]
});

Reduce the band dimension

Similar to apply_dimension, the reduce_dimension process operates on either the time or the band dimension, but instead of returning a new series of values, it collapses the dimension into a single value using a reducer function.

To reduce the band dimension, specify the dimension as "bands" and provide a reducer function, such as "mean", "max" or "median". It will result in a single value, for example the mean brightness or maximum response across all bands of the cube with time and spatial dimensions preserved.

brightness = cube.reduce_dimension(dimension="bands", reducer="mean")
brightness <- cube$reduce_dimension(dimension="bands", reducer="mean")
const brightness = cube.reduce_dimension({dimension: "bands", reducer: "mean"});

Scale spectral values

Before applying any analysis or exporting the data, it is often necessary to convert the raw digital numbers to a physical range. To scale spectral values from their original digital number range to a physical range, a process like linear_scale_range can be used. However, always confirm the source scaling in the collection metadata first before applying linear_scale_range.

reflectance <- cube$linear_scale_range(
  input_min=0, input_max=10000, output_min=0, output_max=1
)
const reflectance = cube.linear_scale_range({
  input_min: 0, input_max: 10000, output_min: 0, output_max: 1
});

Select an array element

The process array_element can be useful to extract a specific element from an array. It returns the element with the specified index or label from the array. Please note that either the parameter index or label must be specified, otherwise the ArrayElementParameterMissing exception is thrown. If both parameters are set the ArrayElementParameterConflict exception is thrown.

red = openeo.processes.array_element(data=band_array, index=0)
red <- openeo::array_element(data = band_array, index = 0)
const red = openeo.processes.array_element({data: band_array, index: 0});

Apply a process to an array

The process array_apply is used to apply a process to each individual value in an array, for example scaling each value before converting the array back into a cube dimension. This is basically what other languages call either a for each loop or a map function.

scaled = openeo.processes.array_apply(
  data=band_array,
  process=lambda value: value.multiply(0.0001),
)
scaled <- openeo::array_apply(
  data = band_array,
  process = function(value) value * 0.0001
)
const scaled = openeo.processes.array_apply({
  data: band_array,
  process: value => value * 0.0001
});
Back to top