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: 2024-11-21 22:18:42 Functions: 12 12 100.0 %

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

Generated by: LCOV version 1.14