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

Generated by: LCOV version 1.14