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.

GOES-19 ABI extraction with AerEO

This chapter extracts data from GOES-19 ABI (Advanced Baseline Imager), the latest geostationary weather satellite serving the Americas. GOES data is stored in AWS open data as NetCDF files and is read with Satpy.

Because GOES is geostationary, its native projection is a fixed full-disk view. The pipeline reprojects a selected channel to a regular grid over a local AOI.

Environment setup

The first cell installs AerEO plus the AWS GOES search plugin and the Satpy reader. On Binder these are pre-installed, so pip will skip the download.

# Install AerEO and any required plugins for this notebook (Google Colab)
!pip install -q "aereo[viz]" aereo-search-aws-goes aereo-read-satpy

Config used in this notebook

job_goes19.yaml configures a GOES-19 ABI extraction over Córdoba, Argentina:

aoi_path: config/aoi/cordoba.geojson
name: viirs_sample
grid_dist: 50_000
grid_cells_margin: 10
target_aoi: ${aoi_path}
output_uri: /tmp/aereo_extraction
overwrite: false

search:
  _target_: aereo.search_aws_goes.search_aws_goes
  _partial_: true
  collections:
    ABI-L1b-RadF: [C07]
  intersects: ${aoi_path}
  start_datetime: "2026-01-01T15:00:00Z"
  end_datetime: "2026-01-01T15:10:59Z"
  satellites: [GOES-19]
  channels: [C07]

read:
  _partial_: true
  _target_: aereo.read_satpy.read_satpy
  reader: abi_l1b
  wishlist: [C07]

reproject:
  _target_: aereo.builtins.reproject_odc
  _partial_: true
reproject_mode: grid
resolution: 2000

write:
  _target_: aereo.builtins.write.write_geotiff

Key points:

  • search_aws_goes scans the AWS open-data GOES archive.

  • satellites: [GOES-19] selects the GOES-East successor.

  • channels: [C07] selects the shortwave window channel (3.9 µm), useful for fire and low-cloud detection.

  • resolution: 2000 matches the ABI CONUS/full-disk resampling spacing used here.

# Download config files and AOIs from the GitHub repository so this
# notebook can run outside the repo (e.g. Google Colab).
import os
import urllib.request

GITHUB_RAW = "https://raw.githubusercontent.com/frandorr/aereo/main"

os.makedirs("config/aoi", exist_ok=True)

# Config files
urllib.request.urlretrieve(
    f"{GITHUB_RAW}/examples/config/job_goes19.yaml",
    "config/job_goes19.yaml",
)

# AOI files
urllib.request.urlretrieve(
    f"{GITHUB_RAW}/examples/config/aoi/cordoba.geojson",
    "config/aoi/cordoba.geojson",
)

Loading the job

ExtractionJob.load_from_config() parses the YAML with Hydra, resolves every _target_ callable, and validates the resulting ExtractionJob. The config_name argument is the YAML filename without extension, and config_dir is the folder that contains it.

A job is a declarative bundle of pipeline steps. Once loaded, the same job can be searched, can have tasks built from it, and can be executed.

from aereo.cache import TaskResultCache
from aereo.executors import LocalExecutor
from aereo.pipeline import ExtractionJob

job = ExtractionJob.load_from_config(
    config_dir="config",
    config_name="job_goes19",
)
assets = job.search()
2026-07-08 15:11:09 [info     ] search_called                  provider=search_aws_goes

Search and task building

job.search() calls the configured search provider and returns a GeoDataFrame of matched assets. This is a separate step from execution: it only discovers what data is available, without reading or writing anything.

job.build_tasks(assets) turns those assets into a list of ExtractionTask objects. Each task groups the assets needed for one grid cell and time slice, so the work can be parallelised later.

tasks = job.build_tasks(assets)
2026-07-08 15:11:10 [info     ] build_tasks_start              assets=2 builder=build_grouped_tasks
/root/repos/aereo/.venv/lib/python3.13/site-packages/pydantic/_internal/_validate_call.py:137: UserWarning: assets has no 'crs' column; assuming all assets share the same native CRS. Mixed-CRS assets in one task may fail or produce incorrect results.
  res = self.__pydantic_validator__.validate_python(pydantic_core.ArgsKwargs(args, kwargs))

Running the extraction

Each task reads the relevant ABI file from AWS, extracts the requested channel, reprojects it to the local grid, and writes a GeoTIFF.

artifacts = job.execute(
    tasks,
    executor=LocalExecutor(workers=-1, cache=TaskResultCache()),
)
print(f"✓ Extracted {len(artifacts)} artifacts")
2026-07-08 15:11:10 [info     ] execute_start                  executor=LocalExecutor task_count=2
2026-07-08 15:11:14 [debug    ] file_downloaded                engine=satpy local_path=/tmp/aereo_extraction/OR_ABI-L1b-RadF-M6C07_G19_s20260011510229_e20260011519549_c20260011519594.nc
2026-07-08 15:11:14 [debug    ] file_downloaded                engine=satpy local_path=/tmp/aereo_extraction/OR_ABI-L1b-RadF-M6C07_G19_s20260011500229_e20260011509548_c20260011509591.nc
✓ Extracted 34 artifacts

Visualizing results

The reprojected ABI channel is rendered over the AOI. Because ABI is geostationary, pay attention to parallax and viewing angle if your AOI is near the edge of the disk.

from aereo.viz import plot_artifact_patches

plot_artifact_patches(
    artifacts,
    ds_factor=1,
    cmap="viridis",
    stretch="percentile",
    aoi=job.target_aoi,
    aoi_edgecolor="blue",
)
(<Figure size 2000x1591.83 with 2 Axes>, <Axes: title={'center': 'Extracted Patches Spatial Overview'}, xlabel='UTM X', ylabel='UTM Y'>)
Ignoring fixed y limits to fulfill fixed data aspect with adjustable data limits.
<Figure size 2000x1591.83 with 2 Axes>