diff --git a/assets/guides/sentinel2/sandwich-harbour-true-color.webp b/assets/guides/sentinel2/sandwich-harbour-true-color.webp new file mode 100644 index 0000000..b7ae626 Binary files /dev/null and b/assets/guides/sentinel2/sandwich-harbour-true-color.webp differ diff --git a/changelog.mdx b/changelog.mdx index 80f8c04..6cf00d2 100644 --- a/changelog.mdx +++ b/changelog.mdx @@ -39,8 +39,8 @@ mode: center This makes it practical to move from catalog search to image processing in one workflow: find a low-cloud scene, select a band, crop it to a geographic region, and pass the resulting data into your analysis without first downloading an entire scene. - - Query Sentinel-2 scenes, resolve their assets, and read or download image data. + + Query a Sentinel-2 scene, read RGB band windows, and display a true-color image. diff --git a/datasets/assets-and-storage/read-and-download.mdx b/datasets/assets-and-storage/read-and-download.mdx index 21df02a..8e57781 100644 --- a/datasets/assets-and-storage/read-and-download.mdx +++ b/datasets/assets-and-storage/read-and-download.mdx @@ -148,8 +148,8 @@ Asset locations can reference authentication metadata. The storage client curren ## Next steps - - Query Sentinel-2 and read a selected image region. + + Query Sentinel-2, read RGB band windows, and display a true-color image. Attach file references to datapoints you ingest. diff --git a/guides/cookbook.mdx b/guides/cookbook.mdx index 31155ec..37fd55b 100644 --- a/guides/cookbook.mdx +++ b/guides/cookbook.mdx @@ -21,13 +21,13 @@ export const cookbookSections = [ tags: ["Open data", "Sentinel-2", "Metadata queries", "Spatial filters"], }, { - title: "Access Sentinel-2 assets", + title: "Visualize Sentinel-2 imagery", href: "/guides/datasets/access-sentinel2-data", - description: "Read a COG window or download a Sentinel-2 image with the storage client.", + description: "Query a Sentinel-2 scene, read its RGB bands, and display a true-color image.", icon: "magnifying-glass-location", level: "Beginner", time: "10 min", - tags: ["Assets", "COG", "Sentinel-2", "Storage client"], + tags: ["Assets", "COG", "Sentinel-2", "True color"], }, { title: "Build a spatio-temporal catalog", diff --git a/guides/datasets/access-sentinel2-data.mdx b/guides/datasets/access-sentinel2-data.mdx index 08bbd2b..c9529e1 100644 --- a/guides/datasets/access-sentinel2-data.mdx +++ b/guides/datasets/access-sentinel2-data.mdx @@ -1,10 +1,10 @@ --- -title: Access Sentinel-2 assets -description: Query Sentinel-2 metadata and read or download the corresponding image assets. +title: Visualize Sentinel-2 imagery +description: Query a Sentinel-2 scene, read its RGB bands, and display a true-color image. icon: satellite --- -Tilebox indexes Sentinel-2 metadata and asset locations in the `open_data.aws_earth.sentinel2` dataset. Query the metadata first, then use the storage client to read only the image data you need. +Tilebox indexes Sentinel-2 metadata and asset locations in the `open_data.aws_earth.sentinel2` dataset. In this guide, you query a low-cloud scene over Sandwich Harbour in Namibia, read its RGB bands, and display a true-color image. Asset collections and the storage client are currently available in the Python SDK. @@ -16,86 +16,102 @@ Tilebox indexes Sentinel-2 metadata and asset locations in the `open_data.aws_ea - You have installed the [Python SDK](/sdks/python/install) with Python 3.11 or newer. ```bash -uv add tilebox shapely +uv add tilebox shapely numpy matplotlib ``` ## Select a Sentinel-2 datapoint -Query a small time and area of interest, then select one low-cloud observation: +Define a small area around the Sandwich Harbour lagoon and dune coast. Query Level-2A observations from a known clear period, sort them by scene-level cloud cover, and select the clearest result: ```python Python from shapely import box from tilebox.datasets import Client, field +area_bounds = (14.42, -23.43, 14.58, -23.25) # west, south, east, north +area = box(*area_bounds) + datasets = Client() collection = datasets.dataset("open_data.aws_earth.sentinel2").collection("L2A") scenes = collection.query( - temporal_extent=("2025-10-01", "2025-11-01"), - spatial_extent=box(-106.0, 38.0, -105.9, 38.1), - filter=field("cloud_cover") < 10, + temporal_extent=("2024-06-17", "2024-06-18"), + spatial_extent=area, + filter=field("cloud_cover") < 1, ) -datapoint = scenes.isel(time=0) -print(datapoint.stac_id.item()) +datapoint = scenes.sortby("cloud_cover").isel(time=0) +print(datapoint.stac_id.item(), datapoint.cloud_cover.item()) ``` +The selected observation is `S2B_T33KVQ_20240617T090616_L2A`, acquired on June 17, 2024, with `0.008524` percent scene-level cloud cover. The cloud-cover value describes the complete Sentinel-2 tile, not only the area of interest. + See [Query open data metadata](/guides/datasets/query-satellite-data) for more query patterns. -## Resolve the assets +## Resolve the RGB assets -Turn the selected datapoint into an asset collection. Each asset describes one file and the locations from which it can be accessed. +Turn the selected datapoint into an asset collection, then resolve its 10-meter red, green, and blue COGs: ```python Python from tilebox.datasets.assets import AssetCollection assets = AssetCollection.from_datapoint(datapoint) - -for key, asset in assets.items(): - print(key, asset.media_type) - red = assets["red"] +green = assets["green"] +blue = assets["blue"] ``` -## Read a Cloud Optimized GeoTIFF window +## Read the COG windows -The Sentinel-2 image assets are Cloud Optimized GeoTIFFs (COGs). Open an image remotely and request a pixel window without downloading the complete file: +Use the storage client to read the area of interest from each COG: ```python Python import asyncio -from tilebox.storage.aio import Client +import numpy as np +from tilebox.storage.aio import Client as StorageClient from tilebox.storage.geotiff import window_from_bounds -async def read_area(): - storage = Client() - geotiff = await storage.open_geotiff(red) - window = window_from_bounds( - geotiff, - (-106.0, 38.0, -105.9, 38.1), - crs="EPSG:4326", - ) - return await geotiff.read(window=window) - -pixels = asyncio.run(read_area()) -print(pixels.shape) +async def read_rgb(): + storage = StorageClient() + bands = [] + + for asset in (red, green, blue): + geotiff = await storage.open_geotiff(asset) + window = window_from_bounds(geotiff, area_bounds, crs="EPSG:4326") + raster = await geotiff.read(window=window) + bands.append(raster.data[0]) + + return np.stack(bands, axis=-1).astype(np.float32) + +rgb = asyncio.run(read_rgb()) ``` -`window_from_bounds` transforms geographic bounds into the image coordinate system and clips the resulting window to the image. +`window_from_bounds` converts the longitude and latitude bounds to the COG's pixel grid. Each read returns a `(2000, 1643)` array of `uint16` values; stacking the bands creates a `(2000, 1643, 3)` RGB array. -## Download an asset +## Display the true-color image -Use `download` when you need the complete file locally: +Apply one contrast stretch across all three channels to preserve their relative color balance, then display the image: ```python Python -async def download_red_band(): - storage = Client() - return await storage.download(red, "data/sentinel-2-red.tif") +import matplotlib.pyplot as plt + +low, high = np.percentile(rgb, (2, 98)) +display_rgb = np.clip((rgb - low) / (high - low), 0, 1) +display_rgb = display_rgb ** (1 / 1.1) -path = asyncio.run(download_red_band()) -print(path) +fig, ax = plt.subplots(figsize=(8, 10)) +ax.imshow(display_rgb) +ax.set_axis_off() +plt.tight_layout(pad=0) +plt.show() ``` +The percentile stretch and gamma correction bring out the coastline, lagoon, and dune textures in a beautiful true-color visualization. + + + Sentinel-2 true-color view of the Atlantic Ocean, Sandwich Harbour lagoon, and orange dunes of the Namib Sand Sea + + ## Next steps diff --git a/guides/datasets/query-satellite-data.mdx b/guides/datasets/query-satellite-data.mdx index 584af04..1926443 100644 --- a/guides/datasets/query-satellite-data.mdx +++ b/guides/datasets/query-satellite-data.mdx @@ -60,8 +60,8 @@ print(latest.cloud_cover.item()) ## Next steps - - Read a COG window or download an image from a selected datapoint. + + Read RGB band windows and display a true-color image. Learn more dataset query patterns.