LCOV - code coverage report
Current view: top level - frmts/hdf5 - hdf5eosparser.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 345 368 93.8 %
Date: 2025-09-10 17:48:50 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         264 : bool HDF5EOSParser::HasHDFEOS(hid_t hRoot)
      26             : {
      27         264 :     hsize_t numObjs = 0;
      28         264 :     H5Gget_num_objs(hRoot, &numObjs);
      29         264 :     bool bFound = false;
      30         918 :     for (hsize_t i = 0; i < numObjs; ++i)
      31             :     {
      32             :         char szName[128];
      33             :         ssize_t nLen =
      34         684 :             H5Gget_objname_by_idx(hRoot, i, szName, sizeof(szName) - 1);
      35         684 :         if (nLen > 0)
      36             :         {
      37         684 :             szName[nLen] = 0;
      38         684 :             if (strcmp(szName, "HDFEOS INFORMATION") == 0)
      39             :             {
      40          30 :                 bFound = true;
      41          30 :                 break;
      42             :             }
      43             :         }
      44             :     }
      45         264 :     if (!bFound)
      46         234 :         return false;
      47             : 
      48             :     H5G_stat_t oStatbuf;
      49          30 :     if (H5Gget_objinfo(hRoot, "HDFEOS INFORMATION", false, &oStatbuf) < 0)
      50           0 :         return false;
      51             : 
      52          30 :     auto hHDFEOSInformation = H5Gopen(hRoot, "HDFEOS INFORMATION");
      53          30 :     if (hHDFEOSInformation < 0)
      54             :     {
      55           0 :         return false;
      56             :     }
      57          30 :     H5Gclose(hHDFEOSInformation);
      58          30 :     return true;
      59             : }
      60             : 
      61             : /************************************************************************/
      62             : /*                               Parse()                                */
      63             : /************************************************************************/
      64             : 
      65          30 : bool HDF5EOSParser::Parse(hid_t hRoot)
      66             : {
      67          30 :     auto hHDFEOSInformation = H5Gopen(hRoot, "HDFEOS INFORMATION");
      68          30 :     if (hHDFEOSInformation < 0)
      69             :     {
      70           0 :         return false;
      71             :     }
      72             : 
      73          30 :     const hid_t hArrayId = H5Dopen(hHDFEOSInformation, "StructMetadata.0");
      74          30 :     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          30 :     const hid_t hAttrSpace = H5Dget_space(hArrayId);
      82          30 :     const hid_t hAttrTypeID = H5Dget_type(hArrayId);
      83             :     const hid_t hAttrNativeType =
      84          30 :         H5Tget_native_type(hAttrTypeID, H5T_DIR_DEFAULT);
      85             : 
      86             :     // Fetch StructMetadata.0 content in a std::string
      87          60 :     std::string osResult;
      88          30 :     if (H5Tget_class(hAttrNativeType) == H5T_STRING &&
      89          60 :         !H5Tis_variable_str(hAttrNativeType) &&
      90          30 :         H5Sget_simple_extent_ndims(hAttrSpace) == 0)
      91             :     {
      92          30 :         const auto nSize = H5Tget_size(hAttrNativeType);
      93          30 :         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          30 :             osResult.resize(nSize);
     101          30 :             H5Dread(hArrayId, hAttrNativeType, H5S_ALL, hAttrSpace, H5P_DEFAULT,
     102          30 :                     &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          30 :     H5Sclose(hAttrSpace);
     111          30 :     H5Tclose(hAttrNativeType);
     112          30 :     H5Tclose(hAttrTypeID);
     113             : 
     114          30 :     H5Dclose(hArrayId);
     115          30 :     H5Gclose(hHDFEOSInformation);
     116             : 
     117          30 :     if (osResult.empty())
     118           0 :         return false;
     119             : 
     120             :     // Parse StructMetadata.0 with NASAKeywordHandler
     121          60 :     NASAKeywordHandler oKWHandler;
     122             : #ifdef DEBUG
     123          30 :     CPLDebug("HDF5EOS", "%s", osResult.c_str());
     124             : #endif
     125          30 :     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          60 :     auto oJsonRoot = oKWHandler.GetJsonObject();
     134          90 :     auto oGridStructure = oJsonRoot.GetObj("GridStructure");
     135          60 :     auto oSwathStructure = oJsonRoot.GetObj("SwathStructure");
     136          30 :     bool bOK = false;
     137             :     // An empty
     138             :     // GROUP=GridStructure
     139             :     // END_GROUP=GridStructure
     140             :     // will generate 2 keys (_type and END_GROUP)
     141          30 :     if (oGridStructure.IsValid() && oGridStructure.GetChildren().size() > 2)
     142             :     {
     143          15 :         bOK = true;
     144          15 :         m_eDataModel = DataModel::GRID;
     145          15 :         ParseGridStructure(oGridStructure);
     146             :     }
     147          30 :     else if (oSwathStructure.IsValid() &&
     148          30 :              oSwathStructure.GetChildren().size() > 2)
     149             :     {
     150          15 :         bOK = true;
     151          15 :         m_eDataModel = DataModel::SWATH;
     152          15 :         ParseSwathStructure(oSwathStructure);
     153             :     }
     154             : 
     155          30 :     return bOK;
     156             : }
     157             : 
     158             : /************************************************************************/
     159             : /*                     GetGTCPProjectionCode()                          */
     160             : /************************************************************************/
     161             : 
     162          15 : static int GetGTCPProjectionCode(const std::string &osProjection)
     163             : {
     164          15 :     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         132 :     for (int i = 0; i < static_cast<int>(CPL_ARRAYSIZE(apszGCTPProjections));
     179             :          ++i)
     180             :     {
     181         132 :         if (osProjection == apszGCTPProjections[i])
     182             :         {
     183          15 :             return i;
     184             :         }
     185             :     }
     186           0 :     return -1;
     187             : }
     188             : 
     189             : /************************************************************************/
     190             : /*                        ParseGridStructure()                          */
     191             : /************************************************************************/
     192             : 
     193          15 : void HDF5EOSParser::ParseGridStructure(const CPLJSONObject &oGridStructure)
     194             : {
     195          60 :     for (const auto &oGrid : oGridStructure.GetChildren())
     196             :     {
     197          45 :         if (oGrid.GetType() == CPLJSONObject::Type::Object)
     198             :         {
     199          45 :             const auto osGridName = oGrid.GetString("GridName");
     200          45 :             const auto oDataFields = oGrid.GetObj("DataField");
     201          45 :             const auto oDimensions = oGrid.GetObj("Dimension");
     202          30 :             std::map<std::string, int> oMapDimensionNameToSize;
     203          30 :             auto poGridMetadata = std::make_unique<GridMetadata>();
     204          15 :             poGridMetadata->osGridName = osGridName;
     205          72 :             for (const auto &oDimension : oDimensions.GetChildren())
     206             :             {
     207          57 :                 if (oDimension.GetType() == CPLJSONObject::Type::Object)
     208             :                 {
     209             :                     std::string osDimensionName =
     210          81 :                         oDimension.GetString("DimensionName");
     211          27 :                     int nSize = oDimension.GetInteger("Size");
     212          27 :                     oMapDimensionNameToSize[osDimensionName] = nSize;
     213          54 :                     Dimension oDim;
     214          27 :                     oDim.osName = std::move(osDimensionName);
     215          27 :                     oDim.nSize = nSize;
     216          27 :                     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          15 :             const int nXDim = oGrid.GetInteger("XDim", 0);
     227          15 :             const int nYDim = oGrid.GetInteger("YDim", 0);
     228          15 :             if (nXDim > 0 && nYDim > 0 &&
     229          35 :                 !cpl::contains(oMapDimensionNameToSize, "XDim") &&
     230           5 :                 !cpl::contains(oMapDimensionNameToSize, "YDim"))
     231             :             {
     232             :                 // Check that we have at least one data field with DimList=(YDim,XDim)
     233             :                 // property.
     234           5 :                 bool bHasDimListYDimXDim = false;
     235          15 :                 for (const auto &oDataField : oDataFields.GetChildren())
     236             :                 {
     237          10 :                     if (oDataField.GetType() == CPLJSONObject::Type::Object)
     238             :                     {
     239          10 :                         const auto oDimList = oDataField.GetArray("DimList");
     240           5 :                         if (oDimList.Size() == 2 &&
     241          15 :                             oDimList[0].ToString() == "YDim" &&
     242          10 :                             oDimList[1].ToString() == "XDim")
     243             :                         {
     244           5 :                             bHasDimListYDimXDim = true;
     245           5 :                             break;
     246             :                         }
     247             :                     }
     248             :                 }
     249           5 :                 if (bHasDimListYDimXDim)
     250             :                 {
     251             :                     {
     252          10 :                         std::string osDimensionName("YDim");
     253           5 :                         oMapDimensionNameToSize[osDimensionName] = nYDim;
     254          10 :                         Dimension oDim;
     255           5 :                         oDim.osName = std::move(osDimensionName);
     256           5 :                         oDim.nSize = nYDim;
     257           5 :                         poGridMetadata->aoDimensions.push_back(std::move(oDim));
     258             :                     }
     259             :                     {
     260          10 :                         std::string osDimensionName("XDim");
     261           5 :                         oMapDimensionNameToSize[osDimensionName] = nXDim;
     262          10 :                         Dimension oDim;
     263           5 :                         oDim.osName = std::move(osDimensionName);
     264           5 :                         oDim.nSize = nXDim;
     265           5 :                         poGridMetadata->aoDimensions.push_back(std::move(oDim));
     266             :                     }
     267             :                 }
     268             :             }
     269             : 
     270          15 :             poGridMetadata->osProjection = oGrid.GetString("Projection");
     271          30 :             poGridMetadata->nProjCode =
     272          15 :                 GetGTCPProjectionCode(poGridMetadata->osProjection);
     273          15 :             poGridMetadata->osGridOrigin = oGrid.GetString("GridOrigin");
     274          15 :             poGridMetadata->nZone = oGrid.GetInteger("ZoneCode", -1);
     275          15 :             poGridMetadata->nSphereCode = oGrid.GetInteger("SphereCode", -1);
     276             : 
     277          45 :             const auto oProjParams = oGrid.GetArray("ProjParams");
     278         106 :             for (int j = 0; j < oProjParams.Size(); ++j)
     279          91 :                 poGridMetadata->adfProjParams.push_back(
     280          91 :                     oProjParams[j].ToDouble());
     281             : 
     282             :             const auto oUpperLeftPointMtrs =
     283          45 :                 oGrid.GetArray("UpperLeftPointMtrs");
     284          45 :             for (int j = 0; j < oUpperLeftPointMtrs.Size(); ++j)
     285          30 :                 poGridMetadata->adfUpperLeftPointMeters.push_back(
     286          30 :                     oUpperLeftPointMtrs[j].ToDouble());
     287             : 
     288          45 :             const auto oLowerRightMtrs = oGrid.GetArray("LowerRightMtrs");
     289          45 :             for (int j = 0; j < oLowerRightMtrs.Size(); ++j)
     290          30 :                 poGridMetadata->adfLowerRightPointMeters.push_back(
     291          30 :                     oLowerRightMtrs[j].ToDouble());
     292             : 
     293          15 :             m_oMapGridNameToGridMetadata[osGridName] =
     294          30 :                 std::move(poGridMetadata);
     295             :             const auto poGridMetadataRef =
     296          15 :                 m_oMapGridNameToGridMetadata[osGridName].get();
     297             : 
     298          60 :             for (const auto &oDataField : oDataFields.GetChildren())
     299             :             {
     300          45 :                 if (oDataField.GetType() == CPLJSONObject::Type::Object)
     301             :                 {
     302             :                     const auto osDataFieldName =
     303          45 :                         oDataField.GetString("DataFieldName");
     304          45 :                     const auto oDimList = oDataField.GetArray("DimList");
     305          30 :                     GridDataFieldMetadata oDataFieldMetadata;
     306          15 :                     bool bValid = oDimList.Size() > 0;
     307          49 :                     for (int j = 0; j < oDimList.Size(); ++j)
     308             :                     {
     309          68 :                         std::string osDimensionName = oDimList[j].ToString();
     310             :                         const auto oIter = oMapDimensionNameToSize.find(
     311          34 :                             osDimensionName.c_str());
     312          34 :                         if (oIter == oMapDimensionNameToSize.end())
     313             :                         {
     314           0 :                             bValid = false;
     315           0 :                             break;
     316             :                         }
     317          68 :                         Dimension oDim;
     318          34 :                         oDim.osName = std::move(osDimensionName);
     319          34 :                         oDim.nSize = oIter->second;
     320          34 :                         oDataFieldMetadata.aoDimensions.push_back(
     321          34 :                             std::move(oDim));
     322             :                     }
     323          15 :                     if (bValid)
     324             :                     {
     325          15 :                         oDataFieldMetadata.poGridMetadata = poGridMetadataRef;
     326             :                         m_oMapSubdatasetNameToGridDataFieldMetadata
     327          30 :                             ["//HDFEOS/GRIDS/" + osGridName + "/Data_Fields/" +
     328          30 :                              osDataFieldName] = std::move(oDataFieldMetadata);
     329             :                     }
     330             :                 }
     331             :             }
     332             :         }
     333             :     }
     334          15 : }
     335             : 
     336             : /************************************************************************/
     337             : /*                        GetGridMetadata()                             */
     338             : /************************************************************************/
     339             : 
     340           9 : bool HDF5EOSParser::GetGridMetadata(const std::string &osGridName,
     341             :                                     GridMetadata &gridMetadataOut) const
     342             : {
     343           9 :     const auto oIter = m_oMapGridNameToGridMetadata.find(osGridName);
     344           9 :     if (oIter == m_oMapGridNameToGridMetadata.end())
     345           6 :         return false;
     346           3 :     gridMetadataOut = *(oIter->second);
     347           3 :     return true;
     348             : }
     349             : 
     350             : /************************************************************************/
     351             : /*                     GetGridDataFieldMetadata()                       */
     352             : /************************************************************************/
     353             : 
     354          12 : bool HDF5EOSParser::GetGridDataFieldMetadata(
     355             :     const char *pszSubdatasetName,
     356             :     GridDataFieldMetadata &gridDataFieldMetadataOut) const
     357             : {
     358             :     const auto oIter =
     359          12 :         m_oMapSubdatasetNameToGridDataFieldMetadata.find(pszSubdatasetName);
     360          12 :     if (oIter == m_oMapSubdatasetNameToGridDataFieldMetadata.end())
     361           3 :         return false;
     362           9 :     gridDataFieldMetadataOut = oIter->second;
     363           9 :     return true;
     364             : }
     365             : 
     366             : /************************************************************************/
     367             : /*                        ParseSwathStructure()                         */
     368             : /************************************************************************/
     369             : 
     370          15 : void HDF5EOSParser::ParseSwathStructure(const CPLJSONObject &oSwathStructure)
     371             : {
     372          60 :     for (const auto &oSwath : oSwathStructure.GetChildren())
     373             :     {
     374          45 :         if (oSwath.GetType() == CPLJSONObject::Type::Object)
     375             :         {
     376          45 :             const auto osSwathName = oSwath.GetString("SwathName");
     377             : 
     378          45 :             const auto oDimensions = oSwath.GetObj("Dimension");
     379          30 :             std::map<std::string, int> oMapDimensionNameToSize;
     380          30 :             auto poSwathMetadata = std::make_unique<SwathMetadata>();
     381          15 :             poSwathMetadata->osSwathName = osSwathName;
     382         126 :             for (const auto &oDimension : oDimensions.GetChildren())
     383             :             {
     384         111 :                 if (oDimension.GetType() == CPLJSONObject::Type::Object)
     385             :                 {
     386             :                     auto osDimensionName =
     387         243 :                         oDimension.GetString("DimensionName");
     388          81 :                     int nSize = oDimension.GetInteger("Size");
     389          81 :                     oMapDimensionNameToSize[osDimensionName] = nSize;
     390         162 :                     Dimension oDim;
     391          81 :                     oDim.osName = std::move(osDimensionName);
     392          81 :                     oDim.nSize = nSize;
     393          81 :                     poSwathMetadata->aoDimensions.emplace_back(std::move(oDim));
     394             :                 }
     395             :             }
     396             : 
     397          15 :             m_oMapSwathNameToSwathMetadata[osSwathName] =
     398          30 :                 std::move(poSwathMetadata);
     399             :             const auto poSwathMetadataRef =
     400          15 :                 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          30 :             std::vector<DimensionMap> aoDimensionMaps;
     411          30 :             std::map<std::string, std::string> oMapDataDimensionToGeoDimension;
     412             : 
     413          45 :             const auto jsonDimensionMaps = oSwath.GetObj("DimensionMap");
     414          57 :             for (const auto &jsonDimensionMap : jsonDimensionMaps.GetChildren())
     415             :             {
     416          42 :                 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          45 :             const auto oGeoFields = oSwath.GetObj("GeoField");
     435          30 :             std::vector<Dimension> aoLongitudeDimensions;
     436          30 :             std::vector<Dimension> aoLatitudeDimensions;
     437          90 :             for (const auto &oGeoField : oGeoFields.GetChildren())
     438             :             {
     439          75 :                 if (oGeoField.GetType() == CPLJSONObject::Type::Object)
     440             :                 {
     441         135 :                     auto osGeoFieldName = oGeoField.GetString("GeoFieldName");
     442         135 :                     auto oDimList = oGeoField.GetArray("DimList");
     443          45 :                     bool bValid = true;
     444          90 :                     std::vector<Dimension> aoDimensions;
     445         120 :                     for (int j = 0; j < oDimList.Size(); ++j)
     446             :                     {
     447         150 :                         const auto osDimensionName = oDimList[j].ToString();
     448             :                         const auto oIter = oMapDimensionNameToSize.find(
     449          75 :                             osDimensionName.c_str());
     450          75 :                         if (oIter == oMapDimensionNameToSize.end())
     451             :                         {
     452           0 :                             bValid = false;
     453           0 :                             break;
     454             :                         }
     455         150 :                         Dimension oDim;
     456          75 :                         oDim.osName = osDimensionName;
     457          75 :                         oDim.nSize = oIter->second;
     458          75 :                         aoDimensions.push_back(std::move(oDim));
     459          75 :                         if (oMapDataDimensionToGeoDimension.find(
     460          75 :                                 osDimensionName) ==
     461         150 :                             oMapDataDimensionToGeoDimension.end())
     462             :                         {
     463             :                             // Create a fake dimension map for this dim
     464          60 :                             DimensionMap oDimensionMap;
     465          30 :                             oDimensionMap.osGeoDimName = osDimensionName;
     466          30 :                             oDimensionMap.osDataDimName = osDimensionName;
     467          30 :                             oDimensionMap.nOffset = 0;
     468          30 :                             oDimensionMap.nIncrement = 1;
     469          30 :                             oMapDataDimensionToGeoDimension[osDimensionName] =
     470          30 :                                 osDimensionName;
     471          30 :                             aoDimensionMaps.emplace_back(oDimensionMap);
     472             :                         }
     473             :                     }
     474          45 :                     if (bValid)
     475             :                     {
     476          90 :                         SwathGeolocationFieldMetadata oMetadata;
     477          45 :                         oMetadata.poSwathMetadata = poSwathMetadataRef;
     478             : 
     479          45 :                         if (osGeoFieldName == "Longitude")
     480          15 :                             aoLongitudeDimensions = aoDimensions;
     481          30 :                         else if (osGeoFieldName == "Latitude")
     482          15 :                             aoLatitudeDimensions = aoDimensions;
     483             : 
     484          45 :                         oMetadata.aoDimensions = std::move(aoDimensions);
     485             : 
     486             :                         const std::string osSubdatasetName =
     487          90 :                             "//HDFEOS/SWATHS/" + osSwathName +
     488          90 :                             "/Geolocation_Fields/" + osGeoFieldName;
     489             :                         m_oMapSubdatasetNameToSwathGeolocationFieldMetadata
     490          45 :                             [osSubdatasetName] = std::move(oMetadata);
     491             :                     }
     492             :                 }
     493             :             }
     494             : 
     495          45 :             const auto oDataFields = oSwath.GetObj("DataField");
     496          90 :             for (const auto &oDataField : oDataFields.GetChildren())
     497             :             {
     498          75 :                 if (oDataField.GetType() == CPLJSONObject::Type::Object)
     499             :                 {
     500             :                     const auto osDataFieldName =
     501         135 :                         oDataField.GetString("DataFieldName");
     502         135 :                     const auto oDimList = oDataField.GetArray("DimList");
     503          90 :                     SwathDataFieldMetadata oMetadata;
     504          45 :                     oMetadata.poSwathMetadata = poSwathMetadataRef;
     505          45 :                     bool bValid = oDimList.Size() > 0;
     506         126 :                     for (int j = 0; j < oDimList.Size(); ++j)
     507             :                     {
     508         162 :                         std::string osDimensionName = oDimList[j].ToString();
     509             :                         const auto oIter = oMapDimensionNameToSize.find(
     510          81 :                             osDimensionName.c_str());
     511          81 :                         if (oIter == oMapDimensionNameToSize.end())
     512             :                         {
     513           0 :                             bValid = false;
     514           0 :                             break;
     515             :                         }
     516         162 :                         Dimension oDim;
     517          81 :                         oDim.osName = std::move(osDimensionName);
     518          81 :                         oDim.nSize = oIter->second;
     519          81 :                         oMetadata.aoDimensions.push_back(std::move(oDim));
     520             :                     }
     521          45 :                     if (bValid)
     522             :                     {
     523          66 :                         if (oMetadata.aoDimensions.size() >= 2 &&
     524          66 :                             aoLongitudeDimensions.size() == 2 &&
     525          21 :                             aoLongitudeDimensions == aoLatitudeDimensions)
     526             :                         {
     527          21 :                             int i = 0;
     528          42 :                             std::string osDataXDimName;
     529          42 :                             std::string osDataYDimName;
     530          78 :                             for (const auto &oDimSwath : oMetadata.aoDimensions)
     531             :                             {
     532             :                                 auto oIter =
     533             :                                     oMapDataDimensionToGeoDimension.find(
     534          57 :                                         oDimSwath.osName);
     535          57 :                                 if (oIter !=
     536         114 :                                     oMapDataDimensionToGeoDimension.end())
     537             :                                 {
     538          42 :                                     const auto &osGeoDimName = oIter->second;
     539          42 :                                     if (osGeoDimName ==
     540          42 :                                         aoLongitudeDimensions[0].osName)
     541             :                                     {
     542          21 :                                         osDataYDimName = oDimSwath.osName;
     543          21 :                                         oMetadata.iYDim = i;
     544             :                                     }
     545          21 :                                     else if (osGeoDimName ==
     546          21 :                                              aoLongitudeDimensions[1].osName)
     547             :                                     {
     548          21 :                                         osDataXDimName = oDimSwath.osName;
     549          21 :                                         oMetadata.iXDim = i;
     550             :                                     }
     551             :                                 }
     552             :                                 else
     553             :                                 {
     554          15 :                                     oMetadata.iOtherDim = i;
     555             :                                 }
     556          57 :                                 ++i;
     557             :                             }
     558          21 :                             if (oMetadata.iXDim >= 0 && oMetadata.iYDim >= 0)
     559             :                             {
     560             :                                 oMetadata.osLongitudeSubdataset =
     561          42 :                                     "//HDFEOS/SWATHS/" + osSwathName +
     562          21 :                                     "/Geolocation_Fields/Longitude";
     563             :                                 oMetadata.osLatitudeSubdataset =
     564          42 :                                     "//HDFEOS/SWATHS/" + osSwathName +
     565          21 :                                     "/Geolocation_Fields/Latitude";
     566             : 
     567          87 :                                 for (const auto &oDimMap : aoDimensionMaps)
     568             :                                 {
     569          66 :                                     if (oDimMap.osDataDimName == osDataYDimName)
     570             :                                     {
     571          21 :                                         oMetadata.nLineOffset = oDimMap.nOffset;
     572          21 :                                         oMetadata.nLineStep =
     573          21 :                                             oDimMap.nIncrement;
     574             :                                     }
     575          45 :                                     else if (oDimMap.osDataDimName ==
     576             :                                              osDataXDimName)
     577             :                                     {
     578          21 :                                         oMetadata.nPixelOffset =
     579          21 :                                             oDimMap.nOffset;
     580          21 :                                         oMetadata.nPixelStep =
     581          21 :                                             oDimMap.nIncrement;
     582             :                                     }
     583             :                                 }
     584             :                             }
     585             :                         }
     586             : 
     587             :                         m_oMapSubdatasetNameToSwathDataFieldMetadata
     588          90 :                             ["//HDFEOS/SWATHS/" + osSwathName +
     589         135 :                              "/Data_Fields/" + osDataFieldName] =
     590          90 :                                 std::move(oMetadata);
     591             :                     }
     592             :                 }
     593             :             }
     594             :         }
     595             :     }
     596          15 : }
     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          22 : bool HDF5EOSParser::GetSwathDataFieldMetadata(
     617             :     const char *pszSubdatasetName,
     618             :     SwathDataFieldMetadata &swathDataFieldMetadataOut) const
     619             : {
     620             :     const auto oIter =
     621          22 :         m_oMapSubdatasetNameToSwathDataFieldMetadata.find(pszSubdatasetName);
     622          22 :     if (oIter == m_oMapSubdatasetNameToSwathDataFieldMetadata.end())
     623          12 :         return false;
     624          10 :     swathDataFieldMetadataOut = oIter->second;
     625          10 :     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           9 : bool HDF5EOSParser::GridMetadata::GetGeoTransform(GDALGeoTransform &gt) const
     649             : {
     650           9 :     if (nProjCode >= 0 &&
     651          20 :         (osGridOrigin == "HE5_HDFE_GD_UL" || osGridOrigin.empty()) &&
     652          27 :         adfUpperLeftPointMeters.size() == 2 &&
     653           9 :         adfLowerRightPointMeters.size() == 2)
     654             :     {
     655           9 :         int nRasterXSize = 0;
     656           9 :         int nRasterYSize = 0;
     657             : 
     658          31 :         for (const auto &oDim : aoDimensions)
     659             :         {
     660          22 :             if (oDim.osName == "XDim")
     661           9 :                 nRasterXSize = oDim.nSize;
     662          13 :             else if (oDim.osName == "YDim")
     663           9 :                 nRasterYSize = oDim.nSize;
     664             :         }
     665           9 :         if (nRasterXSize <= 0 || nRasterYSize <= 0)
     666           0 :             return false;
     667           9 :         if (nProjCode == 0)  // GEO
     668             :         {
     669           2 :             gt[0] = CPLPackedDMSToDec(adfUpperLeftPointMeters[0]);
     670           2 :             gt[1] = (CPLPackedDMSToDec(adfLowerRightPointMeters[0]) -
     671           2 :                      CPLPackedDMSToDec(adfUpperLeftPointMeters[0])) /
     672             :                     nRasterXSize;
     673           2 :             gt[2] = 0;
     674           2 :             gt[3] = CPLPackedDMSToDec(adfUpperLeftPointMeters[1]);
     675           2 :             gt[4] = 0;
     676           4 :             gt[5] = (CPLPackedDMSToDec(adfLowerRightPointMeters[1]) -
     677           2 :                      CPLPackedDMSToDec(adfUpperLeftPointMeters[1])) /
     678             :                     nRasterYSize;
     679             :         }
     680             :         else
     681             :         {
     682           7 :             gt[0] = adfUpperLeftPointMeters[0];
     683           7 :             gt[1] = (adfLowerRightPointMeters[0] - adfUpperLeftPointMeters[0]) /
     684             :                     nRasterXSize;
     685           7 :             gt[2] = 0;
     686           7 :             gt[3] = adfUpperLeftPointMeters[1];
     687           7 :             gt[4] = 0;
     688           7 :             gt[5] = (adfLowerRightPointMeters[1] - adfUpperLeftPointMeters[1]) /
     689             :                     nRasterYSize;
     690             :         }
     691           9 :         return true;
     692             :     }
     693           0 :     return false;
     694             : }
     695             : 
     696             : /************************************************************************/
     697             : /*                              GetSRS()                                */
     698             : /************************************************************************/
     699             : 
     700           9 : std::unique_ptr<OGRSpatialReference> HDF5EOSParser::GridMetadata::GetSRS() const
     701             : {
     702          18 :     std::vector<double> l_adfProjParams = adfProjParams;
     703           9 :     l_adfProjParams.resize(15);
     704          18 :     auto poSRS = std::make_unique<OGRSpatialReference>();
     705           9 :     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     706          18 :     if (poSRS->importFromUSGS(nProjCode, nZone, l_adfProjParams.data(),
     707          18 :                               nSphereCode) == OGRERR_NONE)
     708             :     {
     709           9 :         return poSRS;
     710             :     }
     711           0 :     return nullptr;
     712             : }

Generated by: LCOV version 1.14