LCOV - code coverage report
Current view: top level - frmts/hdf5 - hdf5eosparser.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 390 415 94.0 %
Date: 2026-05-07 11:45:54 Functions: 11 11 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Hierarchical Data Format Release 5 (HDF5)
       4             :  * Purpose:  Implementation of HDF5 HDFEOS parser
       5             :  * Author:   Even Rouault
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2023, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #ifdef _POSIX_C_SOURCE
      14             : #undef _POSIX_C_SOURCE
      15             : #endif
      16             : 
      17             : #include "cpl_error.h"
      18             : #include "nasakeywordhandler.h"
      19             : 
      20             : #include "hdf5eosparser.h"
      21             : 
      22             : #include <cstring>
      23             : #include <utility>
      24             : 
      25             : /************************************************************************/
      26             : /*                             HasHDFEOS()                              */
      27             : /************************************************************************/
      28             : 
      29         484 : bool HDF5EOSParser::HasHDFEOS(hid_t hRoot)
      30             : {
      31         484 :     hsize_t numObjs = 0;
      32         484 :     H5Gget_num_objs(hRoot, &numObjs);
      33         484 :     bool bFound = false;
      34        1514 :     for (hsize_t i = 0; i < numObjs; ++i)
      35             :     {
      36             :         char szName[128];
      37             :         ssize_t nLen =
      38        1062 :             H5Gget_objname_by_idx(hRoot, i, szName, sizeof(szName) - 1);
      39        1062 :         if (nLen > 0)
      40             :         {
      41        1062 :             szName[nLen] = 0;
      42        1062 :             if (strcmp(szName, "HDFEOS INFORMATION") == 0)
      43             :             {
      44          32 :                 bFound = true;
      45          32 :                 break;
      46             :             }
      47             :         }
      48             :     }
      49         484 :     if (!bFound)
      50         452 :         return false;
      51             : 
      52             :     H5G_stat_t oStatbuf;
      53          32 :     if (H5Gget_objinfo(hRoot, "HDFEOS INFORMATION", false, &oStatbuf) < 0)
      54           0 :         return false;
      55             : 
      56          32 :     auto hHDFEOSInformation = H5Gopen(hRoot, "HDFEOS INFORMATION");
      57          32 :     if (hHDFEOSInformation < 0)
      58             :     {
      59           0 :         return false;
      60             :     }
      61          32 :     H5Gclose(hHDFEOSInformation);
      62          32 :     return true;
      63             : }
      64             : 
      65             : /************************************************************************/
      66             : /*                               Parse()                                */
      67             : /************************************************************************/
      68             : 
      69          32 : bool HDF5EOSParser::Parse(hid_t hRoot)
      70             : {
      71          32 :     auto hHDFEOSInformation = H5Gopen(hRoot, "HDFEOS INFORMATION");
      72          32 :     if (hHDFEOSInformation < 0)
      73             :     {
      74           0 :         return false;
      75             :     }
      76             : 
      77          32 :     const hid_t hArrayId = H5Dopen(hHDFEOSInformation, "StructMetadata.0");
      78          32 :     if (hArrayId < 0)
      79             :     {
      80           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find StructMetadata.0");
      81           0 :         H5Gclose(hHDFEOSInformation);
      82           0 :         return false;
      83             :     }
      84             : 
      85          32 :     const hid_t hAttrSpace = H5Dget_space(hArrayId);
      86          32 :     const hid_t hAttrTypeID = H5Dget_type(hArrayId);
      87             :     const hid_t hAttrNativeType =
      88          32 :         H5Tget_native_type(hAttrTypeID, H5T_DIR_DEFAULT);
      89             : 
      90             :     // Fetch StructMetadata.0 content in a std::string
      91          64 :     std::string osResult;
      92          32 :     if (H5Tget_class(hAttrNativeType) == H5T_STRING &&
      93          64 :         !H5Tis_variable_str(hAttrNativeType) &&
      94          32 :         H5Sget_simple_extent_ndims(hAttrSpace) == 0)
      95             :     {
      96          32 :         const auto nSize = H5Tget_size(hAttrNativeType);
      97          32 :         if (nSize > 10 * 1024 * 1024)
      98             :         {
      99           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     100             :                      "Too large HDFEOS INFORMATION.StructMetadata.0");
     101             :         }
     102             :         else
     103             :         {
     104          32 :             osResult.resize(nSize);
     105          32 :             H5Dread(hArrayId, hAttrNativeType, H5S_ALL, hAttrSpace, H5P_DEFAULT,
     106          32 :                     &osResult[0]);
     107             :         }
     108             :     }
     109             :     else
     110             :     {
     111           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     112             :                  "HDFEOS INFORMATION.StructMetadata.0 not of type string");
     113             :     }
     114          32 :     H5Sclose(hAttrSpace);
     115          32 :     H5Tclose(hAttrNativeType);
     116          32 :     H5Tclose(hAttrTypeID);
     117             : 
     118          32 :     H5Dclose(hArrayId);
     119          32 :     H5Gclose(hHDFEOSInformation);
     120             : 
     121          32 :     if (osResult.empty())
     122           0 :         return false;
     123             : 
     124             :     // Parse StructMetadata.0 with NASAKeywordHandler
     125          64 :     NASAKeywordHandler oKWHandler;
     126             : #ifdef DEBUG
     127          32 :     CPLDebug("HDF5EOS", "%s", osResult.c_str());
     128             : #endif
     129          32 :     if (!oKWHandler.Parse(osResult.c_str()))
     130             :     {
     131           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     132             :                  "Cannot parse HDFEOS INFORMATION.StructMetadata.0 with "
     133             :                  "NASAKeywordHandler");
     134           0 :         return false;
     135             :     }
     136             : 
     137          64 :     auto oJsonRoot = oKWHandler.GetJsonObject();
     138          96 :     auto oGridStructure = oJsonRoot.GetObj("GridStructure");
     139          64 :     auto oSwathStructure = oJsonRoot.GetObj("SwathStructure");
     140          32 :     bool bOK = false;
     141             :     // An empty
     142             :     // GROUP=GridStructure
     143             :     // END_GROUP=GridStructure
     144             :     // will generate 2 keys (_type and END_GROUP)
     145          32 :     if (oGridStructure.IsValid() && oGridStructure.GetChildren().size() > 2)
     146             :     {
     147          16 :         bOK = true;
     148          16 :         m_eDataModel = DataModel::GRID;
     149          16 :         ParseGridStructure(oGridStructure);
     150             :     }
     151          32 :     else if (oSwathStructure.IsValid() &&
     152          32 :              oSwathStructure.GetChildren().size() > 2)
     153             :     {
     154          16 :         bOK = true;
     155          16 :         m_eDataModel = DataModel::SWATH;
     156          16 :         ParseSwathStructure(oSwathStructure);
     157             :     }
     158             : 
     159          32 :     return bOK;
     160             : }
     161             : 
     162             : /************************************************************************/
     163             : /*                       GetGTCPProjectionCode()                        */
     164             : /************************************************************************/
     165             : 
     166          17 : static int GetGTCPProjectionCode(const std::string &osProjection)
     167             : {
     168          17 :     const char *const apszGCTPProjections[] = {
     169             :         "HE5_GCTP_GEO",    "HE5_GCTP_UTM",    "HE5_GCTP_SPCS",
     170             :         "HE5_GCTP_ALBERS", "HE5_GCTP_LAMCC",  "HE5_GCTP_MERCAT",
     171             :         "HE5_GCTP_PS",     "HE5_GCTP_POLYC",  "HE5_GCTP_EQUIDC",
     172             :         "HE5_GCTP_TM",     "HE5_GCTP_STEREO", "HE5_GCTP_LAMAZ",
     173             :         "HE5_GCTP_AZMEQD", "HE5_GCTP_GNOMON", "HE5_GCTP_ORTHO",
     174             :         "HE5_GCTP_GVNSP",  "HE5_GCTP_SNSOID", "HE5_GCTP_EQRECT",
     175             :         "HE5_GCTP_MILLER", "HE5_GCTP_VGRINT", "HE5_GCTP_HOM",
     176             :         "HE5_GCTP_ROBIN",  "HE5_GCTP_SOM",    "HE5_GCTP_ALASKA",
     177             :         "HE5_GCTP_GOOD",   "HE5_GCTP_MOLL",   "HE5_GCTP_IMOLL",
     178             :         "HE5_GCTP_HAMMER", "HE5_GCTP_WAGIV",  "HE5_GCTP_WAGVII",
     179             :         "HE5_GCTP_OBLEQA"};
     180             :     // HE5_GCTP_CEA, HE5_GCTP_BCEA, HE5_GCTP_ISINUS not taken
     181             :     // into account.
     182         166 :     for (int i = 0; i < static_cast<int>(CPL_ARRAYSIZE(apszGCTPProjections));
     183             :          ++i)
     184             :     {
     185         166 :         if (osProjection == apszGCTPProjections[i])
     186             :         {
     187          17 :             return i;
     188             :         }
     189             :     }
     190           0 :     return -1;
     191             : }
     192             : 
     193             : /************************************************************************/
     194             : /*                         ParseGridStructure()                         */
     195             : /************************************************************************/
     196             : 
     197          16 : void HDF5EOSParser::ParseGridStructure(const CPLJSONObject &oGridStructure)
     198             : {
     199          65 :     for (const auto &oGrid : oGridStructure.GetChildren())
     200             :     {
     201          49 :         if (oGrid.GetType() == CPLJSONObject::Type::Object)
     202             :         {
     203          51 :             const auto osGridName = oGrid.GetString("GridName");
     204          51 :             const auto oDataFields = oGrid.GetObj("DataField");
     205          51 :             const auto oDimensions = oGrid.GetObj("Dimension");
     206          34 :             std::map<std::string, int> oMapDimensionNameToSize;
     207          34 :             auto poGridMetadata = std::make_unique<GridMetadata>();
     208          17 :             poGridMetadata->osGridName = osGridName;
     209          86 :             for (const auto &oDimension : oDimensions.GetChildren())
     210             :             {
     211          69 :                 if (oDimension.GetType() == CPLJSONObject::Type::Object)
     212             :                 {
     213             :                     std::string osDimensionName =
     214         105 :                         oDimension.GetString("DimensionName");
     215          35 :                     int nSize = oDimension.GetInteger("Size");
     216          35 :                     oMapDimensionNameToSize[osDimensionName] = nSize;
     217          70 :                     Dimension oDim;
     218          35 :                     oDim.osName = std::move(osDimensionName);
     219          35 :                     oDim.nSize = nSize;
     220          35 :                     poGridMetadata->aoDimensions.push_back(std::move(oDim));
     221             :                 }
     222             :             }
     223             : 
     224             :             // Happens for example for products following
     225             :             // AMSR-E/AMSR2 Unified L3 Daily 12.5 km Brightness Temperatures,
     226             :             // Sea Ice Concentration, Motion & Snow Depth Polar Grids
     227             :             // (https://nsidc.org/sites/default/files/au_si12-v001-userguide_1.pdf)
     228             :             // such as
     229             :             // https://n5eil01u.ecs.nsidc.org/AMSA/AU_SI12.001/2012.07.02/AMSR_U2_L3_SeaIce12km_B04_20120702.he5
     230          17 :             const int nXDim = oGrid.GetInteger("XDim", 0);
     231          17 :             const int nYDim = oGrid.GetInteger("YDim", 0);
     232          17 :             if (nXDim > 0 && nYDim > 0 &&
     233          39 :                 !cpl::contains(oMapDimensionNameToSize, "XDim") &&
     234           5 :                 !cpl::contains(oMapDimensionNameToSize, "YDim"))
     235             :             {
     236             :                 // Check that we have at least one data field with DimList=(YDim,XDim)
     237             :                 // property.
     238           5 :                 bool bHasDimListYDimXDim = false;
     239          15 :                 for (const auto &oDataField : oDataFields.GetChildren())
     240             :                 {
     241          10 :                     if (oDataField.GetType() == CPLJSONObject::Type::Object)
     242             :                     {
     243          10 :                         const auto oDimList = oDataField.GetArray("DimList");
     244           5 :                         if (oDimList.Size() == 2 &&
     245          15 :                             oDimList[0].ToString() == "YDim" &&
     246          10 :                             oDimList[1].ToString() == "XDim")
     247             :                         {
     248           5 :                             bHasDimListYDimXDim = true;
     249           5 :                             break;
     250             :                         }
     251             :                     }
     252             :                 }
     253           5 :                 if (bHasDimListYDimXDim)
     254             :                 {
     255             :                     {
     256          10 :                         std::string osDimensionName("YDim");
     257           5 :                         oMapDimensionNameToSize[osDimensionName] = nYDim;
     258          10 :                         Dimension oDim;
     259           5 :                         oDim.osName = std::move(osDimensionName);
     260           5 :                         oDim.nSize = nYDim;
     261           5 :                         poGridMetadata->aoDimensions.push_back(std::move(oDim));
     262             :                     }
     263             :                     {
     264          10 :                         std::string osDimensionName("XDim");
     265           5 :                         oMapDimensionNameToSize[osDimensionName] = nXDim;
     266          10 :                         Dimension oDim;
     267           5 :                         oDim.osName = std::move(osDimensionName);
     268           5 :                         oDim.nSize = nXDim;
     269           5 :                         poGridMetadata->aoDimensions.push_back(std::move(oDim));
     270             :                     }
     271             :                 }
     272             :             }
     273             : 
     274          17 :             poGridMetadata->osProjection = oGrid.GetString("Projection");
     275          34 :             poGridMetadata->nProjCode =
     276          17 :                 GetGTCPProjectionCode(poGridMetadata->osProjection);
     277          17 :             poGridMetadata->osGridOrigin = oGrid.GetString("GridOrigin");
     278          17 :             poGridMetadata->nZone = oGrid.GetInteger("ZoneCode", -1);
     279          17 :             poGridMetadata->nSphereCode = oGrid.GetInteger("SphereCode", -1);
     280             : 
     281          51 :             const auto oProjParams = oGrid.GetArray("ProjParams");
     282         134 :             for (int j = 0; j < oProjParams.Size(); ++j)
     283         117 :                 poGridMetadata->adfProjParams.push_back(
     284         117 :                     oProjParams[j].ToDouble());
     285             : 
     286             :             const auto oUpperLeftPointMtrs =
     287          51 :                 oGrid.GetArray("UpperLeftPointMtrs");
     288          51 :             for (int j = 0; j < oUpperLeftPointMtrs.Size(); ++j)
     289          34 :                 poGridMetadata->adfUpperLeftPointMeters.push_back(
     290          34 :                     oUpperLeftPointMtrs[j].ToDouble());
     291             : 
     292          51 :             const auto oLowerRightMtrs = oGrid.GetArray("LowerRightMtrs");
     293          51 :             for (int j = 0; j < oLowerRightMtrs.Size(); ++j)
     294          34 :                 poGridMetadata->adfLowerRightPointMeters.push_back(
     295          34 :                     oLowerRightMtrs[j].ToDouble());
     296             : 
     297          17 :             m_oMapGridNameToGridMetadata[osGridName] =
     298          34 :                 std::move(poGridMetadata);
     299             :             const auto poGridMetadataRef =
     300          17 :                 m_oMapGridNameToGridMetadata[osGridName].get();
     301             : 
     302          80 :             for (const auto &oDataField : oDataFields.GetChildren())
     303             :             {
     304          63 :                 if (oDataField.GetType() == CPLJSONObject::Type::Object)
     305             :                 {
     306             :                     const auto osDataFieldName =
     307          87 :                         oDataField.GetString("DataFieldName");
     308          87 :                     const auto oDimList = oDataField.GetArray("DimList");
     309          58 :                     GridDataFieldMetadata oDataFieldMetadata;
     310          29 :                     bool bValid = oDimList.Size() > 0;
     311         101 :                     for (int j = 0; j < oDimList.Size(); ++j)
     312             :                     {
     313         144 :                         std::string osDimensionName = oDimList[j].ToString();
     314             :                         const auto oIter = oMapDimensionNameToSize.find(
     315          72 :                             osDimensionName.c_str());
     316          72 :                         if (oIter == oMapDimensionNameToSize.end())
     317             :                         {
     318           0 :                             bValid = false;
     319           0 :                             break;
     320             :                         }
     321         144 :                         Dimension oDim;
     322          72 :                         oDim.osName = std::move(osDimensionName);
     323          72 :                         oDim.nSize = oIter->second;
     324          72 :                         oDataFieldMetadata.aoDimensions.push_back(
     325          72 :                             std::move(oDim));
     326             :                     }
     327          29 :                     if (bValid)
     328             :                     {
     329          29 :                         oDataFieldMetadata.poGridMetadata = poGridMetadataRef;
     330             :                         m_oMapSubdatasetNameToGridDataFieldMetadata
     331          58 :                             ["//HDFEOS/GRIDS/" + osGridName + "/Data_Fields/" +
     332          58 :                              osDataFieldName] = std::move(oDataFieldMetadata);
     333             :                     }
     334             :                 }
     335             :             }
     336             :         }
     337             :     }
     338          16 : }
     339             : 
     340             : /************************************************************************/
     341             : /*                          GetGridMetadata()                           */
     342             : /************************************************************************/
     343             : 
     344          12 : bool HDF5EOSParser::GetGridMetadata(const std::string &osGridName,
     345             :                                     GridMetadata &gridMetadataOut) const
     346             : {
     347          12 :     const auto oIter = m_oMapGridNameToGridMetadata.find(osGridName);
     348          12 :     if (oIter == m_oMapGridNameToGridMetadata.end())
     349           6 :         return false;
     350           6 :     gridMetadataOut = *(oIter->second);
     351           6 :     return true;
     352             : }
     353             : 
     354             : /************************************************************************/
     355             : /*                      GetGridDataFieldMetadata()                      */
     356             : /************************************************************************/
     357             : 
     358          13 : bool HDF5EOSParser::GetGridDataFieldMetadata(
     359             :     const char *pszSubdatasetName,
     360             :     GridDataFieldMetadata &gridDataFieldMetadataOut) const
     361             : {
     362             :     const auto oIter =
     363          13 :         m_oMapSubdatasetNameToGridDataFieldMetadata.find(pszSubdatasetName);
     364          13 :     if (oIter == m_oMapSubdatasetNameToGridDataFieldMetadata.end())
     365           3 :         return false;
     366          10 :     gridDataFieldMetadataOut = oIter->second;
     367          10 :     return true;
     368             : }
     369             : 
     370             : /************************************************************************/
     371             : /*                        ParseSwathStructure()                         */
     372             : /************************************************************************/
     373             : 
     374          16 : void HDF5EOSParser::ParseSwathStructure(const CPLJSONObject &oSwathStructure)
     375             : {
     376          64 :     for (const auto &oSwath : oSwathStructure.GetChildren())
     377             :     {
     378          48 :         if (oSwath.GetType() == CPLJSONObject::Type::Object)
     379             :         {
     380          48 :             const auto osSwathName = oSwath.GetString("SwathName");
     381             : 
     382          48 :             const auto oDimensions = oSwath.GetObj("Dimension");
     383          32 :             std::map<std::string, int> oMapDimensionNameToSize;
     384          32 :             auto poSwathMetadata = std::make_unique<SwathMetadata>();
     385          16 :             poSwathMetadata->osSwathName = osSwathName;
     386         132 :             for (const auto &oDimension : oDimensions.GetChildren())
     387             :             {
     388         116 :                 if (oDimension.GetType() == CPLJSONObject::Type::Object)
     389             :                 {
     390             :                     auto osDimensionName =
     391         252 :                         oDimension.GetString("DimensionName");
     392          84 :                     int nSize = oDimension.GetInteger("Size");
     393          84 :                     oMapDimensionNameToSize[osDimensionName] = nSize;
     394         168 :                     Dimension oDim;
     395          84 :                     oDim.osName = std::move(osDimensionName);
     396          84 :                     oDim.nSize = nSize;
     397          84 :                     poSwathMetadata->aoDimensions.emplace_back(std::move(oDim));
     398             :                 }
     399             :             }
     400             : 
     401          16 :             m_oMapSwathNameToSwathMetadata[osSwathName] =
     402          32 :                 std::move(poSwathMetadata);
     403             :             const auto poSwathMetadataRef =
     404          16 :                 m_oMapSwathNameToSwathMetadata[osSwathName].get();
     405             : 
     406             :             struct DimensionMap
     407             :             {
     408             :                 std::string osGeoDimName{};
     409             :                 std::string osDataDimName{};
     410             :                 int nOffset = 0;
     411             :                 int nIncrement = 1;
     412             :             };
     413             : 
     414          32 :             std::vector<DimensionMap> aoDimensionMaps;
     415          32 :             std::map<std::string, std::string> oMapDataDimensionToGeoDimension;
     416             : 
     417          48 :             const auto jsonDimensionMaps = oSwath.GetObj("DimensionMap");
     418          60 :             for (const auto &jsonDimensionMap : jsonDimensionMaps.GetChildren())
     419             :             {
     420          44 :                 if (jsonDimensionMap.GetType() == CPLJSONObject::Type::Object)
     421             :                 {
     422          24 :                     DimensionMap oDimensionMap;
     423             :                     oDimensionMap.osGeoDimName =
     424          12 :                         jsonDimensionMap.GetString("GeoDimension");
     425             :                     oDimensionMap.osDataDimName =
     426          12 :                         jsonDimensionMap.GetString("DataDimension");
     427          12 :                     oDimensionMap.nOffset =
     428          12 :                         jsonDimensionMap.GetInteger("Offset", 0);
     429          12 :                     oDimensionMap.nIncrement =
     430          12 :                         jsonDimensionMap.GetInteger("Increment", 1);
     431             :                     oMapDataDimensionToGeoDimension[oDimensionMap
     432          12 :                                                         .osDataDimName] =
     433          12 :                         oDimensionMap.osGeoDimName;
     434          12 :                     aoDimensionMaps.emplace_back(oDimensionMap);
     435             :                 }
     436             :             }
     437             : 
     438          48 :             const auto oGeoFields = oSwath.GetObj("GeoField");
     439          32 :             std::vector<Dimension> aoLongitudeDimensions;
     440          32 :             std::vector<Dimension> aoLatitudeDimensions;
     441             : 
     442             :             // First pass to create dimensions
     443          96 :             for (const auto &oGeoField : oGeoFields.GetChildren())
     444             :             {
     445          80 :                 if (oGeoField.GetType() == CPLJSONObject::Type::Object)
     446             :                 {
     447         144 :                     auto osGeoFieldName = oGeoField.GetString("GeoFieldName");
     448         144 :                     auto oDimList = oGeoField.GetArray("DimList");
     449          48 :                     bool bValid = true;
     450          96 :                     std::vector<Dimension> aoDimensions;
     451         128 :                     for (int j = 0; j < oDimList.Size(); ++j)
     452             :                     {
     453         160 :                         const auto osDimensionName = oDimList[j].ToString();
     454             :                         const auto oIter = oMapDimensionNameToSize.find(
     455          80 :                             osDimensionName.c_str());
     456          80 :                         if (oIter == oMapDimensionNameToSize.end())
     457             :                         {
     458           0 :                             bValid = false;
     459           0 :                             break;
     460             :                         }
     461         160 :                         Dimension oDim;
     462          80 :                         oDim.osName = osDimensionName;
     463          80 :                         oDim.nSize = oIter->second;
     464          80 :                         aoDimensions.push_back(std::move(oDim));
     465          80 :                         if (oMapDataDimensionToGeoDimension.find(
     466          80 :                                 osDimensionName) ==
     467         160 :                             oMapDataDimensionToGeoDimension.end())
     468             :                         {
     469             :                             // Create a fake dimension map for this dim
     470          64 :                             DimensionMap oDimensionMap;
     471          32 :                             oDimensionMap.osGeoDimName = osDimensionName;
     472          32 :                             oDimensionMap.osDataDimName = osDimensionName;
     473          32 :                             oDimensionMap.nOffset = 0;
     474          32 :                             oDimensionMap.nIncrement = 1;
     475          32 :                             oMapDataDimensionToGeoDimension[osDimensionName] =
     476          32 :                                 osDimensionName;
     477          32 :                             aoDimensionMaps.emplace_back(oDimensionMap);
     478             :                         }
     479             :                     }
     480          48 :                     if (bValid)
     481             :                     {
     482          48 :                         if (osGeoFieldName == "Longitude")
     483          16 :                             aoLongitudeDimensions = std::move(aoDimensions);
     484          32 :                         else if (osGeoFieldName == "Latitude")
     485          16 :                             aoLatitudeDimensions = std::move(aoDimensions);
     486             :                     }
     487             :                 }
     488             :             }
     489             : 
     490             :             // Second pass to register field
     491          96 :             for (const auto &oGeoField : oGeoFields.GetChildren())
     492             :             {
     493          80 :                 if (oGeoField.GetType() == CPLJSONObject::Type::Object)
     494             :                 {
     495             :                     const auto osGeoFieldName =
     496         144 :                         oGeoField.GetString("GeoFieldName");
     497         144 :                     const auto oDimList = oGeoField.GetArray("DimList");
     498          48 :                     bool bValid = true;
     499          96 :                     SwathFieldMetadata oMetadata;
     500          48 :                     oMetadata.poSwathMetadata = poSwathMetadataRef;
     501         128 :                     for (int j = 0; j < oDimList.Size(); ++j)
     502             :                     {
     503         160 :                         const auto osDimensionName = oDimList[j].ToString();
     504             :                         const auto oIter = oMapDimensionNameToSize.find(
     505          80 :                             osDimensionName.c_str());
     506          80 :                         if (oIter == oMapDimensionNameToSize.end())
     507             :                         {
     508           0 :                             bValid = false;
     509           0 :                             break;
     510             :                         }
     511         160 :                         Dimension oDim;
     512          80 :                         oDim.osName = std::move(osDimensionName);
     513          80 :                         oDim.nSize = oIter->second;
     514          80 :                         oMetadata.aoDimensions.push_back(std::move(oDim));
     515             :                     }
     516          48 :                     if (bValid)
     517             :                     {
     518          80 :                         if (oMetadata.aoDimensions.size() >= 2 &&
     519          80 :                             aoLongitudeDimensions.size() == 2 &&
     520          32 :                             aoLongitudeDimensions == aoLatitudeDimensions)
     521             :                         {
     522          32 :                             int i = 0;
     523          64 :                             std::string osDataXDimName;
     524          64 :                             std::string osDataYDimName;
     525          96 :                             for (const auto &oDimSwath : oMetadata.aoDimensions)
     526             :                             {
     527             :                                 auto oIter =
     528             :                                     oMapDataDimensionToGeoDimension.find(
     529          64 :                                         oDimSwath.osName);
     530          64 :                                 if (oIter !=
     531         128 :                                     oMapDataDimensionToGeoDimension.end())
     532             :                                 {
     533          64 :                                     const auto &osGeoDimName = oIter->second;
     534          64 :                                     if (osGeoDimName ==
     535          64 :                                         aoLongitudeDimensions[0].osName)
     536             :                                     {
     537          32 :                                         osDataYDimName = oDimSwath.osName;
     538          32 :                                         oMetadata.iYDim = i;
     539             :                                     }
     540          32 :                                     else if (osGeoDimName ==
     541          32 :                                              aoLongitudeDimensions[1].osName)
     542             :                                     {
     543          32 :                                         osDataXDimName = oDimSwath.osName;
     544          32 :                                         oMetadata.iXDim = i;
     545             :                                     }
     546             :                                 }
     547             :                                 else
     548             :                                 {
     549           0 :                                     oMetadata.iOtherDim = i;
     550             :                                 }
     551          64 :                                 ++i;
     552             :                             }
     553          32 :                             if (oMetadata.iXDim >= 0 && oMetadata.iYDim >= 0)
     554             :                             {
     555             :                                 oMetadata.osLongitudeSubdataset =
     556          64 :                                     "//HDFEOS/SWATHS/" + osSwathName +
     557          32 :                                     "/Geolocation_Fields/Longitude";
     558             :                                 oMetadata.osLatitudeSubdataset =
     559          64 :                                     "//HDFEOS/SWATHS/" + osSwathName +
     560          32 :                                     "/Geolocation_Fields/Latitude";
     561             : 
     562         120 :                                 for (const auto &oDimMap : aoDimensionMaps)
     563             :                                 {
     564          88 :                                     if (oDimMap.osDataDimName == osDataYDimName)
     565             :                                     {
     566          32 :                                         oMetadata.nLineOffset = oDimMap.nOffset;
     567          32 :                                         oMetadata.nLineStep =
     568          32 :                                             oDimMap.nIncrement;
     569             :                                     }
     570          56 :                                     else if (oDimMap.osDataDimName ==
     571             :                                              osDataXDimName)
     572             :                                     {
     573          32 :                                         oMetadata.nPixelOffset =
     574          32 :                                             oDimMap.nOffset;
     575          32 :                                         oMetadata.nPixelStep =
     576          32 :                                             oDimMap.nIncrement;
     577             :                                     }
     578             :                                 }
     579             :                             }
     580             :                         }
     581             : 
     582             :                         const std::string osSubdatasetName =
     583          96 :                             "//HDFEOS/SWATHS/" + osSwathName +
     584          96 :                             "/Geolocation_Fields/" + osGeoFieldName;
     585             :                         m_oMapSubdatasetNameToSwathFieldMetadata
     586          48 :                             [osSubdatasetName] = std::move(oMetadata);
     587             :                     }
     588             :                 }
     589             :             }
     590             : 
     591          48 :             const auto oDataFields = oSwath.GetObj("DataField");
     592          94 :             for (const auto &oDataField : oDataFields.GetChildren())
     593             :             {
     594          78 :                 if (oDataField.GetType() == CPLJSONObject::Type::Object)
     595             :                 {
     596             :                     const auto osDataFieldName =
     597         138 :                         oDataField.GetString("DataFieldName");
     598         138 :                     const auto oDimList = oDataField.GetArray("DimList");
     599          92 :                     SwathFieldMetadata oMetadata;
     600          46 :                     oMetadata.poSwathMetadata = poSwathMetadataRef;
     601          46 :                     bool bValid = oDimList.Size() > 0;
     602         130 :                     for (int j = 0; j < oDimList.Size(); ++j)
     603             :                     {
     604         168 :                         std::string osDimensionName = oDimList[j].ToString();
     605             :                         const auto oIter = oMapDimensionNameToSize.find(
     606          84 :                             osDimensionName.c_str());
     607          84 :                         if (oIter == oMapDimensionNameToSize.end())
     608             :                         {
     609           0 :                             bValid = false;
     610           0 :                             break;
     611             :                         }
     612         168 :                         Dimension oDim;
     613          84 :                         oDim.osName = std::move(osDimensionName);
     614          84 :                         oDim.nSize = oIter->second;
     615          84 :                         oMetadata.aoDimensions.push_back(std::move(oDim));
     616             :                     }
     617          46 :                     if (bValid)
     618             :                     {
     619          68 :                         if (oMetadata.aoDimensions.size() >= 2 &&
     620          68 :                             aoLongitudeDimensions.size() == 2 &&
     621          22 :                             aoLongitudeDimensions == aoLatitudeDimensions)
     622             :                         {
     623          22 :                             int i = 0;
     624          44 :                             std::string osDataXDimName;
     625          44 :                             std::string osDataYDimName;
     626          82 :                             for (const auto &oDimSwath : oMetadata.aoDimensions)
     627             :                             {
     628             :                                 auto oIter =
     629             :                                     oMapDataDimensionToGeoDimension.find(
     630          60 :                                         oDimSwath.osName);
     631          60 :                                 if (oIter !=
     632         120 :                                     oMapDataDimensionToGeoDimension.end())
     633             :                                 {
     634          44 :                                     const auto &osGeoDimName = oIter->second;
     635          44 :                                     if (osGeoDimName ==
     636          44 :                                         aoLongitudeDimensions[0].osName)
     637             :                                     {
     638          22 :                                         osDataYDimName = oDimSwath.osName;
     639          22 :                                         oMetadata.iYDim = i;
     640             :                                     }
     641          22 :                                     else if (osGeoDimName ==
     642          22 :                                              aoLongitudeDimensions[1].osName)
     643             :                                     {
     644          22 :                                         osDataXDimName = oDimSwath.osName;
     645          22 :                                         oMetadata.iXDim = i;
     646             :                                     }
     647             :                                 }
     648             :                                 else
     649             :                                 {
     650          16 :                                     oMetadata.iOtherDim = i;
     651             :                                 }
     652          60 :                                 ++i;
     653             :                             }
     654          22 :                             if (oMetadata.iXDim >= 0 && oMetadata.iYDim >= 0)
     655             :                             {
     656             :                                 oMetadata.osLongitudeSubdataset =
     657          44 :                                     "//HDFEOS/SWATHS/" + osSwathName +
     658          22 :                                     "/Geolocation_Fields/Longitude";
     659             :                                 oMetadata.osLatitudeSubdataset =
     660          44 :                                     "//HDFEOS/SWATHS/" + osSwathName +
     661          22 :                                     "/Geolocation_Fields/Latitude";
     662             : 
     663          90 :                                 for (const auto &oDimMap : aoDimensionMaps)
     664             :                                 {
     665          68 :                                     if (oDimMap.osDataDimName == osDataYDimName)
     666             :                                     {
     667          22 :                                         oMetadata.nLineOffset = oDimMap.nOffset;
     668          22 :                                         oMetadata.nLineStep =
     669          22 :                                             oDimMap.nIncrement;
     670             :                                     }
     671          46 :                                     else if (oDimMap.osDataDimName ==
     672             :                                              osDataXDimName)
     673             :                                     {
     674          22 :                                         oMetadata.nPixelOffset =
     675          22 :                                             oDimMap.nOffset;
     676          22 :                                         oMetadata.nPixelStep =
     677          22 :                                             oDimMap.nIncrement;
     678             :                                     }
     679             :                                 }
     680             :                             }
     681             :                         }
     682             : 
     683             :                         m_oMapSubdatasetNameToSwathFieldMetadata
     684          92 :                             ["//HDFEOS/SWATHS/" + osSwathName +
     685         138 :                              "/Data_Fields/" + osDataFieldName] =
     686          92 :                                 std::move(oMetadata);
     687             :                     }
     688             :                 }
     689             :             }
     690             :         }
     691             :     }
     692          16 : }
     693             : 
     694             : /************************************************************************/
     695             : /*                          GetSwathMetadata()                          */
     696             : /************************************************************************/
     697             : 
     698           3 : bool HDF5EOSParser::GetSwathMetadata(const std::string &osSwathName,
     699             :                                      SwathMetadata &swathMetadataOut) const
     700             : {
     701           3 :     const auto oIter = m_oMapSwathNameToSwathMetadata.find(osSwathName);
     702           3 :     if (oIter == m_oMapSwathNameToSwathMetadata.end())
     703           0 :         return false;
     704           3 :     swathMetadataOut = *(oIter->second.get());
     705           3 :     return true;
     706             : }
     707             : 
     708             : /************************************************************************/
     709             : /*                       GetSwathFieldMetadata()                        */
     710             : /************************************************************************/
     711             : 
     712          23 : bool HDF5EOSParser::GetSwathFieldMetadata(
     713             :     const char *pszSubdatasetName,
     714             :     SwathFieldMetadata &swathFieldMetadataOut) const
     715             : {
     716             :     const auto oIter =
     717          23 :         m_oMapSubdatasetNameToSwathFieldMetadata.find(pszSubdatasetName);
     718          23 :     if (oIter == m_oMapSubdatasetNameToSwathFieldMetadata.end())
     719           2 :         return false;
     720          21 :     swathFieldMetadataOut = oIter->second;
     721          21 :     return true;
     722             : }
     723             : 
     724             : /************************************************************************/
     725             : /*                          GetGeoTransform()                           */
     726             : /************************************************************************/
     727             : 
     728          12 : bool HDF5EOSParser::GridMetadata::GetGeoTransform(GDALGeoTransform &gt) const
     729             : {
     730          12 :     if (nProjCode >= 0 &&
     731          26 :         (osGridOrigin == "HE5_HDFE_GD_UL" || osGridOrigin.empty()) &&
     732          36 :         adfUpperLeftPointMeters.size() == 2 &&
     733          12 :         adfLowerRightPointMeters.size() == 2)
     734             :     {
     735          12 :         int nRasterXSize = 0;
     736          12 :         int nRasterYSize = 0;
     737             : 
     738          46 :         for (const auto &oDim : aoDimensions)
     739             :         {
     740          34 :             if (oDim.osName == "XDim")
     741          12 :                 nRasterXSize = oDim.nSize;
     742          22 :             else if (oDim.osName == "YDim")
     743          12 :                 nRasterYSize = oDim.nSize;
     744             :         }
     745          12 :         if (nRasterXSize <= 0 || nRasterYSize <= 0)
     746           0 :             return false;
     747          12 :         if (nProjCode == 0)  // GEO
     748             :         {
     749           2 :             gt.xorig = CPLPackedDMSToDec(adfUpperLeftPointMeters[0]);
     750           2 :             gt.xscale = (CPLPackedDMSToDec(adfLowerRightPointMeters[0]) -
     751           2 :                          CPLPackedDMSToDec(adfUpperLeftPointMeters[0])) /
     752             :                         nRasterXSize;
     753           2 :             gt.xrot = 0;
     754           2 :             gt.yorig = CPLPackedDMSToDec(adfUpperLeftPointMeters[1]);
     755           2 :             gt.yrot = 0;
     756           2 :             gt.yscale = (CPLPackedDMSToDec(adfLowerRightPointMeters[1]) -
     757           2 :                          CPLPackedDMSToDec(adfUpperLeftPointMeters[1])) /
     758             :                         nRasterYSize;
     759             :         }
     760             :         else
     761             :         {
     762          10 :             gt.xorig = adfUpperLeftPointMeters[0];
     763          10 :             gt.xscale =
     764          10 :                 (adfLowerRightPointMeters[0] - adfUpperLeftPointMeters[0]) /
     765             :                 nRasterXSize;
     766          10 :             gt.xrot = 0;
     767          10 :             gt.yorig = adfUpperLeftPointMeters[1];
     768          10 :             gt.yrot = 0;
     769          10 :             gt.yscale =
     770          10 :                 (adfLowerRightPointMeters[1] - adfUpperLeftPointMeters[1]) /
     771             :                 nRasterYSize;
     772             :         }
     773          12 :         return true;
     774             :     }
     775           0 :     return false;
     776             : }
     777             : 
     778             : /************************************************************************/
     779             : /*                               GetSRS()                               */
     780             : /************************************************************************/
     781             : 
     782          10 : std::unique_ptr<OGRSpatialReference> HDF5EOSParser::GridMetadata::GetSRS() const
     783             : {
     784          20 :     std::vector<double> l_adfProjParams = adfProjParams;
     785          10 :     l_adfProjParams.resize(15);
     786          20 :     auto poSRS = std::make_unique<OGRSpatialReference>();
     787          10 :     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     788          20 :     if (poSRS->importFromUSGS(nProjCode, nZone, l_adfProjParams.data(),
     789          20 :                               nSphereCode) == OGRERR_NONE)
     790             :     {
     791          10 :         return poSRS;
     792             :     }
     793           0 :     return nullptr;
     794             : }

Generated by: LCOV version 1.14