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

Generated by: LCOV version 1.14