InSAR Product Archive

UNAVCO/EarthScope InSAR Product HDF5 Format Specification

Version 2.0 - Multi-Interferogram Support

1. Overview

This document describes the HDF5 data format (Version 2.0) for the EarthScope/UNAVCO community-contributed InSAR data archive. The format supports interferograms from multiple tracks/platforms within a single HDF5 file, with comprehensive metadata standards for each track. See the introduction of HDF5 for InSAR Products for more context. Contact support for questions or help.

Key Features of Version 2.0:

  • Simplified Structure: no GEOCODE group (interferograms of different tracks/platforms are directly under root)
  • Multi-Track Support: Store multiple interferograms from different platforms/orbits in a single file
  • Independent Geometries: Each track has its own view geometries (e.g., LOS vectors) and acquisition parameters
  • Hierarchical Organization: Root → Track → Interferograms or Time Series
  • Multi-Platform Fusion: Combine data from different sensors (ALOS-2, Sentinel-1, etc.)
  • Clear Metadata Separation: File-wide processing vs. track-specific acquisition metadata
  • Flexible Product Types: Support for interferograms, displacement time series, and velocity maps

Common Use Cases:

  • Single-Track Interferogram Archive: Store multiple interferometric pairs from one satellite track, including wrapped/unwrapped phase, coherence, and LOS geometry. Ideal for earthquake or landslide studies from a single viewing geometry.
  • Multi-Track Event Analysis: Combine interferograms from different platforms and/or view geometries to resolve 3D surface deformation. Essential for volcanic deformation, large earthquakes, or subsidence monitoring where multiple viewing angles improve deformation characterization.
  • Multi-Platform Fusion: Integrate data from different sensors (e.g., ALOS-2 L-band + Sentinel-1 C-band) to leverage different penetration depths and temporal sampling. Useful for vegetated areas or rapid deformation events.
  • Time Series Products: Store cumulative displacement time series or average velocity maps in LOS direction. Each track maintains its own LOS geometry for proper interpretation. Commonly used for long-term ground motion monitoring.
  • Regional Deformation Mosaics: Combine multiple adjacent tracks covering a large spatial extent (e.g., entire plate boundaries, large volcanic systems). Each track preserves independent processing parameters and quality metrics.
  • Comparative Studies: Archive interferograms from the same area but different time periods, processing methods, or atmospheric corrections. Facilitates method comparison and reprocessing validation.
  • Data Publication and Archiving: Distribute processed InSAR products with comprehensive metadata for reproducibility. Include processing software versions, DEM sources, and atmospheric corrections alongside the data products.

2. File Structure

2.1 Structure for INTERFEROGRAM Products

šŸ“ / (root)
│
ā”œā”€ā”€
ā”œā”€ā”€ @processing_type = "INTERFEROGRAM"
ā”œā”€ā”€ @processing_software = "ISCE2 v2.6.3 + MintPy v1.5.1"
ā”œā”€ā”€ @history = "2024-01-15T10:30:00"
ā”œā”€ā”€ @sign_convention = "Positive LOS displacement corresponds to surface motion toward the sensor"
│
ā”œā”€ā”€ šŸ“ ALOS2_073_A/ ← Track 1
│ ā”œā”€ā”€
│ ā”œā”€ā”€ šŸ“„ line_of_sight_e
│ ā”œā”€ā”€ šŸ“„ line_of_sight_n
│ ā”œā”€ā”€ šŸ“„ line_of_sight_u
│ │
│ ā”œā”€ā”€ šŸ“ 20240101_20240113/
│ │ ā”œā”€ā”€ šŸ“„ unwrapped_interferogram
│ │ ā”œā”€ā”€ šŸ“„ wrapped_interferogram
│ │ └── šŸ“„ correlation
│ │
│ └── šŸ“ 20240113_20240125/
│
└── šŸ“ S1_064_D/ ← Track 2

2.2 Structure for DISP. TIME SERIES Products

šŸ“ / (root)
│
ā”œā”€ā”€
ā”œā”€ā”€ @processing_type = "DISP. TIME SERIES"
ā”œā”€ā”€ @processing_software = "MintPy v1.5.1"
ā”œā”€ā”€ @history = "2024-01-15T10:30:00"
ā”œā”€ā”€ @sign_convention = "Positive LOS displacement corresponds to surface motion toward the sensor"
│
ā”œā”€ā”€ šŸ“ ALOS2_073_A/ ← Track 1
│ ā”œā”€ā”€
│ ā”œā”€ā”€ @reference_date = "20240101" ← Track-specific reference (REQUIRED)
│ ā”œā”€ā”€ šŸ“„ line_of_sight_e
│ ā”œā”€ā”€ šŸ“„ line_of_sight_n
│ ā”œā”€ā”€ šŸ“„ line_of_sight_u
│ │
│ ā”œā”€ā”€ šŸ“„ dLOS_20240101 ← Reference date (zero displacement)
│ ā”œā”€ā”€ šŸ“„ dLOS_20240113 ← Cumulative displacement
│ ā”œā”€ā”€ šŸ“„ dLOS_20240125
│ ā”œā”€ā”€ šŸ“„ dLOS_20240208
│ └── šŸ“„ dLOS_20240401
│
└── šŸ“ S1_064_D/ ← Track 2
ā”œā”€ā”€ @reference_date = "20240105" ← Different reference date!
ā”œā”€ā”€ šŸ“„ line_of_sight_e
ā”œā”€ā”€ šŸ“„ line_of_sight_n
ā”œā”€ā”€ šŸ“„ line_of_sight_u
│
ā”œā”€ā”€ šŸ“„ dLOS_20240105 ← Reference for this track
ā”œā”€ā”€ šŸ“„ dLOS_20240117
└── šŸ“„ dLOS_20240129

2.3 Structure for LOS_VELOCITY Products

šŸ“ / (root)
│
ā”œā”€ā”€ @processing_type = "LOS_VELOCITY"
ā”œā”€ā”€ @processing_software = "MintPy v1.5.1"
ā”œā”€ā”€ @history = "2024-01-15T10:30:00"
ā”œā”€ā”€ @sign_convention = "Positive LOS displacement corresponds to surface motion toward the sensor"
│
ā”œā”€ā”€ šŸ“ ALOS2_073_A/
│ ā”œā”€ā”€ šŸ“„ line_of_sight_e
│ ā”œā”€ā”€ šŸ“„ line_of_sight_n
│ ā”œā”€ā”€ šŸ“„ line_of_sight_u
│ ā”œā”€ā”€ šŸ“„ velocity ← Mean LOS velocity
│ └── šŸ“„ velocity_std ← Optional: standard deviation
│
└── šŸ“ S1_064_D/

2.4 Track Naming Convention

Format: {PLATFORM}_{ORBIT}_{DIRECTION}

Examples:

  • ALOS2_073_A - ALOS-2, orbit 73, ascending
  • S1_064_D - Sentinel-1, orbit 64, descending
  • TSX_142_A - TerraSAR-X, orbit 142, ascending
  • CSK_037_D - COSMO-SkyMed, orbit 37, descending

Alternative (if needed): {PLATFORM}_{ORBIT}_{DIRECTION}_{SWATH}

3. Root-Level Metadata (File-Wide)

These metadata fields apply to the entire file and describe processing methods used across all tracks.

Attribute Type Description Example
processing_type REQUIRED String Type of InSAR product "INTERFEROGRAM", "LOS_VELOCITY", "DISP. TIME SERIES"
processing_software REQUIRED String Software name and version used "ISCE2 v2.6.3 + SNAPHU v2.0.5"
history REQUIRED String File creation timestamp (ISO 8601) "2024-01-15T10:30:00"
sign_convention REQUIRED String Sign convention of LOS displacement/velocity "Positive LOS displacement corresponds to surface motion toward the sensor"
creators RECOMMENDED String (JSON) JSON array of author objects '[{"name":"Jane Smith","institution":"Univ"}]'
publication RECOMMENDED String Related publication citation with DOI "Smith et al. (2024). doi:10.xxxx/yyyy"

4. Track-Level Metadata

These metadata fields are stored as attributes on each track group (e.g., /ALOS2_073_A/) and describe the acquisition parameters specific to that track.

Attribute Type Description Example
platform REQUIRED String Name of the platform/mission "ALOS-2", "SENTINEL-1", see supported platforms
relative_orbit REQUIRED Integer Relative orbit/track/path number 73, 154
flight_direction REQUIRED String Flight direction "A" (Ascending) or "D" (Descending)
look_direction REQUIRED String Look direction "R" (Right) or "L" (Left)
beam_mode REQUIRED String Beam mode as used by space agency "WD1", "IW", "SM"
beam_swath REQUIRED String Swath identifier (no spaces) "W1", "IW2", "strip_013", "NA" if unknown
wavelength REQUIRED Float Radar wavelength (meters) 0.0555462 (C-band), 0.236 (L-band)
scene_footprint REQUIRED String WKT POLYGON of scene extent (lon, lat) "POLYGON((-118.2 34.0, ...))"
first_date REQUIRED String Earliest acquisition date of the track (ISO 8601: YYYY-MM-DD) "2024-01-01"
last_date REQUIRED String Latest acquisition date of the track (ISO 8601: YYYY-MM-DD) "2024-03-01"
time_acquisition REQUIRED String Approximate time (UTC) of image acquisition over the study area "18:03"
polarization RECOMMENDED String Polarization mode "VV", "HH", "VV+VH"
frame RECOMMENDED Integer Frame number 1234
atmos_correct_method RECOMMENDED String Atmospheric correction method "GACOS", "ERA5", "None"
processing_dem RECOMMENDED String DEM source used for processing "SRTM30", "ASTER"
post_processing_method RECOMMENDED String Post-processing approach "MintPy", "StaMPS", "NSBAS"
percent_unwrapped RECOMMENDED Float Average % unwrapped pixels (track-wide) 92.5
average_coherence RECOMMENDED Float Average coherence (track-wide) 0.68
reference_date REQUIRED for TIME SERIES String Reference date for this track's time series (applicable for DISP. TIME SERIES product). Overrides root-level reference_date if present "20240101"

5. Interferogram-Specific Metadata

These metadata fields are stored as attributes on each date-pair group (e.g., /ALOS2_073_A/20240101_20240113/) for INTERFEROGRAM products only:

Attribute Type Description Example
reference_date RECOMMENDED String Reference (first) acquisition date (YYYYMMDD) "20240101"
secondary_date RECOMMENDED String Secondary (repeat) acquisition date (YYYYMMDD) "20240113"
temporal_baseline_days RECOMMENDED Integer Temporal baseline in days 12
baseline_perp RECOMMENDED Float Perpendicular baseline (meters) 45.2
reference_platform RECOMMENDED String Reference image platform/satellite "SENTINEL-1A"
repeat_platform RECOMMENDED String Repeat image platform/satellite "SENTINEL-1B"
phase_sign_convention RECOMMENDED String Sign convention of phase "Postive phase corresponds to range increase (i.e., motion away from the platform)"
percent_unwrapped RECOMMENDED Float Percentage of unwrapped pixels for this interferogram 94.2
average_coherence RECOMMENDED Float Average coherence for this interferogram 0.72

6. Dataset Definitions

Dataset Organization by Processing Type:

  • INTERFEROGRAM: Date-pair groups (e.g., 20240101_20240113/) containing unwrapped/wrapped phase and coherence
  • DISP. TIME SERIES: Individual displacement datasets (e.g., dLOS_20240101) directly under each track
  • LOS_VELOCITY: Single velocity dataset (velocity) per track with optional uncertainty fields

6.1 Line-of-Sight (LOS) Vectors (Track-Specific)

Location: /{TRACK}/line_of_sight_{e,n,u} (each track has its own)

Data Type: Float32 or Float64

Components:

  • line_of_sight_e - East component
  • line_of_sight_n - North component
  • line_of_sight_u - Up component

Units: Dimensionless (unit vectors)

Valid Range: [0.0, 1.0] for each component

Note: Each track has independent LOS vectors since acquisition geometry differs between tracks.

6.2 Unwrapped Interferogram (INTERFEROGRAM products only)

Location: /{TRACK}/YYYYMMDD_YYYYMMDD/unwrapped_interferogram

Data Type: Float32 or Float64

Units: Radians

Convention: Positive = increase in range (away from sensor), Negative = decrease in range (towards sensor)

Attributes:

  • @description - "Unwrapped interferometric phase"
  • @units - "radians"
  • @unwrap_method - Method used (e.g., "SNAPHU")

6.3 Wrapped Interferogram (INTERFEROGRAM products only)

Location: /{TRACK}/YYYYMMDD_YYYYMMDD/wrapped_interferogram

Data Type: Float32 or Float64

Units: Radians

Valid Range: [-Ļ€, +Ļ€]

Attributes:

  • @description - "Wrapped interferometric phase"
  • @units - "radians"
  • @valid_range - [-3.14159, 3.14159]

6.4 Correlation (Coherence) (INTERFEROGRAM products only)

Location: /{TRACK}/YYYYMMDD_YYYYMMDD/correlation

Data Type: Float32 or Float64

Units: Dimensionless

Valid Range: [0.0, 1.0]

Interpretation: 0 = pure noise, 1 = perfect correlation

Attributes:

  • @description - "Interferometric coherence"
  • @units - "dimensionless"
  • @valid_range - [0.0, 1.0]
  • @max_coherence - Maximum coherence observed

6.5 LOS Displacement Time Series (DISP. TIME SERIES products only)

Location: /{TRACK}/dLOS_YYYYMMDD

Naming Convention: dLOS_{date} where date is in YYYYMMDD format

Data Type: Float32 or Float64

Units: Meters (m)

Convention: Positive = motion toward sensor (range decrease), Negative = motion away from sensor (range increase)

Reference: All displacements are relative to the reference date specified in track-level @reference_date (or root-level if track-level not present)

Attributes:

  • @description - "Cumulative LOS displacement relative to reference date"
  • @units - "meters"
  • @acquisition_date - Date of this acquisition (YYYYMMDD)
  • @reference_date - Reference date for this dataset (YYYYMMDD) - should match track-level reference_date
  • @temporal_coherence (optional) - Average temporal coherence for this date

Important Notes:

  • Track-specific reference dates: Each track MUST have its own @reference_date attribute. Different tracks can have different reference dates
  • The reference date dataset (e.g., dLOS_20240101) should contain all zeros
  • Each subsequent date contains cumulative displacement since that track's reference date
  • Root-level @reference_date is optional and can be used if all tracks share the same reference
  • Datasets are stored directly under the track group, not in date-pair subgroups
  • Time series processing typically uses SBAS, PS, or similar methods

6.6 LOS Velocity (LOS_VELOCITY products only)

Location: /{TRACK}/velocity

Data Type: Float32 or Float64

Units: Meters per year (m/year) or millimeters per year (mm/year)

Convention: Positive = motion toward sensor, Negative = motion away from sensor

Attributes:

  • @description - "Mean LOS velocity"
  • @units - "m/year" or "mm/year"
  • @time_span_start - Start date of velocity estimation (YYYY-MM-DD)
  • @time_span_end - End date of velocity estimation (YYYY-MM-DD)
  • @estimation_method - Method used (e.g., "linear regression", "MSBAS")

Optional Uncertainty Field: /{TRACK}/velocity_std

  • Same dimensions and units as velocity
  • Contains standard deviation or uncertainty estimate
  • @description - "Standard deviation of LOS velocity"

Key Differences Between Product Types:

Feature INTERFEROGRAM DISP. TIME SERIES LOS_VELOCITY
Data Organization Date-pair groups Individual date datasets Single velocity dataset
Dataset Names YYYYMMDD_YYYYMMDD/ dLOS_YYYYMMDD velocity
Units Radians Meters (m) m/year or mm/year
Reference Date Not applicable Required (track-level) Time span in attributes
Typical Use Raw phase measurements Time-dependent deformation Long-term rates

7. Code Examples

šŸ“ About These Examples

The following code examples are demonstrations of the file format structure. They use synthetic data to illustrate the organization and metadata requirements.

To use these examples with your data:

  • Replace the synthetic data generation sections with your actual interferogram, displacement, or velocity arrays
  • Update all metadata fields (platform, orbit, dates, etc.) to match your processing
  • Adjust array dimensions to match your data size
  • Add additional tracks or dates as needed

7.1 Python Examples

7.1.1 Creating an INTERFEROGRAM File

This example demonstrates the structure for interferogram products. Replace the synthetic data with your actual unwrapped/wrapped phase and coherence arrays.

example_create_interferogram.py
#!/usr/bin/env python3
"""
Example: Creating an Interferogram HDF5 File (Version 2.0)
Demonstrates the file structure - replace synthetic data with your actual interferograms
"""

import h5py
import numpy as np
from datetime import datetime

# Output filename
filename = 'example_interferogram.h5'

with h5py.File(filename, 'w') as f:
    
    # ============================================================
    # 1. ROOT-LEVEL METADATA (file-wide)
    # ============================================================
    f.attrs['processing_type'] = 'INTERFEROGRAM'
    f.attrs['processing_software'] = 'ISCE2 v2.6.3 + SNAPHU v2.0.5'
    f.attrs['history'] = datetime.now().isoformat()
    f.attrs['sign_convention'] = 'Positive LOS displacement corresponds to surface motion toward the sensor'
    
    # ============================================================
    # 2. CREATE TRACK
    # ============================================================
    track = f.create_group('ALOS2_073_A')
    
    # Track-level metadata (REQUIRED fields)
    track.attrs['platform'] = 'ALOS-2'
    track.attrs['relative_orbit'] = 73
    track.attrs['flight_direction'] = 'A'
    track.attrs['look_direction'] = 'R'
    track.attrs['beam_mode'] = 'WD1'
    track.attrs['beam_swath'] = 'W1'
    track.attrs['wavelength'] = 0.236
    track.attrs['scene_footprint'] = 'POLYGON((-118.5 34.0, -118.0 34.0, -118.0 34.5, -118.5 34.5, -118.5 34.0))'
    track.attrs['first_date'] = '2024-01-01'
    track.attrs['last_date'] = '2024-02-08'
    track.attrs['time_acquisition'] = '10:23'
    
    # Optional but recommended
    track.attrs['polarization'] = 'VV'
    
    # ============================================================
    # 3. LOS VECTORS (replace with your actual LOS geometry)
    # ============================================================
    # TODO: Replace this synthetic data with your actual LOS vectors
    demo_shape = (1000, 1200)  # Replace with your data dimensions
    
    # Generate synthetic LOS vectors for demonstration
    los_e_data = np.random.uniform(0.35, 0.45, demo_shape).astype(np.float32)
    los_n_data = np.random.uniform(-0.05, 0.05, demo_shape).astype(np.float32)
    los_u_data = np.random.uniform(0.75, 0.85, demo_shape).astype(np.float32)
    
    # Normalize to unit vectors
    magnitude = np.sqrt(los_e_data**2 + los_n_data**2 + los_u_data**2)
    los_e_data /= magnitude
    los_n_data /= magnitude
    los_u_data /= magnitude
    
    # Write LOS datasets with compression
    los_e = track.create_dataset('line_of_sight_e', data=los_e_data, compression='gzip')
    los_e.attrs['description'] = 'LOS unit vector - East component'
    los_e.attrs['units'] = 'dimensionless'
    
    los_n = track.create_dataset('line_of_sight_n', data=los_n_data, compression='gzip')
    los_n.attrs['description'] = 'LOS unit vector - North component'
    los_n.attrs['units'] = 'dimensionless'
    
    los_u = track.create_dataset('line_of_sight_u', data=los_u_data, compression='gzip')
    los_u.attrs['description'] = 'LOS unit vector - Up component'
    los_u.attrs['units'] = 'dimensionless'
    
    # ============================================================
    # 4. INTERFEROGRAM DATA (replace with your actual phase data)
    # ============================================================
    # Create date-pair group
    date_group = track.create_group('20240101_20240113')
    
    # Interferogram metadata
    date_group.attrs['reference_date'] = '20240101'
    date_group.attrs['secondary_date'] = '20240113'
    date_group.attrs['temporal_baseline_days'] = 12
    date_group.attrs['baseline_perp'] = 45.2
    
    # TODO: Replace this synthetic data with your actual unwrapped phase
    # Generate synthetic unwrapped interferogram for demonstration
    x = np.linspace(0, 4*np.pi, demo_shape[1])
    y = np.linspace(0, 4*np.pi, demo_shape[0])
    X, Y = np.meshgrid(x, y)
    unwrapped_data = (np.sin(X/3) * np.cos(Y/3) * 2 * np.pi).astype(np.float32)
    
    # Write unwrapped interferogram
    unwrapped = date_group.create_dataset('unwrapped_interferogram', 
                                           data=unwrapped_data, compression='gzip')
    unwrapped.attrs['description'] = 'Unwrapped interferometric phase'
    unwrapped.attrs['units'] = 'radians'
    unwrapped.attrs['unwrap_method'] = 'SNAPHU'
    
    # Create wrapped interferogram
    wrapped_data = np.angle(np.exp(1j * unwrapped_data)).astype(np.float32)
    wrapped = date_group.create_dataset('wrapped_interferogram', 
                                        data=wrapped_data, compression='gzip')
    wrapped.attrs['description'] = 'Wrapped interferometric phase'
    wrapped.attrs['units'] = 'radians'
    
    # TODO: Replace with your actual coherence data
    # Generate synthetic coherence for demonstration
    corr_data = np.random.uniform(0.5, 0.9, demo_shape).astype(np.float32)
    correlation = date_group.create_dataset('correlation', 
                                            data=corr_data, compression='gzip')
    correlation.attrs['description'] = 'Interferometric coherence'
    correlation.attrs['units'] = 'dimensionless'

print(f"āœ“ Created {filename}")
print(f"  Explore with: h5ls -r {filename}")
print(f"\nNOTE: This file contains synthetic demo data.")
print("Replace the data generation sections with your actual interferogram data.")

7.1.2 Creating a TIME SERIES File

This example demonstrates the structure for displacement time series products. Replace the synthetic displacement with your actual time series results.

example_create_timeseries.py
#!/usr/bin/env python3
"""
Example: Creating a Time Series HDF5 File (Version 2.0)
Demonstrates the file structure - replace synthetic data with your actual time series
"""

import h5py
import numpy as np
from datetime import datetime

# Output filename
filename = 'example_timeseries.h5'

with h5py.File(filename, 'w') as f:
    
    # ============================================================
    # 1. ROOT-LEVEL METADATA (file-wide)
    # ============================================================
    f.attrs['processing_type'] = 'DISP. TIME SERIES'
    f.attrs['processing_software'] = 'MintPy v1.5.1'
    f.attrs['history'] = datetime.now().isoformat()
    f.attrs['sign_convention'] = 'Positive LOS displacement corresponds to surface motion toward the sensor'
    
    # ============================================================
    # 2. CREATE TRACK
    # ============================================================
    track = f.create_group('ALOS2_073_A')
    
    # Track-level metadata (REQUIRED fields)
    track.attrs['platform'] = 'ALOS-2'
    track.attrs['relative_orbit'] = 73
    track.attrs['flight_direction'] = 'A'
    track.attrs['look_direction'] = 'R'
    track.attrs['beam_mode'] = 'WD1'
    track.attrs['beam_swath'] = 'W1'
    track.attrs['wavelength'] = 0.236
    track.attrs['scene_footprint'] = 'POLYGON((-118.5 34.0, -118.0 34.0, -118.0 34.5, -118.5 34.5, -118.5 34.0))'
    track.attrs['first_date'] = '2024-01-01'
    track.attrs['last_date'] = '2024-04-01'
    track.attrs['time_acquisition'] = '10:23'
    track.attrs['polarization'] = 'VV'
    
    # REQUIRED for time series: track-specific reference date
    track.attrs['reference_date'] = '20240101'
    
    # ============================================================
    # 3. LOS VECTORS (replace with your actual LOS geometry)
    # ============================================================
    # TODO: Replace with your actual LOS vectors
    demo_shape = (1000, 1200)  # Replace with your data dimensions
    
    los_e_data = np.random.uniform(0.35, 0.45, demo_shape).astype(np.float32)
    los_n_data = np.random.uniform(-0.05, 0.05, demo_shape).astype(np.float32)
    los_u_data = np.random.uniform(0.75, 0.85, demo_shape).astype(np.float32)
    
    magnitude = np.sqrt(los_e_data**2 + los_n_data**2 + los_u_data**2)
    los_e_data /= magnitude
    los_n_data /= magnitude
    los_u_data /= magnitude
    
    los_e = track.create_dataset('line_of_sight_e', data=los_e_data, 
                                 compression='gzip', compression_opts=6)
    los_e.attrs['description'] = 'LOS unit vector - East component'
    los_e.attrs['units'] = 'dimensionless'
    
    los_n = track.create_dataset('line_of_sight_n', data=los_n_data, 
                                 compression='gzip', compression_opts=6)
    los_n.attrs['description'] = 'LOS unit vector - North component'
    los_n.attrs['units'] = 'dimensionless'
    
    los_u = track.create_dataset('line_of_sight_u', data=los_u_data, 
                                 compression='gzip', compression_opts=6)
    los_u.attrs['description'] = 'LOS unit vector - Up component'
    los_u.attrs['units'] = 'dimensionless'
    
    # ============================================================
    # 4. DISPLACEMENT TIME SERIES (replace with your actual displacements)
    # ============================================================
    # List of dates in your time series
    dates = ['20240101', '20240113', '20240125', '20240208']
    
    print(f"Creating time series with {len(dates)} dates...")
    
    for idx, date_str in enumerate(dates):
        
        # TODO: Replace this synthetic data with your actual displacement
        if idx == 0:
            # Reference date: all zeros
            disp_data = np.zeros(demo_shape, dtype=np.float32)
        else:
            # Cumulative displacement in meters
            # This is synthetic - replace with your actual time series data
            disp_data = (np.random.randn(*demo_shape) * 0.01 * idx).astype(np.float32)
        
        # Create dataset: dLOS_YYYYMMDD (note: directly under track, not in subgroups)
        dataset_name = f'dLOS_{date_str}'
        dset = track.create_dataset(dataset_name, data=disp_data, 
                                     compression='gzip', compression_opts=6)
        
        # Dataset attributes
        dset.attrs['description'] = 'Cumulative LOS displacement relative to reference date'
        dset.attrs['units'] = 'meters'  # Note: meters, not radians!
        dset.attrs['acquisition_date'] = date_str
        dset.attrs['reference_date'] = '20240101'  # Should match track-level reference_date
        
        print(f"  Added dLOS_{date_str}")

print(f"\nāœ“ Created {filename}")
print(f"  Explore with: h5ls -r {filename}")
print(f"\nNOTE: This file contains synthetic demo data.")
print("Replace the displacement generation with your actual time series data.")

7.1.3 Reading HDF5 Files

Example script demonstrating how to read InSAR HDF5 files and extract data.

example_read_insar.py
#!/usr/bin/env python3
"""
Example: Reading InSAR HDF5 Files
Demonstrates how to read different product types
"""

import h5py
import numpy as np

# Adjust to your actual filename
filename = 'example_timeseries.h5'

with h5py.File(filename, 'r') as f:
    
    # ============================================================
    # 1. READ ROOT METADATA
    # ============================================================
    proc_type = f.attrs['processing_type']
    print(f"Processing type: {proc_type}")
    print(f"Software: {f.attrs['processing_software']}")
    
    # ============================================================
    # 2. READ TRACK METADATA
    # ============================================================
    # Adjust track name to match your file
    track_name = 'ALOS2_073_A'
    track = f[track_name]
    
    print(f"\nTrack: {track_name}")
    print(f"  Platform: {track.attrs['platform']}")
    print(f"  Orbit: {track.attrs['relative_orbit']}")
    print(f"  Wavelength: {track.attrs['wavelength']:.4f} m")
    
    # ============================================================
    # 3. READ LOS VECTORS
    # ============================================================
    los_e = track['line_of_sight_e'][:]
    los_n = track['line_of_sight_n'][:]
    los_u = track['line_of_sight_u'][:]
    print(f"  LOS vectors shape: {los_e.shape}")
    
    # ============================================================
    # 4. READ DATA BASED ON PRODUCT TYPE
    # ============================================================
    if proc_type == 'INTERFEROGRAM':
        print("\nReading interferogram...")
        
        # List available interferogram date pairs
        ifgs = [key for key in track.keys() if '_' in key and len(key) == 17]
        print(f"  Found {len(ifgs)} interferogram(s): {ifgs}")
        
        # Read specific interferogram (adjust date pair to your data)
        date_pair = '20240101_20240113'
        ifg_group = track[date_pair]
        
        unwrapped = ifg_group['unwrapped_interferogram'][:]
        print(f"  Unwrapped phase shape: {unwrapped.shape}")
        print(f"  Unwrapped range: [{unwrapped.min():.2f}, {unwrapped.max():.2f}] radians")
        
        # Read coherence
        correlation = ifg_group['correlation'][:]
        print(f"  Mean coherence: {correlation.mean():.3f}")
        
    elif proc_type == 'DISP. TIME SERIES':
        print("\nReading time series...")
        
        # Get reference date (REQUIRED at track level)
        ref_date = track.attrs['reference_date']
        print(f"  Reference date: {ref_date}")
        
        # List all displacement dates
        dates = sorted([key.replace('dLOS_', '') for key in track.keys() 
                       if key.startswith('dLOS_')])
        print(f"  Found {len(dates)} date(s): {dates}")
        
        # Read specific date (adjust to your data)
        date_to_read = '20240113'
        disp = track[f'dLOS_{date_to_read}'][:]
        print(f"  Displacement shape: {disp.shape}")
        print(f"  Displacement range: [{disp.min():.4f}, {disp.max():.4f}] meters")
        
    elif proc_type == 'LOS_VELOCITY':
        print("\nReading velocity...")
        
        velocity = track['velocity'][:]
        print(f"  Velocity shape: {velocity.shape}")
        print(f"  Velocity range: [{velocity.min():.4f}, {velocity.max():.4f}] m/year")
        
        # Read uncertainty if available
        if 'velocity_std' in track:
            velocity_std = track['velocity_std'][:]
            print(f"  Mean uncertainty: {velocity_std.mean():.4f} m/year")

print("\nāœ“ File read successfully")

šŸ’” Adapting Python Examples for Your Data:

  • Replace synthetic data: Look for # TODO: comments marking where to insert your actual arrays
  • Update dimensions: Change demo_shape = (1000, 1200) to match your data size
  • Update metadata: Modify platform, orbit, dates, and other parameters to match your processing
  • Add multiple tracks: Duplicate the track creation sections with different names and geometries
  • Include all your dates: Expand the dates list for time series or add more date-pair groups for interferograms
  • Load your data: Use your preferred method (GDAL, rasterio, numpy.load, etc.) to read your processed results

7.2 MATLAB Examples

7.2.1 Creating an INTERFEROGRAM File

This example script shows the structure for interferogram products. Adapt the data generation sections to match your actual processed interferograms.

example_create_interferogram.m
%% Example: Creating an Interferogram HDF5 File (Version 2.0)
% This script demonstrates the HDF5 format structure
% Replace demo data generation with your actual interferogram data

clear; clc;

filename = 'example_interferogram.h5';
if exist(filename, 'file'), delete(filename); end

%% 1. Root-level metadata (file-wide)
h5writeatt(filename, '/', 'processing_type', 'INTERFEROGRAM');
h5writeatt(filename, '/', 'processing_software', 'ISCE2 v2.6.3 + SNAPHU v2.0.5');
h5writeatt(filename, '/', 'history', char(datetime('now', 'Format', 'yyyy-MM-dd''T''HH:mm:ss')));
h5writeatt(filename, '/', 'sign_convention', ...
    'Positive LOS displacement corresponds to surface motion toward the sensor');

%% 2. Track-level metadata
track_path = '/ALOS2_073_A';

% REQUIRED metadata
h5writeatt(filename, track_path, 'platform', 'ALOS-2');
h5writeatt(filename, track_path, 'relative_orbit', int32(73));
h5writeatt(filename, track_path, 'flight_direction', 'A');
h5writeatt(filename, track_path, 'look_direction', 'R');
h5writeatt(filename, track_path, 'beam_mode', 'WD1');
h5writeatt(filename, track_path, 'beam_swath', 'W1');
h5writeatt(filename, track_path, 'wavelength', 0.236);
h5writeatt(filename, track_path, 'first_date', '2024-01-01');
h5writeatt(filename, track_path, 'last_date', '2024-02-08');
h5writeatt(filename, track_path, 'time_acquisition', '10:23');
h5writeatt(filename, track_path, 'scene_footprint', ...
    'POLYGON((-118.5 34.0, -118.0 34.0, -118.0 34.5, -118.5 34.5, -118.5 34.0))');

% Optional but recommended
h5writeatt(filename, track_path, 'polarization', 'VV');

%% 3. LOS vectors (replace with your actual LOS geometry)
% TODO: Replace this with your actual LOS vectors
demo_shape = [1000, 1200];  % [rows, cols] - adjust to your data size

% Generate synthetic LOS vectors for demonstration
los_e = 0.35 + 0.10 * rand(demo_shape, 'single');
los_n = -0.05 + 0.10 * rand(demo_shape, 'single');
los_u = 0.75 + 0.10 * rand(demo_shape, 'single');

% Normalize to unit vectors
magnitude = sqrt(los_e.^2 + los_n.^2 + los_u.^2);
los_e = los_e ./ magnitude;
los_n = los_n ./ magnitude;
los_u = los_u ./ magnitude;

% Write LOS datasets with compression
h5create(filename, [track_path '/line_of_sight_e'], demo_shape, ...
    'Datatype', 'single', 'ChunkSize', [100 100], 'Deflate', 6);
h5write(filename, [track_path '/line_of_sight_e'], los_e);
h5writeatt(filename, [track_path '/line_of_sight_e'], 'units', 'dimensionless');

h5create(filename, [track_path '/line_of_sight_n'], demo_shape, ...
    'Datatype', 'single', 'ChunkSize', [100 100], 'Deflate', 6);
h5write(filename, [track_path '/line_of_sight_n'], los_n);
h5writeatt(filename, [track_path '/line_of_sight_n'], 'units', 'dimensionless');

h5create(filename, [track_path '/line_of_sight_u'], demo_shape, ...
    'Datatype', 'single', 'ChunkSize', [100 100], 'Deflate', 6);
h5write(filename, [track_path '/line_of_sight_u'], los_u);
h5writeatt(filename, [track_path '/line_of_sight_u'], 'units', 'dimensionless');

%% 4. Interferogram data (replace with your actual phase data)
date_path = [track_path '/20240101_20240113'];

% Interferogram metadata
h5writeatt(filename, date_path, 'reference_date', '20240101');
h5writeatt(filename, date_path, 'secondary_date', '20240113');
h5writeatt(filename, date_path, 'temporal_baseline_days', int32(12));
h5writeatt(filename, date_path, 'baseline_perp', 45.2);

% TODO: Replace this synthetic data with your actual unwrapped interferogram
% Generate synthetic unwrapped phase for demonstration
[X, Y] = meshgrid(linspace(0, 4*pi, demo_shape(2)), linspace(0, 4*pi, demo_shape(1)));
unwrapped = single(sin(X/3) .* cos(Y/3) * 2 * pi);

h5create(filename, [date_path '/unwrapped_interferogram'], demo_shape, ...
    'Datatype', 'single', 'ChunkSize', [100 100], 'Deflate', 6);
h5write(filename, [date_path '/unwrapped_interferogram'], unwrapped);
h5writeatt(filename, [date_path '/unwrapped_interferogram'], 'units', 'radians');
h5writeatt(filename, [date_path '/unwrapped_interferogram'], 'unwrap_method', 'SNAPHU');

% Wrapped phase
wrapped = single(angle(exp(1i * unwrapped)));
h5create(filename, [date_path '/wrapped_interferogram'], demo_shape, ...
    'Datatype', 'single', 'ChunkSize', [100 100], 'Deflate', 6);
h5write(filename, [date_path '/wrapped_interferogram'], wrapped);
h5writeatt(filename, [date_path '/wrapped_interferogram'], 'units', 'radians');

% TODO: Replace with your actual coherence data
% Generate synthetic coherence for demonstration
coherence = single(0.5 + 0.4 * rand(demo_shape));
h5create(filename, [date_path '/correlation'], demo_shape, ...
    'Datatype', 'single', 'ChunkSize', [100 100], 'Deflate', 6);
h5write(filename, [date_path '/correlation'], coherence);
h5writeatt(filename, [date_path '/correlation'], 'units', 'dimensionless');

fprintf('āœ“ Created %s\n', filename);
fprintf('  Explore with: h5disp(''%s'')\n\n', filename);
fprintf('NOTE: This file contains synthetic demo data.\n');
fprintf('Replace the data generation sections with your actual interferogram data.\n');

7.2.2 Creating a TIME SERIES File

This example shows how to structure displacement time series data. Replace the synthetic displacement with your actual time series results.

example_create_timeseries.m
%% Example: Creating a Time Series HDF5 File (Version 2.0)
% This script demonstrates the time series format structure
% Replace demo data with your actual time series displacements

clear; clc;

filename = 'example_timeseries.h5';
if exist(filename, 'file'), delete(filename); end

%% 1. Root-level metadata
h5writeatt(filename, '/', 'processing_type', 'DISP. TIME SERIES');
h5writeatt(filename, '/', 'processing_software', 'MintPy v1.5.1');
h5writeatt(filename, '/', 'history', char(datetime('now', 'Format', 'yyyy-MM-dd''T''HH:mm:ss')));
h5writeatt(filename, '/', 'sign_convention', ...
    'Positive LOS displacement corresponds to surface motion toward the sensor');

%% 2. Track-level metadata
track_path = '/ALOS2_073_A';

% REQUIRED metadata
h5writeatt(filename, track_path, 'platform', 'ALOS-2');
h5writeatt(filename, track_path, 'relative_orbit', int32(73));
h5writeatt(filename, track_path, 'flight_direction', 'A');
h5writeatt(filename, track_path, 'look_direction', 'R');
h5writeatt(filename, track_path, 'beam_mode', 'WD1');
h5writeatt(filename, track_path, 'beam_swath', 'W1');
h5writeatt(filename, track_path, 'wavelength', 0.236);
h5writeatt(filename, track_path, 'first_date', '2024-01-01');
h5writeatt(filename, track_path, 'last_date', '2024-04-01');
h5writeatt(filename, track_path, 'time_acquisition', '10:23');

% REQUIRED for time series: track-specific reference date
h5writeatt(filename, track_path, 'reference_date', '20240101');

% Optional
h5writeatt(filename, track_path, 'polarization', 'VV');

%% 3. LOS vectors (replace with your actual LOS geometry)
% TODO: Replace with your actual LOS vectors
demo_shape = [1000, 1200];  % [rows, cols] - adjust to your data size

los_e = 0.35 + 0.10 * rand(demo_shape, 'single');
los_n = -0.05 + 0.10 * rand(demo_shape, 'single');
los_u = 0.75 + 0.10 * rand(demo_shape, 'single');

magnitude = sqrt(los_e.^2 + los_n.^2 + los_u.^2);
los_e = los_e ./ magnitude;
los_n = los_n ./ magnitude;
los_u = los_u ./ magnitude;

h5create(filename, [track_path '/line_of_sight_e'], demo_shape, ...
    'Datatype', 'single', 'ChunkSize', [100 100], 'Deflate', 6);
h5write(filename, [track_path '/line_of_sight_e'], los_e);
h5writeatt(filename, [track_path '/line_of_sight_e'], 'units', 'dimensionless');

h5create(filename, [track_path '/line_of_sight_n'], demo_shape, ...
    'Datatype', 'single', 'ChunkSize', [100 100], 'Deflate', 6);
h5write(filename, [track_path '/line_of_sight_n'], los_n);
h5writeatt(filename, [track_path '/line_of_sight_n'], 'units', 'dimensionless');

h5create(filename, [track_path '/line_of_sight_u'], demo_shape, ...
    'Datatype', 'single', 'ChunkSize', [100 100], 'Deflate', 6);
h5write(filename, [track_path '/line_of_sight_u'], los_u);
h5writeatt(filename, [track_path '/line_of_sight_u'], 'units', 'dimensionless');

%% 4. Displacement time series (replace with your actual displacements)
dates = {'20240101', '20240113', '20240125', '20240208'};

fprintf('Creating time series with %d dates...\n', length(dates));

for i = 1:length(dates)
    date_str = dates{i};
    
    % TODO: Replace this synthetic data with your actual displacement data
    if i == 1
        % Reference date: all zeros
        disp_data = zeros(demo_shape, 'single');
    else
        % Cumulative displacement in meters
        % This is synthetic - replace with your actual time series data
        disp_data = single(randn(demo_shape) * 0.01 * (i-1));
    end
    
    % Write dataset: dLOS_YYYYMMDD (note: directly under track, not in subgroups)
    dataset_path = [track_path '/dLOS_' date_str];
    h5create(filename, dataset_path, demo_shape, ...
        'Datatype', 'single', 'ChunkSize', [100 100], 'Deflate', 6);
    h5write(filename, dataset_path, disp_data);
    
    % Dataset attributes
    h5writeatt(filename, dataset_path, 'units', 'meters');  % Note: meters, not radians!
    h5writeatt(filename, dataset_path, 'acquisition_date', date_str);
    h5writeatt(filename, dataset_path, 'reference_date', '20240101');
    
    fprintf('  Added dLOS_%s\n', date_str);
end

fprintf('\nāœ“ Created %s\n', filename);
fprintf('  Explore with: h5disp(''%s'')\n\n', filename);
fprintf('NOTE: This file contains synthetic demo data.\n');
fprintf('Replace the displacement generation with your actual time series data.\n');

7.2.3 Reading HDF5 Files

Helper script to read and display InSAR HDF5 file contents.

example_read_insar.m
%% Example: Reading InSAR HDF5 Files
% Demonstrates how to read different product types

clear; clc;

% Adjust to your actual filename
filename = 'example_timeseries.h5';

%% 1. Read root metadata
proc_type = h5readatt(filename, '/', 'processing_type');
fprintf('Processing type: %s\n', proc_type);

%% 2. Read track metadata
track = 'ALOS2_073_A';  % Adjust to your track name
platform = h5readatt(filename, ['/' track], 'platform');
wavelength = h5readatt(filename, ['/' track], 'wavelength');
fprintf('Platform: %s, Wavelength: %.4f m\n', platform, wavelength);

%% 3. Read LOS vectors
los_e = h5read(filename, ['/' track '/line_of_sight_e']);
los_n = h5read(filename, ['/' track '/line_of_sight_n']);
los_u = h5read(filename, ['/' track '/line_of_sight_u']);
fprintf('LOS vectors size: %s\n', mat2str(size(los_e)));

%% 4. Read data based on product type
if strcmp(proc_type, 'INTERFEROGRAM')
    fprintf('\nReading interferogram...\n');
    
    % Read specific interferogram (adjust dates to your data)
    date_pair = '20240101_20240113';
    unwrapped = h5read(filename, ['/' track '/' date_pair '/unwrapped_interferogram']);
    fprintf('  Unwrapped phase range: [%.2f, %.2f] radians\n', ...
        min(unwrapped(:)), max(unwrapped(:)));
    
    % Visualize
    figure;
    imagesc(unwrapped);
    colorbar;
    title('Unwrapped Phase (radians)');
    axis equal tight;
    
elseif strcmp(proc_type, 'DISP. TIME SERIES')
    fprintf('\nReading time series...\n');
    
    % Get reference date (REQUIRED at track level)
    ref_date = h5readatt(filename, ['/' track], 'reference_date');
    fprintf('  Reference date: %s\n', ref_date);
    
    % Read specific date (adjust to your data)
    date_to_read = '20240113';
    disp_data = h5read(filename, ['/' track '/dLOS_' date_to_read]);
    fprintf('  Displacement range: [%.4f, %.4f] m\n', ...
        min(disp_data(:)), max(disp_data(:)));
    
    % Visualize
    figure;
    imagesc(disp_data);
    colorbar;
    title(sprintf('LOS Displacement: %s (meters)', date_to_read));
    axis equal tight;
    
elseif strcmp(proc_type, 'LOS_VELOCITY')
    fprintf('\nReading velocity...\n');
    
    velocity = h5read(filename, ['/' track '/velocity']);
    fprintf('  Velocity range: [%.4f, %.4f] m/year\n', ...
        min(velocity(:)), max(velocity(:)));
    
    % Visualize
    figure;
    imagesc(velocity);
    colorbar;
    title('LOS Velocity (m/year)');
    axis equal tight;
end

fprintf('\nāœ“ File read successfully\n');

šŸ’” Adapting MATLAB Examples for Your Data:

  • Replace demo data generation: Look for % TODO: comments marking sections where you should load your actual data
  • Update metadata: Change platform names, orbits, dates, and other metadata to match your processing
  • Adjust array sizes: Change demo_shape = [1000, 1200] to your actual data dimensions
  • Add multiple tracks: Duplicate the track sections with different track names and geometries
  • Include all dates: Add more date-pair groups for interferograms or expand the dates cell array for time series
  • Load your data: Use imread, geotiffread, load, or other methods to read your processed results