Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Interactive dashboard for Sentinel-2 satellite imagery


Overview

In this notebook, we will take a look at how to retrieve Sentinel-2 L2A satellite imagery from the Microsoft Planetary Computer Data Catalog (MSPC). We will go over how to interact with the Data Catalog, which exposes a SpatioTemporal Asset Catalog (STAC) interface for querying, searching and retrieving data. We will use the odc-stac package to load the data lazily, which means data is not actually read unless required (say, for plotting). Once loaded, we will process the data and make a simple interactive dashboard to look at the satellite imagery over a location for different seasons. We will use the HoloViz ecosystem for the interactive dashboard.

Prerequisites

ConceptsImportanceNotes
XarrayHelpfulBackground
Dask + XarrayNecessaryBackground
About the Microsoft Planetary Computer (MSPC)HelpfulBackground
Documentation of pystac-clientHelpfulConsult as needed
Landsat ML CookbookHelpfulSimilar cookbook, illustrates accessing Landsat data from MSPC
About the HoloViz ecosystemHelpfulHow different HoloViz packages work with each other
Sentinel-2 L2A User GuideNecessaryBackground about the satellite data
Sentinel-2 L2A data definitionsHelpfulSection 2.3.10 has some useful information about the data we access in this cookbook
  • Time to learn: 15 minutes

Imports

Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...

Since we will use dask to distribute our computation, we can create a dask cluster and connect to it via a dask client. For this recipe, we will create a LocalCluster with default values.

Loading...

Open the catalog

The root of Microsoft Planetary Computer’s STAC API endpoint is located at https://planetarycomputer.microsoft.com/api/stac/v1. We will load in the catalog using the pystac_client.Client.open method. Even though the STAC metadata in MSPC is publicly accessible, authentication is required to access the actual data. The modifier keyword can be used to explicitly “sign” an item, which basically means we can access the privately stored data (more information here).

Microsoft Planetary Computer STAC API - Searchable spatiotemporal metadata describing Earth science datasets hosted by the Microsoft Planetary Computer

Let’s search for any collections that have the substring “sentinel-2” in it to discover the sentinel-2 data.

[<CollectionClient id=sentinel-2-l2a>]

Looks like there is only one collection (sentinel-2-l2a) available in the catalog - which is the dataset we want to use.

Query, Filter and Load the collection

Now that we have the ID of the collection of interest, we can specify certain filters to narrow down to exactly the data we want to look at. The final visualization in the recipe will look at how the NCAR Mesa Lab, Boulder CO looks like throughout the year as seen from space. To narrow down our search, we will use the following criteria -

  • Bounding box: We will limit our spatial extent to the bounding box of the NCAR Mesa Lab region.

  • Date range: We will look at the year 2022

  • Collection: sentinel-2-l2a (from previous cell)

  • Cloud threshold: Since cloud can block the satellite from making an observation of the ground, we will limit our search to satellite images where the cloud cover is less than a certain threshold, 40% in this case.

Feel free to change these filtering paramters to suit your needs when running in an interactive session.

Found 100 items in the sentinel-2-l2a

We now have an ItemCollection with the data that we need. Let’s look at one of the items in the collection and explore what assets it has.

Assets available:
AOT, B01, B02, B03, B04, B05, B06, B07, B08, B09, B11, B12, B8A, SCL, WVP, visual, preview, safe-manifest, granule-metadata, inspire-metadata, product-metadata, datastrip-metadata, tilejson, rendered_preview

Seems like there are a lot of assets associated with the item - you can read about them here. We are interested in the assets that start with a ‘B’, which are the bands associated with the different spectral bands in which the MultiSpectral Instrument (MSI) of the satellite captures observations. Specifically, the RGB – or Red, Green and Blue - bands that we need to create a “True color” image are as follows:

bandcorresponds to
B04Red
B03Green
B02Blue

We will use the odc.stac.stac_load function to load in the assets that start with the alphabet ‘B’. This function will return a lazily-loaded xr.DataSet (using dask). For plotting purposes it is better if we have the data as a xr.DataArray instead with the bands as a dimension. We can do that using .to_array(dim=<dim_name>) method of a dataset.

Loading...

Prepare the data for visualization

On January 25th, 2022, the European Space Agency (data provider for Sentinel-2 satellite) made a change in their processing pipeline to address some issues that you can read about here if interested. For the purpose of this notebook, we will process the newer dataset such that it is harmonized with the old processing pipeline - in simple words, we will make sure that the data has the same statistical properties so that we can visualize them seamlessly.

Loading...

Now that we have a harmonized dataset, we still need to process the data as follows:

  • Sentinel-2 L2A provides the Surface Reflectance (SR) data, which usually ranges from 0 (no reflection) to 1.0 (complete reflection). However, the actual values in the loaded dataset ranges from 0 to ~10,000. These data values need to be scaled to 0.0-1.0 by dividing the data by 10,000. More details can be found in section 2.3.10 of this document.

We will then explicitly trigger the dask computation using the compute() method and load the result into memory. This is to reduce repeated calls to retrieve data from MSPC. By loading the processed This wouldn’t have been possible if the dataset was large.

/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)
/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)
/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)
/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)
/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)
/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)
/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)
/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)
/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)
/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)
/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)
/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)
/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)
/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)
/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)
/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)
/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)
/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)
/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)
/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)
/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)
/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)
/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)
/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)
/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)
/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)
/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)
/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)
/home/runner/micromamba/envs/is2-cookbook-dev/lib/python3.14/site-packages/dask/_task_spec.py:768: RuntimeWarning: invalid value encountered in divide
  return self.func(*new_argspec)

We have now processed the data so that we can visualize it!

Let’s look at the Blue, Green and Red bands.

Let us make a dashboard composed of 4 different interactive plots showing the RGB view of the satellite observation for four different seasons. We need a function that will take a time input and does the following tasks:

  1. plot an interactive RGB image of the data and overlay it on a map of the world.

  2. provide a date slider widget which can be used to interact with the plot.

  3. only set the default value of the date slider to the time, but allow the user to slide through the length of the entire dataset.

Using this function, we will be able to compose the dashboard.

Loading...

Let’s now compose a dashboard using panel.

Loading...

Summary

In this recipe, we looked at how to access Sentinel-2 satellite data over a region of interest and create an interactive dashboard to visualize the data.

What’s next?

We plotted the RGB or True color image of our region of interest using a subset of all the bands available. We can further calculate useful indices, such as the Normalized Difference Snow Index (NDSI) or the Normalized Difference Vegetation Index.

Resources and references