Band math

We have seen how the ol/source/GeoTIFF source can be used to render true and false color composites. We did this by rendering scaled reflectance values directly into one of the red, green, or blue display channels. It is also possible to run calculations on reflectance values from a GeoTIFF (or other data tile source) and map the output values to RGBA color values.

The ol/layer/WebGLTile layer accepts a style property that can be used to control source rendering. In this example, we'll use math expressions to calculate the Normalized Difference Vegetation Index (NDVI) from the same Sentinel-2 sources used in the previous examples.

The NDVI is the ratio of the difference between near-infrared (NIR) and red to the sum of near-infrared and red reflectance values.

NDVI = (NIR - RED) / (NIR + RED)

This normalized difference provides an index of green vegetation density or health. In dividing the difference in NIR and red reflectance by the sum, the index is normalized against variations in brightness or illumination. The idea is that vegetated sunny slopes should have a similar index as vegetated shady slopes.

To render NDVI values, we need to configure our source to use the near-infrared (B08) and red (B04) bands. Update your main.js script so the source looks like this:

const source = new GeoTIFF({
  sources: [
    {
      // red reflectance
      url: 'https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/21/H/UB/2021/9/S2B_21HUB_20210915_0_L2A/B04.tif',
      max: 10000,
    },
    {
      // near-infrared reflectance
      url: 'https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/21/H/UB/2021/9/S2B_21HUB_20210915_0_L2A/B08.tif',
      max: 10000,
    },
  ],
});

Next, we'll create the expression for calculating NDVI from the source's input bands. Add the following variables to your main.js:

// near-infrared is the second band from above
const nir = ['band', 2];

// near-infrared is the first band from above
const red = ['band', 1];

const difference = ['-', nir, red];
const sum = ['+', nir, red];

const ndvi = ['/', difference, sum];

The expressions above use an array-based syntax of this form: [operator, ...arguments]. The band operator accesses bands from the list of sources for the ol/source/GeoTIFF – where the first configured band is 1, the second is 2, etc. The -, +, and / operators calculate the difference, sum, and ratio of their input arguments. The ndvi expression will result in a value between -1 (not very vegetated) and 1 (very vegetated). The next step is to map these values to colors.

The ol/layer/WebGLTile layer's style takes a color property that determines the final output color for each pixel. We want to interpolate colors between the unvegetated (brownish) and vegetated (green) NDVI values. To do this, we use an interpolate operator, applying linear interpolation between a number of stop values.

Edit your main.js so the layer definition looks like this:

const layer = new TileLayer({
  source: source,
  style: {
    color: [
      'interpolate',
      ['linear'],
      ndvi,
      -0.2, // ndvi values <= -0.2 will get the color below
      [191, 191, 191],
      0, // ndvi values between -0.2 and 0 will get an interpolated color between the one above and the one below
      [255, 255, 224],
      0.2,
      [145, 191, 82],
      0.4,
      [79, 138, 46],
      0.6,
      [15, 84, 10],
    ],
  },
});

If everything worked out, you should see your NDVI visualization at http://localhost:5173/.

NDVI generated from a Sentinel-2 GeoTIFF
NDVI generated from a Sentinel-2 GeoTIFF

Choosing these stop values and colors is hard work. Next up we will pull in a helper library to do this for us.

results matching ""

    No results matching ""