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.

Searching, subsetting and visualising imagery before it enters your project workflow

1. Why this page matters

In the previous page, you defined search criteria for suitable spatial data. Here, you apply that logic to Remote Sensing data.

Remote sensing datasets can be very useful for SDS320 projects, but they come with trade-offs. Spatial Resolution determines which objects are visible. Temporal coverage determines whether you can observe the period of interest. Cloud cover affects usability. Spectral Bands determine which surface properties you can analyse.

This page focuses on search, inspection, small subset downloads and quick visual checks. Full Preprocessing comes later.

By the end of this page, you should be able to answer:

Which imagery source fits my project, and can I retrieve a useful subset without downloading too much data?

2. Core idea

Modern imagery access often follows a search-first workflow.

Instead of downloading a full scene immediately, you first search metadata, inspect candidate scenes, choose useful assets, and then retrieve only the area and bands you need.

A common workflow is:

Choose source
→ search catalog
→ inspect collection
→ search items
→ inspect assets
→ load or download a small subset
→ visualise and check
→ document the decision

This is especially useful for cloud-hosted Cloud Optimized GeoTIFFs, where Python can read only a spatial window from a large remote file.


3. STAC in one minute

SpatioTemporal Asset Catalog (STAC) is a standard way to describe and search geospatial assets.

A typical STAC structure is:

Catalog
→ Collection
→ Item
→ Asset
STAC levelMeaning
CatalogEntry point to a data archive or API.
CollectionDataset group, such as Sentinel-2, Landsat or NAIP.
ItemOne scene, tile or observation with footprint and date.
AssetOne file connected to an item, such as a band, preview or metadata file.

In practice, you connect to a catalog, choose a collection, search for items by area and time, inspect the assets, and then load or download the files that matter for your project.


4. Explore the Planetary Computer

The Microsoft Planetary Computer hosts many Earth observation datasets and provides a STAC API.

Useful entry points:

Start with the online catalogue before writing code. It helps you understand which collections exist, what they contain and which collection IDs you need.


5. Choose an imagery source

Common open or partly open sources include:

SourceTypical useMain limitation
Sentinel-2Multispectral land monitoring, vegetation, land cover and change detection.Clouds and 10–20 m bands may limit small-object mapping.
LandsatLong-term change analysis over decades.Coarser multispectral resolution than Sentinel-2.
NAIPHigh-resolution aerial imagery for the United States.Coverage is limited to the United States.
SWISSIMAGEVery high-resolution imagery for Switzerland.Very large data volume at 10 cm resolution.
Vantor Open DataEvent-focused high-resolution imagery for selected disasters.Coverage is limited to released events and licensing must be checked.

Choose based on your project need, not only on popularity.


6. Shared helper code for spatial subsets

The following helper functions demonstrate how to retrieve a spatial subset of large remote imagery files. They search STAC items, read only a spatial window of interest, write a local GeoTIFF, and display a quick visual preview.

You can configure the output dimensions to suit your needs—smaller subsets load faster and use less disk space, while larger subsets preserve more detail. These functions use spatial windowing to avoid downloading the entire scene. For final analysis, always verify the native resolution and document any resampling used.

from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np
import rasterio
from pyproj import Transformer
from rasterio.windows import from_bounds
from rasterio.windows import transform as window_transform


def bbox_to_dataset_crs(bbox_wgs84, dst_crs):
    """Transform a WGS84 bounding box to a raster's native CRS.

    Rasterio windows must be given in the source file's own CRS, not WGS84.

    Args:
        bbox_wgs84: Bounding box in WGS84 [min_lon, min_lat, max_lon, max_lat].
        dst_crs: Target CRS (e.g., src.crs from rasterio.open()).

    Returns:
        Bounding box in the target CRS [min_x, min_y, max_x, max_y].
    """
    transformer = Transformer.from_crs("EPSG:4326", dst_crs, always_xy=True)
    min_lon, min_lat, max_lon, max_lat = bbox_wgs84
    xs, ys = transformer.transform([min_lon, max_lon], [min_lat, max_lat])
    return min(xs), min(ys), max(xs), max(ys)


def ycbcr_to_rgb(stack):
    """Convert a 3-band YCbCr image (ITU-R BT.601) to RGB uint8.

    Some imagery (e.g. JPEG-compressed GeoTIFFs, as used by SWISSIMAGE) stores
    colour as YCbCr rather than RGB, and must be converted before it can be
    written or plotted as a normal RGB image.

    Args:
        stack: Array of shape (3, height, width) with YCbCr bands.

    Returns:
        RGB array of shape (3, height, width) as uint8.
    """
    y, cb, cr = stack.astype("float32")
    r = y + 1.402 * (cr - 128.0)
    g = y - 0.344136 * (cb - 128.0) - 0.714136 * (cr - 128.0)
    b = y + 1.772 * (cb - 128.0)
    return np.clip(np.stack([r, g, b]), 0, 255).astype("uint8")


def save_single_asset_subset(
    item, asset_key, bbox_wgs84, output_path, band_indexes=(1, 2, 3)
):
    """Read the AOI window from one raster asset and save it as a GeoTIFF.

    Output pixel dimensions follow the AOI at the source's native resolution,
    so a landscape bbox produces a landscape image (no separate width/height
    arguments needed).

    Args:
        item: A STAC Item object.
        asset_key: String key of the asset to read (e.g., 'B02', 'red', 'image').
        bbox_wgs84: Bounding box in WGS84 [min_lon, min_lat, max_lon, max_lat].
        output_path: File path for the output GeoTIFF.
        band_indexes: Tuple of band indices to read (1-indexed; default (1,2,3) for RGB).

    Returns:
        Path to the written GeoTIFF file.
    """
    href = item.assets[asset_key].href

    with rasterio.open(href) as src:
        print("Source CRS:", src.crs)
        bounds = bbox_to_dataset_crs(bbox_wgs84, src.crs)
        window = from_bounds(*bounds, transform=src.transform)

        stack = src.read(
            list(band_indexes), window=window, boundless=True, fill_value=0
        )
        print("Read shape, dtype:", stack.shape, stack.dtype)

        profile = src.profile.copy()
        profile.update(
            driver="GTiff",
            height=stack.shape[1],
            width=stack.shape[2],
            count=len(band_indexes),
            transform=window_transform(window, src.transform),
            compress="deflate",
        )
        profile.pop("photometric", None)

        if (
            str(src.profile.get("photometric", "")).lower() == "ycbcr"
            and stack.shape[0] == 3
        ):
            print("Converting YCbCr -> RGB")
            stack = ycbcr_to_rgb(stack)
        profile.update(dtype=str(stack.dtype))

    output_path = Path(output_path)
    output_path.parent.mkdir(parents=True, exist_ok=True)
    with rasterio.open(output_path, "w", **profile) as dst:
        dst.write(stack)

    size_mb = output_path.stat().st_size / 1_000_000
    print(f"Saved: {output_path} ({size_mb:.2f} MB)")
    return output_path


def save_multiband_subset(item, asset_keys, bbox_wgs84, output_path):
    """Read the AOI window from several single-band assets and save as one GeoTIFF.

    Each asset key is read separately (one band each) and stacked in the given
    order, e.g. asset_keys=['B04', 'B03', 'B02'] for a red-green-blue stack.
    Output pixel dimensions follow the AOI at the source's native resolution.

    Args:
        item: A STAC Item object.
        asset_keys: Ordered list of asset keys, one per output band.
        bbox_wgs84: Bounding box in WGS84 [min_lon, min_lat, max_lon, max_lat].
        output_path: File path for the output GeoTIFF.

    Returns:
        Path to the written GeoTIFF file.
    """
    bands = []
    profile = None

    for key in asset_keys:
        with rasterio.open(item.assets[key].href) as src:
            bounds = bbox_to_dataset_crs(bbox_wgs84, src.crs)
            window = from_bounds(*bounds, transform=src.transform)
            band = src.read(1, window=window, boundless=True, fill_value=0)
            bands.append(band)

            if profile is None:
                profile = src.profile.copy()
                profile.update(
                    driver="GTiff",
                    height=band.shape[0],
                    width=band.shape[1],
                    count=len(asset_keys),
                    transform=window_transform(window, src.transform),
                    compress="deflate",
                )
                profile.pop("photometric", None)

    stack = np.stack(bands)
    profile.update(dtype=str(stack.dtype))

    output_path = Path(output_path)
    output_path.parent.mkdir(parents=True, exist_ok=True)
    with rasterio.open(output_path, "w", **profile) as dst:
        dst.write(stack)

    size_mb = output_path.stat().st_size / 1_000_000
    print(f"Saved: {output_path} ({size_mb:.2f} MB)")
    return output_path


def stretch_rgb(rgb):
    """Apply a percentile stretch to an RGB image for better visual contrast.

    Raw satellite data often spans a wide dynamic range. A 2-98 percentile stretch clips
    extreme values and maps the middle 96% to the display range, improving visibility of
    detail without losing important information to saturation.

    Args:
        rgb: Array of shape (3, height, width) or (height, width, 3).

    Returns:
        Stretched array normalized to [0, 1].
    """
    rgb = np.moveaxis(rgb, 0, -1).astype("float32")
    p_low, p_high = np.nanpercentile(rgb, (2, 98))
    if p_high == p_low:
        return np.zeros_like(rgb)
    rgb = (rgb - p_low) / (p_high - p_low)
    return np.clip(rgb, 0, 1)


def plot_rgb(path, title):
    """Load a GeoTIFF and display it as an RGB image with percentile stretch.

    Args:
        path: File path to a 3-band GeoTIFF.
        title: Title for the plot.
    """
    with rasterio.open(path) as src:
        rgb = src.read([1, 2, 3])
    plt.figure(figsize=(7, 3))
    plt.imshow(stretch_rgb(rgb))
    plt.title(title)
    plt.axis("off")
    plt.show()

7. Practical subset workflows

The following examples all follow the same pathway:

define a small bounding box
→ search a catalogue
→ inspect the selected item
→ choose assets
→ save or visualise a subset
→ check whether the result is useful

Use them as patterns for your own project. The exact locations are examples, but the decisions are transferable.


Sentinel-2 data

Sentinel-2 is a useful first example because it is global, multispectral and available through the Planetary Computer.

Here, the subset focuses on Rome. At 10 m resolution, a subset of approximately 1000 × 400 pixels covers several kilometres and shows the city pattern clearly.

from pystac_client import Client
import planetary_computer as pc

pc_catalog = Client.open("https://planetarycomputer.microsoft.com/api/stac/v1")

rome_bbox = [12.42, 41.88, 12.52, 41.91]  # WGS84 lon/lat around Rome

search = pc_catalog.search(
    collections=["sentinel-2-l2a"],
    bbox=rome_bbox,
    datetime="2025-06-01/2025-08-31",
    query={"eo:cloud_cover": {"lt": 20}},
    max_items=10,
)

items = list(search.items())
print(f"Found {len(items)} Sentinel-2 item(s)")

Select the least-cloudy item and inspect its assets.

items_sorted = sorted(
    items,
    key=lambda item: item.properties.get("eo:cloud_cover", 999),
)

s2_item = pc.sign(items_sorted[0])

print("Selected item:", s2_item.id)
print("Date:", s2_item.datetime)
print("Cloud cover:", round(s2_item.properties.get("eo:cloud_cover"), 2))

print("\nUseful assets:")
for key in ["B02", "B03", "B04", "B08", "SCL", "visual"]:
    if key in s2_item.assets:
        print(key, "-", s2_item.assets[key].title)

Download only a small RGB subset.

s2_subset = save_multiband_subset(
    item=s2_item,
    asset_keys=["B04", "B03", "B02"],  # red, green, blue
    bbox_wgs84=rome_bbox,
    output_path="data/raw/sentinel2/s2_rome_rgb_subset.tif",
)

plot_rgb(s2_subset, "Sentinel-2 subset: Rome")

Project check: if your project targets small objects such as individual trees, cars or buildings, Sentinel-2 may be too coarse. If your project targets broader land-cover patterns, vegetation, water or change, it may be a good candidate.


Landsat

Landsat is useful when the time dimension matters. It has a long archive and global coverage, but coarser resolution than Sentinel-2.

Here, the subset focuses on the Mount Everest region in the Himalaya. The preview is useful for understanding terrain and glacier context, but 30 m pixels are not suitable for very small objects.

everest_bbox = [86.80, 27.90, 87.01, 27.98]  # WGS84 lon/lat around Mount Everest

search = pc_catalog.search(
    collections=["landsat-c2-l2"],
    bbox=everest_bbox,
    datetime="2026-07-01/2026-09-30",
    query={"eo:cloud_cover": {"lt": 30}},
    max_items=10,
)

items = list(search.items())
print(f"Found {len(items)} Landsat item(s)")
items_sorted = sorted(
    items,
    key=lambda item: item.properties.get("eo:cloud_cover", 999),
)

landsat_item = pc.sign(items_sorted[0])

print("Selected item:", landsat_item.id)
print("Date:", landsat_item.datetime)
print("Cloud cover:", landsat_item.properties.get("eo:cloud_cover"))

print("\nFirst asset keys:")
print(list(landsat_item.assets.keys())[:20])

For Landsat Collection 2 Level-2, common visible-band asset names are red, green and blue.

The notebook example below uses swir22, nir08 and red. This is a false-colour composite, not a true-colour RGB image. It can be useful for highlighting snow, ice, rock and vegetation patterns, but the colours should be explained in captions and project notes.

landsat_subset = save_multiband_subset(
    item=landsat_item,
    asset_keys=["swir22", "nir08", "red"],
    bbox_wgs84=everest_bbox,
    output_path="data/raw/landsat/landsat_everest_rgb_subset.tif",
)

plot_rgb(landsat_subset, "Landsat subset: Mount Everest region")

Project check: Landsat is strongest when you need temporal depth. Do not force it to answer questions that require very fine spatial detail.


NAIP

NAIP provides high-resolution aerial imagery for the United States. It is useful for fine spatial detail such as buildings, tree crowns and urban features.

Here, the subset focuses on the Statue of Liberty in New York Harbor.

nyc_bbox = [-74.050, 40.688, -74.039, 40.691]  # WGS84 lon/lat around Statue of Liberty

search = pc_catalog.search(
    collections=["naip"],
    bbox=nyc_bbox,
    datetime="2021-01-01/2024-12-31",
    max_items=10,
)

items = list(search.items())
print(f"Found {len(items)} NAIP item(s)")
naip_item = pc.sign(items[0])

print("Selected item:", naip_item.id)
print("Date:", naip_item.datetime)
print("\nAsset keys:")
print(list(naip_item.assets.keys()))

NAIP items on the Planetary Computer typically include a multi-band image asset. Inspect asset keys first; in many cases the image asset is named image.

naip_asset_key = "image"

if naip_asset_key not in naip_item.assets:
    raise KeyError(f"Check available NAIP assets: {list(naip_item.assets.keys())}")

naip_subset = save_single_asset_subset(
    item=naip_item,
    asset_key=naip_asset_key,
    bbox_wgs84=nyc_bbox,
    output_path="data/raw/naip/naip_statue_of_liberty_rgb_subset.tif",
    band_indexes=(1, 2, 3),  # red, green, blue
)

plot_rgb(naip_subset, "NAIP subset: Statue of Liberty, New York")

Project check: NAIP is useful for high-detail examples, but it is not a global source. Use it for United States study areas or for comparing what higher-resolution imagery can reveal.


Vantor Open Data

Vantor Open Data provides high-resolution imagery for selected disaster events. The root catalog is a static STAC catalog, and event collections are listed as child collections.

Here, the example uses a recent international event collection from the catalog: Bordeaux, France wildfire. Because event catalogs change, inspect available collections before using this example in class.

from pystac_client import Client

vantor_catalog = Client.open(
    "https://vantor-opendata.s3.amazonaws.com/events/catalog.json"
)

collections = list(vantor_catalog.get_collections())
print(f"Found {len(collections)} Vantor event collection(s)")

for collection in collections[:10]:
    print(collection.id, "-", collection.title)

Select a Bordeaux-related collection if it is available.

matching = [
    collection
    for collection in collections
    if "Bordeaux" in collection.id or "Bordeaux" in (collection.title or "")
]

if not matching:
    raise ValueError(
        "No Bordeaux collection found. Choose another event from the printed list."
    )

vantor_collection = matching[0]

print("Selected collection:", vantor_collection.id)
print("Title:", vantor_collection.title)
print("License:", vantor_collection.license)
print("Temporal extent:", vantor_collection.extent.temporal.intervals)
print("Spatial extent:", vantor_collection.extent.spatial.bboxes)

Because static event catalogs differ, inspect the items and assets before choosing a subset.

vantor_items = list(vantor_collection.get_items())
print(f"Found {len(vantor_items)} item(s)")

vantor_item = vantor_items[0]

print("Selected item:", vantor_item.id)
print("Date:", vantor_item.datetime)
print("Item bbox:", vantor_item.bbox)

print("\nAssets:")
for key, asset in vantor_item.assets.items():
    print(key, "-", asset.title, "-", asset.media_type)

Choose a small subset near your area of interest.

vantor_bbox = [-1.0846, 44.8698, -1.0666, 44.8748]

print(vantor_bbox)

Select an image asset. Asset names vary, so inspect the printed list first.

image_asset_keys = [
    key
    for key, asset in vantor_item.assets.items()
    if "tif" in str(asset.href).lower() or "tiff" in str(asset.media_type).lower()
]

if not image_asset_keys:
    raise ValueError(
        "No GeoTIFF-like asset found. Inspect the asset list and choose manually."
    )

vantor_asset_key = image_asset_keys[0]
print("Using asset:", vantor_asset_key)
vantor_subset = save_single_asset_subset(
    item=vantor_item,
    asset_key=vantor_asset_key,
    bbox_wgs84=vantor_bbox,
    output_path="data/raw/vantor/vantor_bordeaux_event_rgb_subset.tif",
    band_indexes=(1, 2, 3),
)

plot_rgb(vantor_subset, "Vantor Open Data subset: Bordeaux wildfire event")

Project check: event-based imagery can be highly relevant for disaster mapping, but the coverage is limited to released events. Always check the event, date, licence and whether your intended use is allowed.


SWISSIMAGE data

For projects in Switzerland, SWISSIMAGE can be useful because it provides very high-resolution aerial imagery. At 10 cm resolution, even a small area can become a large raster.

The key classroom pattern is:

Search bbox: WGS84 coordinates
Loaded data: often Swiss projected CRS, such as EPSG:2056

The notebook code below uses the swisstopo STAC search endpoint, groups tiles by year, and mosaics the same Campus Irchel extent across available years. This keeps the example focused on a small area while avoiding visible tile boundaries.

import requests
from collections import defaultdict

# WGS84 bounding box around UZH Campus Irchel
irchel_bbox_wgs84 = [8.550, 47.3970, 8.5535, 47.3995]

# Swiss orthophotos are distributed in LV95 (EPSG:2056); transform the
# bbox once so rasterio.merge can mosaic tiles to this exact extent.
transformer = Transformer.from_crs("EPSG:4326", "EPSG:2056", always_xy=True)
minx, miny = transformer.transform(*irchel_bbox_wgs84[:2])
maxx, maxy = transformer.transform(*irchel_bbox_wgs84[2:])
irchel_bounds_lv95 = (minx, miny, maxx, maxy)

collection_id = "ch.swisstopo.swissimage-dop10"
search_url = "https://data.geo.admin.ch/api/stac/v0.9/search"

response = requests.post(
    search_url,
    json={"collections": [collection_id], "bbox": irchel_bbox_wgs84, "limit": 100},
)
response.raise_for_status()
swissimage_items = response.json().get("features", [])

# SWISSIMAGE tiles are flown in different years; group all tiles that
# intersect the bbox by year so each year can be mosaicked separately.
yearly_tiles = defaultdict(list)
for item in swissimage_items:
    year = item.get("properties", {}).get("datetime", "").split("-")[0] or "Unknown"
    for asset in item.get("assets", {}).values():
        if "image/tiff" in asset.get("type", ""):
            yearly_tiles[year].append(asset.get("href"))
            break

sorted_years = sorted(yearly_tiles)
print(f"Found imagery across {len(sorted_years)} years: {sorted_years}")
for year in sorted_years:
    print(f"  {year}: {len(yearly_tiles[year])} tile(s) to mosaic")

Mosaic all tiles that intersect the bbox for each year, so tile boundaries do not show, and compare the exact same extent across years.

from rasterio.merge import merge

fig, axes = plt.subplots(1, len(sorted_years), figsize=(7 * len(sorted_years), 7))
if len(sorted_years) == 1:
    axes = [axes]

for ax, year in zip(axes, sorted_years):
    try:
        src_files = [rasterio.open(url) for url in yearly_tiles[year]]
        mosaic, _ = merge(src_files, bounds=irchel_bounds_lv95, indexes=[1, 2, 3])
        for src in src_files:
            src.close()

        rgb = np.clip(np.transpose(mosaic, (1, 2, 0)).astype(float) / 255.0, 0, 1)
        ax.imshow(rgb)
        ax.set_title(f"UZH Campus Irchel ({year})", fontweight="bold")
    except Exception as e:
        ax.text(
            0.5,
            0.5,
            f"Error: {e}",
            ha="center",
            va="center",
            transform=ax.transAxes,
            color="red",
        )
    ax.axis("off")

plt.tight_layout()
plt.suptitle("SWISSIMAGE 10cm mosaicked time series: UZH Campus Irchel", y=1.03)
plt.show()

Mosaicking to an exact extent like this keeps you in control of the area and years you need, without downloading full scenes or letting tile seams show. Without subsetting, large raster files can become impractical for quick inspection.

Project check: SWISSIMAGE is excellent for detailed visual inspection in Switzerland, but its resolution can quickly create large arrays. Keep test areas small and document the CRS transformation clearly.


8. Inspect your subset before using it

After creating a subset, inspect the saved file. This is the quality gate between data acquisition and preprocessing.

def inspect_raster(path):
    with rasterio.open(path) as src:
        print("Path:", path)
        print("Width x height:", src.width, "x", src.height)
        print("Bands:", src.count)
        print("CRS:", src.crs)
        print("Resolution:", src.res)
        print("Bounds:", src.bounds)
        print("Data type:", src.dtypes[0])
        print("File size:", f"{Path(path).stat().st_size / 1_000_000:.2f} MB")
inspect_raster(s2_subset)

Run the same check for the other subsets you create.

Use the output to decide whether the subset is ready for the next step.

CheckWhy it matters
Width and heightShows whether the subset is small enough to handle and large enough to inspect.
BandsConfirms whether you downloaded the expected band combination.
CRSHelps avoid spatial misalignment later.
ResolutionShows whether the data can capture your target feature.
BoundsConfirms that the subset covers the intended area.
File sizeHelps keep project storage and repository decisions realistic.

9. Python reactivation

Remote sensing search and download workflows often use familiar Python building blocks:

Python ideaWhere it appears
ListBounding boxes and lists of bands.
DictionaryQuery filters such as cloud-cover thresholds.
LoopInspecting collections, items and assets.
FunctionReusing subset and plotting code.
PathlibCreating output folders and file paths.

Example:

query_filter = {"eo:cloud_cover": {"lt": 20}}
bands = ["B04", "B03", "B02"]

for band in bands:
    print("Requested band:", band)

Keep project parameters visible near the top of your notebook:

project_area = "rome"
bbox_wgs84 = [12.43, 41.88, 12.49, 41.91]
date_range = "2025-06-01/2025-08-31"
max_cloud_cover = 20

This makes your workflow easier to adapt and document.


10. Common pitfalls

Use this table when your imagery workflow becomes confusing, fails unexpectedly or produces data that are too large.

PitfallHow to avoid it
Search returns no itemsCheck bbox order, date range, collection ID and whether the collection covers the area.
Too many items are returnedNarrow the bbox, date range or quality filter.
The saved subset is too largeReduce the bbox, number of bands or number of years.
The subset looks blurry or blockyCheck source resolution and whether the target feature is visible at that resolution.
The subset looks too dark or washed outApply or adjust the percentile stretch for display.
The image appears in the wrong placeCheck search CRS, output CRS and bbox coordinates.
Bands appear in strange coloursCheck band order before plotting an RGB Composite.
Cloud cover is low but the image looks cloudyInspect the actual image and quality layers, not only metadata.
Asset names differ from the examplePrint item.assets.keys() and choose assets manually.
The workflow only works for one locationMove bbox, date range, collection ID and output paths into project parameters.

11. Mini task

Create a subset of imagery for your own project area. Choose the source, bounding box and bands based on what makes sense for your project question.

Try to keep the subset manageable. For a first test, aim for a file below about 100 MB. If the file is larger, shrink the bounding box before continuing.

Document:

Data source:
Catalog:
Collection:
Bounding box:
Date range:
Selected item:
Selected assets:
Output file:
File size:
What the subset shows:
Main limitation:
Next project decision:

12. Key takeaways