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.

Explore EarthCARE Data Structure

LifeWatch ERIC

Inspect the internal structure of EarthCARE L2 products to understand dimensions, variables, and coordinate systems before conversion.

import earthcarekit as eck
import numpy as np
import xarray as xr

ORBIT = "06109D"

MSI products (2D swath)

MSI has (along_track, across_track) dimensions with 2D lat/lon arrays — similar to Sentinel-3 OLCI/SLSTR.

for file_type in ["MSI_CM__2A", "MSI_COP_2A", "MSI_AOT_2A"]:
    try:
        result = eck.search_product(file_type=file_type, orbit_and_frame=ORBIT)
        with eck.read_product(result.filepath[0]) as ds:
            print(f"\n{'='*60}")
            print(f"{file_type}")
            print(f"{'='*60}")
            print(f"Dimensions: {dict(ds.sizes)}")
            print(f"\n2D variables (along_track, across_track):")
            for v in ds.data_vars:
                if ds[v].dims == ("along_track", "across_track"):
                    vals = ds[v].values
                    fill = -128 if vals.dtype == np.int8 else None
                    if fill is not None:
                        valid = vals[vals != fill]
                    elif vals.dtype in (np.float32, np.float64):
                        valid = vals[np.isfinite(vals)]
                    else:
                        valid = vals.ravel()
                    n_valid = len(valid)
                    if n_valid > 0:
                        print(f"  {v}: dtype={vals.dtype}, valid={n_valid}, range=[{valid.min():.3f}, {valid.max():.3f}]")
                    else:
                        print(f"  {v}: dtype={vals.dtype}, ALL FILL")
    except Exception as e:
        print(f"\n{file_type}: not available ({e})")

============================================================
MSI_CM__2A
============================================================
Dimensions: {'along_track': 11216, 'across_track': 357}

2D variables (along_track, across_track):
  latitude_swath: dtype=float64, valid=4004112, range=[22.291, 67.620]
  longitude_swath: dtype=float64, valid=4004112, range=[-0.939, 19.344]
  quality_status: dtype=int8, valid=4004112, range=[0.000, 3.000]
  cloud_mask: dtype=int8, valid=3987502, range=[0.000, 3.000]
  cloud_type: dtype=int8, valid=3981555, range=[0.000, 9.000]
  cloud_phase: dtype=int8, valid=954386, range=[1.000, 3.000]
  surface_classification: dtype=int16, valid=4004112, range=[-32765.000, -32509.000]
  cloud_mask_quality_status: dtype=int8, valid=4004112, range=[8.000, 16.000]
  cloud_type_quality_status: dtype=int8, valid=3981555, range=[2.000, 16.000]
  cloud_phase_quality_status: dtype=int8, valid=4004112, range=[-127.000, 16.000]
  plot_surface_classification: dtype=float64, valid=4004112, range=[1.000, 8.000]
  plot_cloud_mask_quality_status: dtype=float64, valid=4004112, range=[3.000, 4.000]
  plot_cloud_type_quality_status: dtype=float64, valid=4004112, range=[0.000, 4.000]
  plot_cloud_phase_quality_status: dtype=float64, valid=4004112, range=[0.000, 4.000]

============================================================
MSI_COP_2A
============================================================
Dimensions: {'along_track': 11216, 'across_track': 357}

2D variables (along_track, across_track):
  latitude_swath: dtype=float64, valid=4004112, range=[22.291, 67.620]
  longitude_swath: dtype=float64, valid=4004112, range=[-0.939, 19.344]
  quality_status: dtype=int8, valid=4004112, range=[0.000, 3.000]
  cloud_optical_thickness: dtype=float32, valid=748392, range=[0.252, 255.000]
  cloud_effective_radius: dtype=float32, valid=748392, range=[0.000, 0.000]
  cloud_water_path: dtype=float32, valid=748392, range=[0.000, 12.008]
  cloud_optical_thickness_error: dtype=float32, valid=748392, range=[0.005, 255.000]
  cloud_effective_radius_error: dtype=float32, valid=748392, range=[0.000, 0.000]
  cloud_water_path_error: dtype=float32, valid=748392, range=[0.000, 12.280]
  cloud_top_pressure: dtype=float32, valid=741865, range=[-48611.242, 124734.094]
  cloud_top_temperature: dtype=float32, valid=741865, range=[172.607, 325.155]
  cloud_top_height: dtype=float32, valid=741865, range=[0.171, 16827.830]
  cloud_top_pressure_error: dtype=float32, ALL FILL
  cloud_top_temperature_error: dtype=float32, valid=741865, range=[0.046, 61.070]
  cloud_top_height_error: dtype=float32, ALL FILL
  isccp_cloud_type: dtype=int64, valid=4004112, range=[-127.000, 9.000]

============================================================
MSI_AOT_2A
============================================================
Dimensions: {'along_track': 11216, 'across_track': 357, 'aerosol_components': 4}

2D variables (along_track, across_track):
  latitude_swath: dtype=float64, valid=4004112, range=[22.291, 67.620]
  longitude_swath: dtype=float64, valid=4004112, range=[-0.939, 19.344]
  viewing_zenith_angle: dtype=float32, valid=4004112, range=[0.686, 17.969]
  aerosol_optical_thickness_670nm: dtype=float32, valid=684230, range=[0.001, 0.462]
  aerosol_optical_thickness_865nm: dtype=float32, valid=341654, range=[0.000, 0.353]
  aerosol_optical_thickness_670nm_error: dtype=float32, valid=684230, range=[0.000, 0.279]
  aerosol_optical_thickness_865nm_error: dtype=float32, valid=341654, range=[0.000, 0.006]
  angstrom_parameter_670nm_865nm: dtype=float32, valid=341603, range=[0.499, 2.399]
  angstrom_parameter_355nm_670nm: dtype=float32, valid=341603, range=[0.676, 1.771]
  quality_mask: dtype=int16, valid=4004112, range=[2.000, 612.000]
  quality_status: dtype=int8, valid=4004112, range=[0.000, 4.000]
  surface_reflectance_670nm: dtype=float32, valid=342576, range=[0.009, 0.125]
  surface_reflectance_670nm_error: dtype=float32, valid=342576, range=[0.018, 0.031]
  plot_quality_mask: dtype=float64, valid=4004112, range=[1.000, 9.000]

ATLID/CPR products (1D profiles)

Profile instruments have (along_track,) lat/lon with a vertical dimension for atmospheric columns.

for file_type in ["ATL_AER_2A"]:
    try:
        result = eck.search_product(file_type=file_type, orbit_and_frame=ORBIT)
        with eck.read_product(result.filepath[0]) as ds:
            print(f"\n{'='*60}")
            print(f"{file_type}")
            print(f"{'='*60}")
            print(f"Dimensions: {dict(ds.sizes)}")
            print(f"Lat: shape={ds['latitude'].shape}, range=[{float(ds['latitude'].min()):.1f}, {float(ds['latitude'].max()):.1f}]")
            print(f"Lon: shape={ds['longitude'].shape}, range=[{float(ds['longitude'].min()):.1f}, {float(ds['longitude'].max()):.1f}]")
            print(f"\n2D variables (along_track, vertical):")
            for v in ds.data_vars:
                if ds[v].dims == ("along_track", "vertical"):
                    print(f"  {v}: shape={ds[v].shape}, dtype={ds[v].dtype}")
    except Exception as e:
        print(f"\n{file_type}: not available ({e})")

============================================================
ATL_AER_2A
============================================================
Dimensions: {'along_track': 5144, 'vertical': 242, 'layer': 25, 'class': 7}
Lat: shape=(5144,), range=[22.5, 67.5]
Lon: shape=(5144,), range=[-0.6, 16.7]

2D variables (along_track, vertical):
  height: shape=(5144, 242), dtype=float32
  range: shape=(5144, 242), dtype=float32
  simple_classification: shape=(5144, 242), dtype=int8
  mie_detection_status: shape=(5144, 242), dtype=int8
  rayleigh_detection_status: shape=(5144, 242), dtype=int8
  horizontal_averaging_mask: shape=(5144, 242), dtype=int32
  internal_lidar_feature_mask: shape=(5144, 242), dtype=int8
  extended_data_quality_status: shape=(5144, 242), dtype=int8
  quality_status: shape=(5144, 242), dtype=int8
  particle_extinction_coefficient_355nm: shape=(5144, 242), dtype=float32
  particle_extinction_coefficient_355nm_error: shape=(5144, 242), dtype=float32
  particle_backscatter_coefficient_355nm: shape=(5144, 242), dtype=float32
  particle_backscatter_coefficient_355nm_error: shape=(5144, 242), dtype=float32
  particle_linear_depol_ratio_355nm: shape=(5144, 242), dtype=float32
  particle_linear_depol_ratio_355nm_error: shape=(5144, 242), dtype=float32
  lidar_ratio_355nm: shape=(5144, 242), dtype=float32
  lidar_ratio_355nm_error: shape=(5144, 242), dtype=float32
  particle_effective_area_radius: shape=(5144, 242), dtype=float32
  multiple_scattering_eta_o_parameter: shape=(5144, 242), dtype=float32
  multiple_scattering_mie_f_fac_parameter: shape=(5144, 242), dtype=float32
  classification: shape=(5144, 242), dtype=int8
  aerosol_classification: shape=(5144, 242), dtype=int8
  aerosol_classification_no_error: shape=(5144, 242), dtype=int8

Visualize ground tracks

import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature

fig, ax = plt.subplots(figsize=(12, 8), subplot_kw={"projection": ccrs.PlateCarree()})
ax.add_feature(cfeature.COASTLINE, linewidth=0.5)
ax.add_feature(cfeature.BORDERS, linewidth=0.3)
ax.gridlines(draw_labels=True, linewidth=0.3)

colors = {"MSI_CM__2A": "blue", "ATL_AER_2A": "red"}

for file_type, color in colors.items():
    try:
        result = eck.search_product(file_type=file_type, orbit_and_frame=ORBIT)
        with eck.read_product(result.filepath[0]) as ds:
            if "latitude_swath" in ds:
                lat = ds["latitude_swath"].values
                lon = ds["longitude_swath"].values
            else:
                lat = ds["latitude"].values
                lon = ds["longitude"].values
            step = max(1, lat.size // 5000)
            ax.scatter(lon.ravel()[::step], lat.ravel()[::step],
                       s=0.3, color=color, label=file_type, transform=ccrs.PlateCarree())
    except Exception:
        pass

ax.legend(markerscale=15)
ax.set_title(f"EarthCARE orbit {ORBIT} — ground tracks")
plt.tight_layout()
plt.show()
<Figure size 1200x800 with 1 Axes>