LCOV - code coverage report
Current view: top level - frmts/hdf5 - hdf5eosparser.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 343 368 93.2 %
Date: 2025-01-18 12:42:00 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         277 : bool HDF5EOSParser::HasHDFEOS(hid_t hRoot)
      26             : {
      27         277 :     hsize_t numObjs = 0;
      28         277 :     H5Gget_num_objs(hRoot, &numObjs);
      29         277 :     bool bFound = false;
      30        1031 :     for (hsize_t i = 0; i < numObjs; ++i)
      31             :     {
      32             :         char szName[128];
      33             :         ssize_t nLen =
      34         780 :             H5Gget_objname_by_idx(hRoot, i, szName, sizeof(szName) - 1);
      35         780 :         if (nLen > 0)
      36             :         {
      37         780 :             szName[nLen] = 0;
      38         780 :             if (strcmp(szName, "HDFEOS INFORMATION") == 0)
      39             :             {
      40          26 :                 bFound = true;
      41          26 :                 break;
      42             :             }
      43             :         }
      44             :     }
      45         277 :     if (!bFound)
      46         251 :         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             :                     const auto 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 = osDimensionName;
     215          24 :                     oDim.nSize = nSize;
     216          24 :                     poGridMetadata->aoDimensions.push_back(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 :                         const std::string osDimensionName("YDim");
     253           2 :                         oMapDimensionNameToSize[osDimensionName] = nYDim;
     254           4 :                         Dimension oDim;
     255           2 :                         oDim.osName = osDimensionName;
     256           2 :                         oDim.nSize = nYDim;
     257           2 :                         poGridMetadata->aoDimensions.push_back(oDim);
     258             :                     }
     259             :                     {
     260           4 :                         const std::string osDimensionName("XDim");
     261           2 :                         oMapDimensionNameToSize[osDimensionName] = nXDim;
     262           4 :                         Dimension oDim;
     263           2 :                         oDim.osName = osDimensionName;
     264           2 :                         oDim.nSize = nXDim;
     265           2 :                         poGridMetadata->aoDimensions.push_back(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 :                         const auto 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 = osDimensionName;
     319          28 :                         oDim.nSize = oIter->second;
     320          28 :                         oDataFieldMetadata.aoDimensions.push_back(oDim);
     321             :                     }
     322          12 :                     if (bValid)
     323             :                     {
     324          12 :                         oDataFieldMetadata.poGridMetadata = poGridMetadataRef;
     325             :                         m_oMapSubdatasetNameToGridDataFieldMetadata
     326          24 :                             ["//HDFEOS/GRIDS/" + osGridName + "/Data_Fields/" +
     327          24 :                              osDataFieldName] = std::move(oDataFieldMetadata);
     328             :                     }
     329             :                 }
     330             :             }
     331             :         }
     332             :     }
     333          12 : }
     334             : 
     335             : /************************************************************************/
     336             : /*                        GetGridMetadata()                             */
     337             : /************************************************************************/
     338             : 
     339           7 : bool HDF5EOSParser::GetGridMetadata(const std::string &osGridName,
     340             :                                     GridMetadata &gridMetadataOut) const
     341             : {
     342           7 :     const auto oIter = m_oMapGridNameToGridMetadata.find(osGridName);
     343           7 :     if (oIter == m_oMapGridNameToGridMetadata.end())
     344           5 :         return false;
     345           2 :     gridMetadataOut = *(oIter->second);
     346           2 :     return true;
     347             : }
     348             : 
     349             : /************************************************************************/
     350             : /*                     GetGridDataFieldMetadata()                       */
     351             : /************************************************************************/
     352             : 
     353          10 : bool HDF5EOSParser::GetGridDataFieldMetadata(
     354             :     const char *pszSubdatasetName,
     355             :     GridDataFieldMetadata &gridDataFieldMetadataOut) const
     356             : {
     357             :     const auto oIter =
     358          10 :         m_oMapSubdatasetNameToGridDataFieldMetadata.find(pszSubdatasetName);
     359          10 :     if (oIter == m_oMapSubdatasetNameToGridDataFieldMetadata.end())
     360           3 :         return false;
     361           7 :     gridDataFieldMetadataOut = oIter->second;
     362           7 :     return true;
     363             : }
     364             : 
     365             : /************************************************************************/
     366             : /*                        ParseSwathStructure()                         */
     367             : /************************************************************************/
     368             : 
     369          14 : void HDF5EOSParser::ParseSwathStructure(const CPLJSONObject &oSwathStructure)
     370             : {
     371          56 :     for (const auto &oSwath : oSwathStructure.GetChildren())
     372             :     {
     373          42 :         if (oSwath.GetType() == CPLJSONObject::Type::Object)
     374             :         {
     375          42 :             const auto osSwathName = oSwath.GetString("SwathName");
     376             : 
     377          42 :             const auto oDimensions = oSwath.GetObj("Dimension");
     378          28 :             std::map<std::string, int> oMapDimensionNameToSize;
     379          28 :             auto poSwathMetadata = std::make_unique<SwathMetadata>();
     380          14 :             poSwathMetadata->osSwathName = osSwathName;
     381         120 :             for (const auto &oDimension : oDimensions.GetChildren())
     382             :             {
     383         106 :                 if (oDimension.GetType() == CPLJSONObject::Type::Object)
     384             :                 {
     385             :                     auto osDimensionName =
     386         234 :                         oDimension.GetString("DimensionName");
     387          78 :                     int nSize = oDimension.GetInteger("Size");
     388          78 :                     oMapDimensionNameToSize[osDimensionName] = nSize;
     389         156 :                     Dimension oDim;
     390          78 :                     oDim.osName = std::move(osDimensionName);
     391          78 :                     oDim.nSize = nSize;
     392          78 :                     poSwathMetadata->aoDimensions.emplace_back(std::move(oDim));
     393             :                 }
     394             :             }
     395             : 
     396          14 :             m_oMapSwathNameToSwathMetadata[osSwathName] =
     397          28 :                 std::move(poSwathMetadata);
     398             :             const auto poSwathMetadataRef =
     399          14 :                 m_oMapSwathNameToSwathMetadata[osSwathName].get();
     400             : 
     401             :             struct DimensionMap
     402             :             {
     403             :                 std::string osGeoDimName;
     404             :                 std::string osDataDimName;
     405             :                 int nOffset = 0;
     406             :                 int nIncrement = 1;
     407             :             };
     408             : 
     409          28 :             std::vector<DimensionMap> aoDimensionMaps;
     410          28 :             std::map<std::string, std::string> oMapDataDimensionToGeoDimension;
     411             : 
     412          42 :             const auto jsonDimensionMaps = oSwath.GetObj("DimensionMap");
     413          54 :             for (const auto &jsonDimensionMap : jsonDimensionMaps.GetChildren())
     414             :             {
     415          40 :                 if (jsonDimensionMap.GetType() == CPLJSONObject::Type::Object)
     416             :                 {
     417          24 :                     DimensionMap oDimensionMap;
     418             :                     oDimensionMap.osGeoDimName =
     419          12 :                         jsonDimensionMap.GetString("GeoDimension");
     420             :                     oDimensionMap.osDataDimName =
     421          12 :                         jsonDimensionMap.GetString("DataDimension");
     422          12 :                     oDimensionMap.nOffset =
     423          12 :                         jsonDimensionMap.GetInteger("Offset", 0);
     424          12 :                     oDimensionMap.nIncrement =
     425          12 :                         jsonDimensionMap.GetInteger("Increment", 1);
     426             :                     oMapDataDimensionToGeoDimension[oDimensionMap
     427          12 :                                                         .osDataDimName] =
     428          12 :                         oDimensionMap.osGeoDimName;
     429          12 :                     aoDimensionMaps.emplace_back(oDimensionMap);
     430             :                 }
     431             :             }
     432             : 
     433          42 :             const auto oGeoFields = oSwath.GetObj("GeoField");
     434          28 :             std::vector<Dimension> aoLongitudeDimensions;
     435          28 :             std::vector<Dimension> aoLatitudeDimensions;
     436          84 :             for (const auto &oGeoField : oGeoFields.GetChildren())
     437             :             {
     438          70 :                 if (oGeoField.GetType() == CPLJSONObject::Type::Object)
     439             :                 {
     440         126 :                     auto osGeoFieldName = oGeoField.GetString("GeoFieldName");
     441         126 :                     auto oDimList = oGeoField.GetArray("DimList");
     442          42 :                     bool bValid = true;
     443          84 :                     std::vector<Dimension> aoDimensions;
     444         112 :                     for (int j = 0; j < oDimList.Size(); ++j)
     445             :                     {
     446         140 :                         const auto osDimensionName = oDimList[j].ToString();
     447             :                         const auto oIter = oMapDimensionNameToSize.find(
     448          70 :                             osDimensionName.c_str());
     449          70 :                         if (oIter == oMapDimensionNameToSize.end())
     450             :                         {
     451           0 :                             bValid = false;
     452           0 :                             break;
     453             :                         }
     454         140 :                         Dimension oDim;
     455          70 :                         oDim.osName = osDimensionName;
     456          70 :                         oDim.nSize = oIter->second;
     457          70 :                         aoDimensions.push_back(oDim);
     458          70 :                         if (oMapDataDimensionToGeoDimension.find(
     459          70 :                                 osDimensionName) ==
     460         140 :                             oMapDataDimensionToGeoDimension.end())
     461             :                         {
     462             :                             // Create a fake dimension map for this dim
     463          56 :                             DimensionMap oDimensionMap;
     464          28 :                             oDimensionMap.osGeoDimName = osDimensionName;
     465          28 :                             oDimensionMap.osDataDimName = osDimensionName;
     466          28 :                             oDimensionMap.nOffset = 0;
     467          28 :                             oDimensionMap.nIncrement = 1;
     468          28 :                             oMapDataDimensionToGeoDimension[osDimensionName] =
     469          28 :                                 osDimensionName;
     470          28 :                             aoDimensionMaps.emplace_back(oDimensionMap);
     471             :                         }
     472             :                     }
     473          42 :                     if (bValid)
     474             :                     {
     475          84 :                         SwathGeolocationFieldMetadata oMetadata;
     476          42 :                         oMetadata.poSwathMetadata = poSwathMetadataRef;
     477             : 
     478          42 :                         if (osGeoFieldName == "Longitude")
     479          14 :                             aoLongitudeDimensions = aoDimensions;
     480          28 :                         else if (osGeoFieldName == "Latitude")
     481          14 :                             aoLatitudeDimensions = aoDimensions;
     482             : 
     483          42 :                         oMetadata.aoDimensions = std::move(aoDimensions);
     484             : 
     485             :                         const std::string osSubdatasetName =
     486          84 :                             "//HDFEOS/SWATHS/" + osSwathName +
     487          84 :                             "/Geolocation_Fields/" + osGeoFieldName;
     488             :                         m_oMapSubdatasetNameToSwathGeolocationFieldMetadata
     489          42 :                             [osSubdatasetName] = std::move(oMetadata);
     490             :                     }
     491             :                 }
     492             :             }
     493             : 
     494          42 :             const auto oDataFields = oSwath.GetObj("DataField");
     495          86 :             for (const auto &oDataField : oDataFields.GetChildren())
     496             :             {
     497          72 :                 if (oDataField.GetType() == CPLJSONObject::Type::Object)
     498             :                 {
     499             :                     const auto osDataFieldName =
     500         132 :                         oDataField.GetString("DataFieldName");
     501         132 :                     const auto oDimList = oDataField.GetArray("DimList");
     502          88 :                     SwathDataFieldMetadata oMetadata;
     503          44 :                     oMetadata.poSwathMetadata = poSwathMetadataRef;
     504          44 :                     bool bValid = oDimList.Size() > 0;
     505         122 :                     for (int j = 0; j < oDimList.Size(); ++j)
     506             :                     {
     507         156 :                         const auto osDimensionName = oDimList[j].ToString();
     508             :                         const auto oIter = oMapDimensionNameToSize.find(
     509          78 :                             osDimensionName.c_str());
     510          78 :                         if (oIter == oMapDimensionNameToSize.end())
     511             :                         {
     512           0 :                             bValid = false;
     513           0 :                             break;
     514             :                         }
     515         156 :                         Dimension oDim;
     516          78 :                         oDim.osName = osDimensionName;
     517          78 :                         oDim.nSize = oIter->second;
     518          78 :                         oMetadata.aoDimensions.push_back(oDim);
     519             :                     }
     520          44 :                     if (bValid)
     521             :                     {
     522          64 :                         if (oMetadata.aoDimensions.size() >= 2 &&
     523          64 :                             aoLongitudeDimensions.size() == 2 &&
     524          20 :                             aoLongitudeDimensions == aoLatitudeDimensions)
     525             :                         {
     526          20 :                             int i = 0;
     527          40 :                             std::string osDataXDimName;
     528          40 :                             std::string osDataYDimName;
     529          74 :                             for (const auto &oDimSwath : oMetadata.aoDimensions)
     530             :                             {
     531             :                                 auto oIter =
     532             :                                     oMapDataDimensionToGeoDimension.find(
     533          54 :                                         oDimSwath.osName);
     534          54 :                                 if (oIter !=
     535         108 :                                     oMapDataDimensionToGeoDimension.end())
     536             :                                 {
     537          40 :                                     const auto &osGeoDimName = oIter->second;
     538          40 :                                     if (osGeoDimName ==
     539          40 :                                         aoLongitudeDimensions[0].osName)
     540             :                                     {
     541          20 :                                         osDataYDimName = oDimSwath.osName;
     542          20 :                                         oMetadata.iYDim = i;
     543             :                                     }
     544          20 :                                     else if (osGeoDimName ==
     545          20 :                                              aoLongitudeDimensions[1].osName)
     546             :                                     {
     547          20 :                                         osDataXDimName = oDimSwath.osName;
     548          20 :                                         oMetadata.iXDim = i;
     549             :                                     }
     550             :                                 }
     551             :                                 else
     552             :                                 {
     553          14 :                                     oMetadata.iOtherDim = i;
     554             :                                 }
     555          54 :                                 ++i;
     556             :                             }
     557          20 :                             if (oMetadata.iXDim >= 0 && oMetadata.iYDim >= 0)
     558             :                             {
     559             :                                 oMetadata.osLongitudeSubdataset =
     560          40 :                                     "//HDFEOS/SWATHS/" + osSwathName +
     561          20 :                                     "/Geolocation_Fields/Longitude";
     562             :                                 oMetadata.osLatitudeSubdataset =
     563          40 :                                     "//HDFEOS/SWATHS/" + osSwathName +
     564          20 :                                     "/Geolocation_Fields/Latitude";
     565             : 
     566          84 :                                 for (const auto &oDimMap : aoDimensionMaps)
     567             :                                 {
     568          64 :                                     if (oDimMap.osDataDimName == osDataYDimName)
     569             :                                     {
     570          20 :                                         oMetadata.nLineOffset = oDimMap.nOffset;
     571          20 :                                         oMetadata.nLineStep =
     572          20 :                                             oDimMap.nIncrement;
     573             :                                     }
     574          44 :                                     else if (oDimMap.osDataDimName ==
     575             :                                              osDataXDimName)
     576             :                                     {
     577          20 :                                         oMetadata.nPixelOffset =
     578          20 :                                             oDimMap.nOffset;
     579          20 :                                         oMetadata.nPixelStep =
     580          20 :                                             oDimMap.nIncrement;
     581             :                                     }
     582             :                                 }
     583             :                             }
     584             :                         }
     585             : 
     586             :                         m_oMapSubdatasetNameToSwathDataFieldMetadata
     587          88 :                             ["//HDFEOS/SWATHS/" + osSwathName +
     588         132 :                              "/Data_Fields/" + osDataFieldName] =
     589          88 :                                 std::move(oMetadata);
     590             :                     }
     591             :                 }
     592             :             }
     593             :         }
     594             :     }
     595          14 : }
     596             : 
     597             : /************************************************************************/
     598             : /*                        GetSwathMetadata()                            */
     599             : /************************************************************************/
     600             : 
     601           3 : bool HDF5EOSParser::GetSwathMetadata(const std::string &osSwathName,
     602             :                                      SwathMetadata &swathMetadataOut) const
     603             : {
     604           3 :     const auto oIter = m_oMapSwathNameToSwathMetadata.find(osSwathName);
     605           3 :     if (oIter == m_oMapSwathNameToSwathMetadata.end())
     606           0 :         return false;
     607           3 :     swathMetadataOut = *(oIter->second.get());
     608           3 :     return true;
     609             : }
     610             : 
     611             : /************************************************************************/
     612             : /*                      GetSwathDataFieldMetadata()                     */
     613             : /************************************************************************/
     614             : 
     615          21 : bool HDF5EOSParser::GetSwathDataFieldMetadata(
     616             :     const char *pszSubdatasetName,
     617             :     SwathDataFieldMetadata &swathDataFieldMetadataOut) const
     618             : {
     619             :     const auto oIter =
     620          21 :         m_oMapSubdatasetNameToSwathDataFieldMetadata.find(pszSubdatasetName);
     621          21 :     if (oIter == m_oMapSubdatasetNameToSwathDataFieldMetadata.end())
     622          12 :         return false;
     623           9 :     swathDataFieldMetadataOut = oIter->second;
     624           9 :     return true;
     625             : }
     626             : 
     627             : /************************************************************************/
     628             : /*                    GetSwathGeolocationFieldMetadata()                */
     629             : /************************************************************************/
     630             : 
     631           2 : bool HDF5EOSParser::GetSwathGeolocationFieldMetadata(
     632             :     const char *pszSubdatasetName,
     633             :     SwathGeolocationFieldMetadata &swathGeolocationFieldMetadataOut) const
     634             : {
     635             :     const auto oIter = m_oMapSubdatasetNameToSwathGeolocationFieldMetadata.find(
     636           2 :         pszSubdatasetName);
     637           2 :     if (oIter == m_oMapSubdatasetNameToSwathGeolocationFieldMetadata.end())
     638           0 :         return false;
     639           2 :     swathGeolocationFieldMetadataOut = oIter->second;
     640           2 :     return true;
     641             : }
     642             : 
     643             : /************************************************************************/
     644             : /*                        GetGeoTransform()                             */
     645             : /************************************************************************/
     646             : 
     647           7 : bool HDF5EOSParser::GridMetadata::GetGeoTransform(
     648             :     double adfGeoTransform[6]) const
     649             : {
     650          14 :     if (nProjCode >= 0 && osGridOrigin == "HE5_HDFE_GD_UL" &&
     651          21 :         adfUpperLeftPointMeters.size() == 2 &&
     652           7 :         adfLowerRightPointMeters.size() == 2)
     653             :     {
     654           7 :         int nRasterXSize = 0;
     655           7 :         int nRasterYSize = 0;
     656             : 
     657          23 :         for (const auto &oDim : aoDimensions)
     658             :         {
     659          16 :             if (oDim.osName == "XDim")
     660           7 :                 nRasterXSize = oDim.nSize;
     661           9 :             else if (oDim.osName == "YDim")
     662           7 :                 nRasterYSize = oDim.nSize;
     663             :         }
     664           7 :         if (nRasterXSize <= 0 || nRasterYSize <= 0)
     665           0 :             return false;
     666           7 :         if (nProjCode == 0)  // GEO
     667             :         {
     668           2 :             adfGeoTransform[0] = CPLPackedDMSToDec(adfUpperLeftPointMeters[0]);
     669           2 :             adfGeoTransform[1] =
     670           2 :                 (CPLPackedDMSToDec(adfLowerRightPointMeters[0]) -
     671           2 :                  CPLPackedDMSToDec(adfUpperLeftPointMeters[0])) /
     672             :                 nRasterXSize;
     673           2 :             adfGeoTransform[2] = 0;
     674           2 :             adfGeoTransform[3] = CPLPackedDMSToDec(adfUpperLeftPointMeters[1]);
     675           2 :             adfGeoTransform[4] = 0;
     676           2 :             adfGeoTransform[5] =
     677           2 :                 (CPLPackedDMSToDec(adfLowerRightPointMeters[1]) -
     678           2 :                  CPLPackedDMSToDec(adfUpperLeftPointMeters[1])) /
     679             :                 nRasterYSize;
     680             :         }
     681             :         else
     682             :         {
     683           5 :             adfGeoTransform[0] = adfUpperLeftPointMeters[0];
     684           5 :             adfGeoTransform[1] =
     685           5 :                 (adfLowerRightPointMeters[0] - adfUpperLeftPointMeters[0]) /
     686             :                 nRasterXSize;
     687           5 :             adfGeoTransform[2] = 0;
     688           5 :             adfGeoTransform[3] = adfUpperLeftPointMeters[1];
     689           5 :             adfGeoTransform[4] = 0;
     690           5 :             adfGeoTransform[5] =
     691           5 :                 (adfLowerRightPointMeters[1] - adfUpperLeftPointMeters[1]) /
     692             :                 nRasterYSize;
     693             :         }
     694           7 :         return true;
     695             :     }
     696           0 :     return false;
     697             : }
     698             : 
     699             : /************************************************************************/
     700             : /*                              GetSRS()                                */
     701             : /************************************************************************/
     702             : 
     703           7 : std::unique_ptr<OGRSpatialReference> HDF5EOSParser::GridMetadata::GetSRS() const
     704             : {
     705          14 :     std::vector<double> l_adfProjParams = adfProjParams;
     706           7 :     l_adfProjParams.resize(15);
     707          14 :     auto poSRS = std::make_unique<OGRSpatialReference>();
     708           7 :     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     709          14 :     if (poSRS->importFromUSGS(nProjCode, nZone, l_adfProjParams.data(),
     710          14 :                               nSphereCode) == OGRERR_NONE)
     711             :     {
     712           7 :         return poSRS;
     713             :     }
     714           0 :     return nullptr;
     715             : }

Generated by: LCOV version 1.14