Lesson 3: Sentinel Data

Open slides

Important
  • Try this without AI first — struggle builds understanding.
  • Ask if you are stuck. Asking builds understanding too!

Task 1

Download a Sentinel-2 L2A scene of your choice from the Copernicus Browser (follow the steps from the lesson slides). Extract the .zip archive and note the path to the resulting .SAFE folder.

Import the Blue (B02), Green (B03), Red (B04), and NIR (B08) bands from the IMG_DATA/R10m/ subfolder (see Spectral Bands (Wikipedia)). Convert digital numbers to surface reflectance using scale and offset manually.

After converting, plot a histogram of one band with hist() and inspect minmax(s2). What do you notice about the value range, and why does this happen? Clip all values to [0, 1] using app() and store the result back as s2.

Task 2

Visualize the Sentinel-2 image as a True Color composite (Red = B04, Green = B03, Blue = B02) and a False Color composite (Red = B08/NIR, Green = B04, Blue = B03). Use plotRGB() with stretch = "histogram".

Which land cover types appear bright red in the False Color image, and why?

Task 3

The SCL band is not in R10m/ — find it in the IMG_DATA/R20m/ folder of your SAFE archive using list.files() with pattern = "SCL". Import it, resample to match s2, and apply a cloud mask keeping only pixels classified as vegetation (4), bare soils (5), water (6), or snow/ice (11).

Resample the SCL to match the resolution of s2 before masking (use method = "near"). Store the result as s2_masked and report what percentage of pixels are cloud-free.

Task 4

Compute NDVI from both s2 (unmasked) and s2_masked (cloud-masked):

\[\text{NDVI} = \frac{\text{B08} - \text{B04}}{\text{B08} + \text{B04}}\]

Compare the global mean NDVI of both images using global(..., mean, na.rm = TRUE). Why does the masked image have a higher mean NDVI?

Task 5

Write the cloud-masked NDVI to disk in two ways:

  1. As Float32 (full precision, no transformation)
  2. As Byte (0–255) using scoff_params() to compute scale and offset, with DEFLATE compression

Compare the file sizes using file.size(). Then read the Byte file back and compute the maximum absolute difference from the original NDVI using global(abs(...), max, na.rm = TRUE). How large is the precision loss?