I.hyper: toolset for hyperspectral data processing in GRASS

Hi,

I was testing thoroughly the dev version and for now everything works well except PRISMA and Tanager basic imports (Tanager ortho works) plus there is a bug in i.hyper.composite, but we located it. What has to be done after those fixes: export and import of hyperspectral cube in h5 and Zarr with metadata retrieve from the GRASS one (so we can finish the circle). I will update you when it’s tested and done.

Cheers,

Alen

1 Like

Sooo, we are moving towards the end of this first major update.

I have fixed all the mentioned issues and added import and export for hyperspectral data in a format similar to r.pack (gzip). A PRISMA scene from this format imports in about 1 second :))

All PRISMA products are now supported, including non-orthorectified ones, using resampling. This part still needs further discussion.

Tanager BASIC is not yet fixed. The import matrix currently introduces two extra rows, but this will be addressed next plus compatibility with GRASS <8.5 (region parsing.

In the meantime, you can check the i.hyper-dev branch and verify whether the metadata meets the requirements.

After fixing Tanager BASIC, we still need to run additional tests, review the documentation, precommit the code, and then proceed with a pull request. Weeehaaaaa

https://github.com/mazingaro/grass-addons/tree/i.hyper-dev

1 Like

Hi @mazingaro , Which rev version holds the metadata revamp?

in i.hyper.import, it seems that enmap.py does not import the illuminationAzimuthAngle and the illuminationZenithAngle (resp. SunAA and SunZA) from the EnMap XML. From prisma.py, PRISMA HE5 Sun ZA, SunAA and Acquisition_Start_Time are present but not imported. Can you try to look if you can load them and store them in the metadata storage so I can get to them for atcorr. thanks

Hi @ychemin

we have been using this EnMap sample data for testing: Example Data Products - EnMAP and there was no illuminationAzimuthAngle/illuminationZenithAngle field in XML so I’ve added it just now - I hope it will do.

This is the summary how the i.hyper.metadata works now:

EnMAP - target metadata field (in hyper.json) extended_metadata.geometry.sun_zenith_deg

When importing product addon searches in this order:
- illuminationZenithAngle/center
- illuminationZenithAngle
- sunZenithAngle/center
- sunZenithAngle
- fallback: 90 - sunElevationAngle/center (or 90 - sunElevationAngle)

---

EnMAP - target metadata field (in hyper.json) extended_metadata.geometry.sun_azimuth_deg

When importing product addon searches in this order:
- sunAzimuthAngle/center
- sunAzimuthAngle
- illuminationAzimuthAngle/center
- illuminationAzimuthAngle

===

PRISMA - target metadata field (in hyper.json) extended_metadata.geometry.sun_zenith_deg

When importing product addon searches for:
- Sun_zenith_angle

---

PRISMA - target metadata field (in hyper.json) extended_metadata.geometry.sun_azimuth_deg

When importing product addon searches for:
- Sun_azimuth_angle

===

TANAGER - target metadata field (in hyper.json) extended_metadata.geometry.sun_zenith_deg

When importing product addon reads:

- /HDFEOS/SWATHS/HYP/Data Fields/sun_zenith (SWATH layout), or
- /HDFEOS/GRIDS/HYP/Data Fields/sun_zenith (GRIDS layout)

---

TANAGER - target metadata field (in hyper.json) extended_metadata.geometry.sun_azimuth_deg
When importing product addon reads:

- /HDFEOS/SWATHS/HYP/Data Fields/sun_azimuth (SWATH layout), or
- /HDFEOS/GRIDS/HYP/Data Fields/sun_azimuth (GRIDS layout)

And for acquisition start time the rules are like this:

EnMAP - target metadata field (in hyper.json) extended_metadata.acquisition.start_time_utc

When importing product addon searches (effective order):
- specific/datatakeStart
- datatakeStart
- base/temporalCoverage/startTime
- temporalCoverage/startTime
- fallback: startTime (generic XML field)

===

PRISMA - target metadata field (in hyper.json) extended_metadata.acquisition.start_time_utc

When importing product addon searches attributes in this order:
- Product_StartTime
- Acquisition_Start_Time (alias)

===

TANAGER - target metadata field (in hyper.json) extended_metadata.acquisition.start_time_utc

When importing product addon derives it from time dataset (minimum valid value):
.../Data Fields/time (preferred), 
else
.../Geolocation Fields/Time

(where ... is /HDFEOS/SWATHS/HYP or /HDFEOS/GRIDS/HYP depending on layout)

Here are some tests I have been doing and the resulting metadata output for the respective fields:

(Sun Apr 12 18:34:09 2026)                                                      
i.hyper.import input=/home/tomazz/work/metadata/dims_op_oc_oc-en_701541677_1/ENMAP.HSI.L2A/ENMAP01-____L2A-DT0000079358_20240620T101839Z_001_V010402_20240622T172015Z/ENMAP01-____L2A-DT00000 product=enmap output=enmap_test composites=rgb,cir,swir_agriculture
Importing product: enmap
Generated composite raster: enmap_test_rgb
Generated composite raster: enmap_test_cir
Generated composite raster: enmap_test_swir_agriculture
(Sun Apr 12 18:39:56 2026) Command finished (5 min 46 sec)                      
(Sun Apr 12 18:40:51 2026)                                                      
i.hyper.import input=/home/tomazz/work/metadata/PRS_L2D_STD_20240430101357_20240430101401_0001.he5 product=prisma output=p_l2d composites=rgb,cir,swir_agriculture,swir_geology
Importing product: prisma
Loading floating point  data with 4  bytes ...  (1279x1244x234)
Created 3D raster with all bands: p_l2d (234 slices).
Generated composite raster: p_l2d_rgb
Generated composite raster: p_l2d_cir
Generated composite raster: p_l2d_swir_agriculture
Generated composite raster: p_l2d_swir_geology
(Sun Apr 12 18:43:14 2026) Command finished (2 min 22 sec)                      
(Sun Apr 12 18:49:05 2026)                                                      
i.hyper.import input=/home/tomazz/work/metadata/20250223_165542_16_4001_ortho_sr_hdf5.h5 product=tanager output=tan_ortho_sr composites=rgb,cir,swir_agriculture,swir_geology
Importing product: tanager
Loading floating point  data with 4  bytes ...  (781x659x426)
Created 3D raster with all bands: tan_ortho_sr (426 slices).
Generated composite raster: tan_ortho_sr_rgb
Generated composite raster: tan_ortho_sr_cir
Generated composite raster: tan_ortho_sr_swir_agriculture
Generated composite raster: tan_ortho_sr_swir_geology
(Sun Apr 12 18:50:42 2026) Command finished (1 min 36 sec)                      
(Sun Apr 12 18:52:55 2026)                                                      
i.hyper.import input=/home/tomazz/work/metadata/20250223_165542_16_4001_basic_sr_hdf5.h5 product=tanager output=tan_basic_sr composites=rgb,cir,swir_agriculture,swir_geology
Importing product: tanager
Loading floating point  data with 4  bytes ...  (781x659x426)
Created 3D raster with all bands: tan_basic_sr (426 slices).
Generated composite raster: tan_basic_sr_rgb
Generated composite raster: tan_basic_sr_cir
Generated composite raster: tan_basic_sr_swir_agriculture
Generated composite raster: tan_basic_sr_swir_geology
(Sun Apr 12 18:54:54 2026) Command finished (1 min 59 sec)                      
(Sun Apr 12 18:56:48 2026)                                                      
i.hyper.import input=/home/tomazz/work/metadata/20250606_090504_75_4001_basic_radiance.h5 product=tanager output=tan_basic_radiance composites=rgb,cir,swir_agriculture,swir_geology
Importing product: tanager
Loading floating point  data with 4  bytes ...  (766x661x426)
Created 3D raster with all bands: tan_basic_radiance (426 slices).
Generated composite raster: tan_basic_radiance_rgb
Generated composite raster: tan_basic_radiance_cir
Generated composite raster: tan_basic_radiance_swir_agriculture
Generated composite raster: tan_basic_radiance_swir_geology
(Sun Apr 12 18:58:50 2026) Command finished (2 min 2 sec)                       
(Sun Apr 12 19:01:43 2026)                                                      
i.hyper.metadata map=enmap_test operation=extended format=text extended_select=acquisition,geometry
{
  "acquisition": {
    "start_time_utc": "2024-06-20T10:18:39.026423Z",
    "end_time_utc": "2024-06-20T10:18:47.926299Z",
    "center_latitude_deg": 46.400604675,
    "center_longitude_deg": 15.25038615,
    "day_of_year": 172,
    "line_time_summary": {
      "count": 2048,
      "min": "2024-06-20T10:18:39.023659Z",
      "max": "2024-06-20T10:18:43.474167Z",
      "step_seconds": 0.0021741612115290807
    }
  },
  "geometry": {
    "sun_zenith_deg": 24.475720999999993,
    "sun_azimuth_deg": 156.193067,
    "view_zenith_deg": 21.917226460841594,
    "view_azimuth_deg": 14.11674159,
    "relative_azimuth_deg": 142.07632541,
    "sensor_altitude_m": 651613.09,
    "jitter_summary": {
      "count": 1024,
      "min": 4.04085398835e-05,
      "max": 7.69989500143e-05,
      "mean": 5.870470818153581e-05
    }
  }
}
(Sun Apr 12 19:01:44 2026) Command finished (0 sec)                             
(Sun Apr 12 19:01:58 2026)                                                      
i.hyper.metadata map=p_l2d operation=extended format=text extended_select=acquisition,geometry
{
  "acquisition": {
    "start_time_utc": "2024-04-30T10:13:57.198999Z",
    "end_time_utc": "2024-04-30T10:14:01.504690Z",
    "center_latitude_deg": 45.82672119140625,
    "center_longitude_deg": 13.363563537597656,
    "day_of_year": 121,
    "line_time_summary": {
      "count": 1000,
      "min": 8886.426356370139,
      "max": 8886.426406204513,
      "step": 4.9884258553285986e-08
    }
  },
  "geometry": {
    "sun_zenith_deg": 32.53619384765625,
    "sun_azimuth_deg": 157.25375366210938,
    "view_zenith_deg": 5.954222622878282,
    "view_azimuth_deg": 238.50083990506,
    "relative_azimuth_deg": 81.2470862429506
  }
}
(Sun Apr 12 19:01:59 2026) Command finished (0 sec)                             
(Sun Apr 12 19:02:15 2026)                                                      
i.hyper.metadata map=tan_basic_radiance operation=extended format=text extended_select=acquisition,geometry
{
  "acquisition": {
    "start_time_utc": "2025-06-06T09:05:04.757094Z",
    "end_time_utc": "2025-06-06T09:05:08.905594Z",
    "center_latitude_deg": 27.656775667966045,
    "center_longitude_deg": 33.65440077133463,
    "day_of_year": 157,
    "line_time_summary": {
      "count": 501,
      "min": "2025-06-06T09:05:04.757094Z",
      "max": "2025-06-06T09:05:08.905594Z",
      "step_seconds": 0.008297000408172607
    }
  },
  "geometry": {
    "sun_zenith_deg": 10.113162424156473,
    "sun_azimuth_deg": 117.21471823718122,
    "view_zenith_deg": 3.5567660789234603,
    "view_azimuth_deg": 104.32959808882576,
    "relative_azimuth_deg": 12.88512014835544,
    "sensor_to_ground_path_length_m": {
      "value": 441254.9664557828,
      "form": "map_mean",
      "source": "sensor_to_ground_path_length"
    }
  }
}
(Sun Apr 12 19:02:16 2026) Command finished (0 sec)                             
(Sun Apr 12 19:02:34 2026)                                                      
i.hyper.metadata map=tan_basic_sr operation=extended format=text extended_select=acquisition,geometry
{
  "acquisition": {
    "start_time_utc": "2025-02-23T16:55:42.165094Z",
    "end_time_utc": "2025-02-23T16:55:46.313594Z",
    "center_latitude_deg": 13.370805874868948,
    "center_longitude_deg": -88.42770494742597,
    "day_of_year": 54,
    "line_time_summary": {
      "count": 501,
      "min": "2025-02-23T16:55:42.165094Z",
      "max": "2025-02-23T16:55:46.313594Z",
      "step_seconds": 0.008297000408172607
    }
  },
  "geometry": {
    "sun_zenith_deg": 28.9422735513726,
    "sun_azimuth_deg": 141.4744229545215,
    "view_zenith_deg": 10.806664064940948,
    "view_azimuth_deg": 272.64278231420786,
    "relative_azimuth_deg": 131.1683593596864,
    "sensor_to_ground_path_length_m": {
      "value": 443403.7851386395,
      "form": "map_mean",
      "source": "sensor_to_ground_path_length"
    }
  }
}
(Sun Apr 12 19:02:34 2026) Command finished (0 sec)                             
(Sun Apr 12 19:02:50 2026)                                                      
i.hyper.metadata map=tan_ortho_sr operation=extended format=text extended_select=acquisition,geometry
{
  "acquisition": {
    "start_time_utc": "2025-02-23T16:55:42.165094Z",
    "end_time_utc": "2025-02-23T16:55:46.313594Z",
    "center_latitude_deg": 13.370870985580325,
    "center_longitude_deg": -88.42762613973778,
    "day_of_year": 54,
    "line_time_summary": {
      "count": 371359,
      "min": "2025-02-23T16:55:42.165094Z",
      "max": "2025-02-23T16:55:46.313594Z",
      "step_seconds": 1.1171161531692609e-05
    }
  },
  "geometry": {
    "sun_zenith_deg": 28.942143427336916,
    "sun_azimuth_deg": 141.47464663775682,
    "view_zenith_deg": 10.80897546287616,
    "view_azimuth_deg": 272.6441305658414,
    "relative_azimuth_deg": 131.16948392808456,
    "sensor_to_ground_path_length_m": {
      "value": 443407.58558329,
      "form": "map_mean",
      "source": "sensor_to_ground_path_length"
    }
  }
}
(Sun Apr 12 19:02:51 2026) Command finished (0 sec)