LCOV - code coverage report
Current view: top level - frmts/hdf5 - s100.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 170 211 80.6 %
Date: 2024-05-03 15:49:35 Functions: 11 11 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Hierarchical Data Format Release 5 (HDF5)
       4             :  * Purpose:  Read S100 bathymetric datasets.
       5             :  * Author:   Even Rouault <even dot rouault at spatialys dot com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2023, Even Rouault <even dot rouault at spatialys dot com>
       9             :  *
      10             :  * Permission is hereby granted, free of charge, to any person obtaining a
      11             :  * copy of this software and associated documentation files (the "Software"),
      12             :  * to deal in the Software without restriction, including without limitation
      13             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      14             :  * and/or sell copies of the Software, and to permit persons to whom the
      15             :  * Software is furnished to do so, subject to the following conditions:
      16             :  *
      17             :  * The above copyright notice and this permission notice shall be included
      18             :  * in all copies or substantial portions of the Software.
      19             :  *
      20             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      21             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      22             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      23             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      24             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      25             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      26             :  * DEALINGS IN THE SOFTWARE.
      27             :  ****************************************************************************/
      28             : 
      29             : #include "s100.h"
      30             : #include "hdf5dataset.h"
      31             : 
      32             : #include <algorithm>
      33             : #include <cmath>
      34             : 
      35             : /************************************************************************/
      36             : /*                       S100BaseDataset()                              */
      37             : /************************************************************************/
      38             : 
      39          19 : S100BaseDataset::S100BaseDataset(const std::string &osFilename)
      40          19 :     : m_osFilename(osFilename)
      41             : {
      42          19 : }
      43             : 
      44             : /************************************************************************/
      45             : /*                              Init()                                  */
      46             : /************************************************************************/
      47             : 
      48          19 : bool S100BaseDataset::Init()
      49             : {
      50             :     // Open the file as an HDF5 file.
      51          19 :     hid_t fapl = H5Pcreate(H5P_FILE_ACCESS);
      52          19 :     H5Pset_driver(fapl, HDF5GetFileDriver(), nullptr);
      53          19 :     hid_t hHDF5 = H5Fopen(m_osFilename.c_str(), H5F_ACC_RDONLY, fapl);
      54          19 :     H5Pclose(fapl);
      55          19 :     if (hHDF5 < 0)
      56           0 :         return false;
      57             : 
      58          38 :     auto poSharedResources = GDAL::HDF5SharedResources::Create(m_osFilename);
      59          19 :     poSharedResources->m_hHDF5 = hHDF5;
      60             : 
      61          19 :     m_poRootGroup = HDF5Dataset::OpenGroup(poSharedResources);
      62          19 :     if (m_poRootGroup == nullptr)
      63           0 :         return false;
      64             : 
      65          19 :     S100ReadSRS(m_poRootGroup.get(), m_oSRS);
      66             : 
      67          19 :     S100ReadVerticalDatum(this, m_poRootGroup.get());
      68             : 
      69             :     m_osMetadataFile =
      70          19 :         S100ReadMetadata(this, m_osFilename, m_poRootGroup.get());
      71             : 
      72          19 :     return true;
      73             : }
      74             : 
      75             : /************************************************************************/
      76             : /*                          GetGeoTransform()                           */
      77             : /************************************************************************/
      78             : 
      79          10 : CPLErr S100BaseDataset::GetGeoTransform(double *padfGeoTransform)
      80             : 
      81             : {
      82          10 :     if (m_bHasGT)
      83             :     {
      84          10 :         memcpy(padfGeoTransform, m_adfGeoTransform, sizeof(double) * 6);
      85          10 :         return CE_None;
      86             :     }
      87             : 
      88           0 :     return GDALPamDataset::GetGeoTransform(padfGeoTransform);
      89             : }
      90             : 
      91             : /************************************************************************/
      92             : /*                         GetSpatialRef()                              */
      93             : /************************************************************************/
      94             : 
      95           9 : const OGRSpatialReference *S100BaseDataset::GetSpatialRef() const
      96             : {
      97           9 :     if (!m_oSRS.IsEmpty())
      98           9 :         return &m_oSRS;
      99           0 :     return GDALPamDataset::GetSpatialRef();
     100             : }
     101             : 
     102             : /************************************************************************/
     103             : /*                         GetFileList()                                */
     104             : /************************************************************************/
     105             : 
     106           4 : char **S100BaseDataset::GetFileList()
     107             : {
     108           4 :     char **papszFileList = GDALPamDataset::GetFileList();
     109           4 :     if (!m_osMetadataFile.empty())
     110           4 :         papszFileList = CSLAddString(papszFileList, m_osMetadataFile.c_str());
     111           4 :     return papszFileList;
     112             : }
     113             : 
     114             : /************************************************************************/
     115             : /*                            S100ReadSRS()                             */
     116             : /************************************************************************/
     117             : 
     118          37 : bool S100ReadSRS(const GDALGroup *poRootGroup, OGRSpatialReference &oSRS)
     119             : {
     120             :     // Get SRS
     121          74 :     auto poHorizontalCRS = poRootGroup->GetAttribute("horizontalCRS");
     122          63 :     if (poHorizontalCRS &&
     123          63 :         poHorizontalCRS->GetDataType().GetClass() == GEDTC_NUMERIC)
     124             :     {
     125             :         // horizontalCRS is v2.2
     126          26 :         oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     127          26 :         if (oSRS.importFromEPSG(poHorizontalCRS->ReadAsInt()) != OGRERR_NONE)
     128             :         {
     129           0 :             oSRS.Clear();
     130             :         }
     131             :     }
     132             :     else
     133             :     {
     134             :         auto poHorizontalDatumReference =
     135          33 :             poRootGroup->GetAttribute("horizontalDatumReference");
     136             :         auto poHorizontalDatumValue =
     137          33 :             poRootGroup->GetAttribute("horizontalDatumValue");
     138          11 :         if (poHorizontalDatumReference && poHorizontalDatumValue)
     139             :         {
     140             :             const char *pszAuthName =
     141          11 :                 poHorizontalDatumReference->ReadAsString();
     142          11 :             const char *pszAuthCode = poHorizontalDatumValue->ReadAsString();
     143          11 :             if (pszAuthName && pszAuthCode)
     144             :             {
     145          11 :                 oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     146          22 :                 if (oSRS.SetFromUserInput(
     147          22 :                         (std::string(pszAuthName) + ':' + pszAuthCode).c_str(),
     148             :                         OGRSpatialReference::
     149          11 :                             SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
     150             :                     OGRERR_NONE)
     151             :                 {
     152           0 :                     oSRS.Clear();
     153             :                 }
     154             :             }
     155             :         }
     156             :     }
     157          74 :     return !oSRS.IsEmpty();
     158             : }
     159             : 
     160             : /************************************************************************/
     161             : /*               S100GetNumPointsLongitudinalLatitudinal()              */
     162             : /************************************************************************/
     163             : 
     164          43 : bool S100GetNumPointsLongitudinalLatitudinal(const GDALGroup *poGroup,
     165             :                                              int &nNumPointsLongitudinal,
     166             :                                              int &nNumPointsLatitudinal)
     167             : {
     168         129 :     auto poSpacingX = poGroup->GetAttribute("gridSpacingLongitudinal");
     169         129 :     auto poSpacingY = poGroup->GetAttribute("gridSpacingLatitudinal");
     170             :     auto poNumPointsLongitudinal =
     171         129 :         poGroup->GetAttribute("numPointsLongitudinal");
     172         129 :     auto poNumPointsLatitudinal = poGroup->GetAttribute("numPointsLatitudinal");
     173          43 :     if (poSpacingX &&
     174         129 :         poSpacingX->GetDataType().GetNumericDataType() == GDT_Float64 &&
     175          86 :         poSpacingY &&
     176          86 :         poSpacingY->GetDataType().GetNumericDataType() == GDT_Float64 &&
     177          43 :         poNumPointsLongitudinal &&
     178          43 :         GDALDataTypeIsInteger(
     179          86 :             poNumPointsLongitudinal->GetDataType().GetNumericDataType()) &&
     180         129 :         poNumPointsLatitudinal &&
     181          43 :         GDALDataTypeIsInteger(
     182          43 :             poNumPointsLatitudinal->GetDataType().GetNumericDataType()))
     183             :     {
     184          43 :         nNumPointsLongitudinal = poNumPointsLongitudinal->ReadAsInt();
     185          43 :         nNumPointsLatitudinal = poNumPointsLatitudinal->ReadAsInt();
     186             : 
     187             :         // Those are optional, but use them when available, to detect
     188             :         // potential inconsistency
     189         129 :         auto poEastBoundLongitude = poGroup->GetAttribute("eastBoundLongitude");
     190         129 :         auto poWestBoundLongitude = poGroup->GetAttribute("westBoundLongitude");
     191             :         auto poSouthBoundLongitude =
     192         129 :             poGroup->GetAttribute("southBoundLatitude");
     193          86 :         auto poNorthBoundLatitude = poGroup->GetAttribute("northBoundLatitude");
     194          43 :         if (poEastBoundLongitude &&
     195           0 :             GDALDataTypeIsFloating(
     196          43 :                 poEastBoundLongitude->GetDataType().GetNumericDataType()) &&
     197           0 :             poWestBoundLongitude &&
     198           0 :             GDALDataTypeIsFloating(
     199           0 :                 poWestBoundLongitude->GetDataType().GetNumericDataType()) &&
     200           0 :             poSouthBoundLongitude &&
     201           0 :             GDALDataTypeIsFloating(
     202           0 :                 poSouthBoundLongitude->GetDataType().GetNumericDataType()) &&
     203          43 :             poNorthBoundLatitude &&
     204           0 :             GDALDataTypeIsFloating(
     205           0 :                 poNorthBoundLatitude->GetDataType().GetNumericDataType()))
     206             :         {
     207           0 :             const double dfSpacingX = poSpacingX->ReadAsDouble();
     208           0 :             const double dfSpacingY = poSpacingY->ReadAsDouble();
     209             : 
     210           0 :             const double dfEast = poEastBoundLongitude->ReadAsDouble();
     211           0 :             const double dfWest = poWestBoundLongitude->ReadAsDouble();
     212           0 :             const double dfSouth = poSouthBoundLongitude->ReadAsDouble();
     213           0 :             const double dfNorth = poNorthBoundLatitude->ReadAsDouble();
     214           0 :             if (std::fabs((dfWest + nNumPointsLongitudinal * dfSpacingX) -
     215           0 :                           dfEast) < 5 * dfSpacingX &&
     216           0 :                 std::fabs((dfSouth + nNumPointsLatitudinal * dfSpacingY) -
     217           0 :                           dfNorth) < 5 * dfSpacingY)
     218             :             {
     219             :                 // We need up to 5 spacings for product
     220             :                 // S-111 Trial Data Set Release 1.1/111UK_20210401T000000Z_SolentAndAppr_dcf2.h5
     221             :             }
     222             :             else
     223             :             {
     224           0 :                 CPLError(
     225             :                     CE_Warning, CPLE_AppDefined,
     226             :                     "Caution: "
     227             :                     "eastBoundLongitude/westBoundLongitude/southBoundLatitude/"
     228             :                     "northBoundLatitude/gridSpacingLongitudinal/"
     229             :                     "gridSpacingLatitudinal/numPointsLongitudinal/"
     230             :                     "numPointsLatitudinal do not seem to be consistent");
     231           0 :                 CPLDebug("S100", "Computed east = %f. Actual = %f",
     232           0 :                          dfWest + nNumPointsLongitudinal * dfSpacingX, dfEast);
     233           0 :                 CPLDebug("S100", "Computed north = %f. Actual = %f",
     234           0 :                          dfSouth + nNumPointsLatitudinal * dfSpacingY, dfNorth);
     235             :             }
     236             :         }
     237             : 
     238          43 :         return true;
     239             :     }
     240           0 :     return false;
     241             : }
     242             : 
     243             : /************************************************************************/
     244             : /*                        S100GetGeoTransform()                         */
     245             : /************************************************************************/
     246             : 
     247          17 : bool S100GetGeoTransform(const GDALGroup *poGroup, double adfGeoTransform[6],
     248             :                          bool bNorthUp)
     249             : {
     250          51 :     auto poOriginX = poGroup->GetAttribute("gridOriginLongitude");
     251          51 :     auto poOriginY = poGroup->GetAttribute("gridOriginLatitude");
     252          51 :     auto poSpacingX = poGroup->GetAttribute("gridSpacingLongitudinal");
     253          51 :     auto poSpacingY = poGroup->GetAttribute("gridSpacingLatitudinal");
     254          17 :     if (poOriginX &&
     255          51 :         poOriginX->GetDataType().GetNumericDataType() == GDT_Float64 &&
     256          34 :         poOriginY &&
     257          34 :         poOriginY->GetDataType().GetNumericDataType() == GDT_Float64 &&
     258          34 :         poSpacingX &&
     259          34 :         poSpacingX->GetDataType().GetNumericDataType() == GDT_Float64 &&
     260          51 :         poSpacingY &&
     261          17 :         poSpacingY->GetDataType().GetNumericDataType() == GDT_Float64)
     262             :     {
     263          17 :         int nNumPointsLongitudinal = 0;
     264          17 :         int nNumPointsLatitudinal = 0;
     265          17 :         if (!S100GetNumPointsLongitudinalLatitudinal(
     266             :                 poGroup, nNumPointsLongitudinal, nNumPointsLatitudinal))
     267           0 :             return false;
     268             : 
     269          17 :         const double dfSpacingX = poSpacingX->ReadAsDouble();
     270          17 :         const double dfSpacingY = poSpacingY->ReadAsDouble();
     271             : 
     272          17 :         adfGeoTransform[0] = poOriginX->ReadAsDouble();
     273          17 :         adfGeoTransform[3] =
     274          17 :             poOriginY->ReadAsDouble() +
     275          17 :             (bNorthUp ? dfSpacingY * (nNumPointsLatitudinal - 1) : 0);
     276          17 :         adfGeoTransform[1] = dfSpacingX;
     277          17 :         adfGeoTransform[5] = bNorthUp ? -dfSpacingY : dfSpacingY;
     278             : 
     279             :         // From pixel-center convention to pixel-corner convention
     280          17 :         adfGeoTransform[0] -= adfGeoTransform[1] / 2;
     281          17 :         adfGeoTransform[3] -= adfGeoTransform[5] / 2;
     282             : 
     283          17 :         return true;
     284             :     }
     285           0 :     return false;
     286             : }
     287             : 
     288             : /************************************************************************/
     289             : /*                        S100GetDimensions()                           */
     290             : /************************************************************************/
     291             : 
     292          18 : bool S100GetDimensions(
     293             :     const GDALGroup *poGroup,
     294             :     std::vector<std::shared_ptr<GDALDimension>> &apoDims,
     295             :     std::vector<std::shared_ptr<GDALMDArray>> &apoIndexingVars)
     296             : {
     297          54 :     auto poOriginX = poGroup->GetAttribute("gridOriginLongitude");
     298          54 :     auto poOriginY = poGroup->GetAttribute("gridOriginLatitude");
     299          54 :     auto poSpacingX = poGroup->GetAttribute("gridSpacingLongitudinal");
     300          54 :     auto poSpacingY = poGroup->GetAttribute("gridSpacingLatitudinal");
     301          18 :     if (poOriginX &&
     302          54 :         poOriginX->GetDataType().GetNumericDataType() == GDT_Float64 &&
     303          36 :         poOriginY &&
     304          36 :         poOriginY->GetDataType().GetNumericDataType() == GDT_Float64 &&
     305          36 :         poSpacingX &&
     306          36 :         poSpacingX->GetDataType().GetNumericDataType() == GDT_Float64 &&
     307          54 :         poSpacingY &&
     308          18 :         poSpacingY->GetDataType().GetNumericDataType() == GDT_Float64)
     309             :     {
     310          18 :         int nNumPointsLongitudinal = 0;
     311          18 :         int nNumPointsLatitudinal = 0;
     312          18 :         if (!S100GetNumPointsLongitudinalLatitudinal(
     313             :                 poGroup, nNumPointsLongitudinal, nNumPointsLatitudinal))
     314           0 :             return false;
     315             : 
     316             :         {
     317             :             auto poDim = std::make_shared<GDALDimensionWeakIndexingVar>(
     318          36 :                 std::string(), "Y", GDAL_DIM_TYPE_HORIZONTAL_Y, std::string(),
     319          36 :                 nNumPointsLatitudinal);
     320             :             auto poIndexingVar = GDALMDArrayRegularlySpaced::Create(
     321          54 :                 std::string(), poDim->GetName(), poDim,
     322          72 :                 poOriginY->ReadAsDouble(), poSpacingY->ReadAsDouble(), 0);
     323          18 :             poDim->SetIndexingVariable(poIndexingVar);
     324          18 :             apoDims.emplace_back(poDim);
     325          18 :             apoIndexingVars.emplace_back(poIndexingVar);
     326             :         }
     327             : 
     328             :         {
     329             :             auto poDim = std::make_shared<GDALDimensionWeakIndexingVar>(
     330          36 :                 std::string(), "X", GDAL_DIM_TYPE_HORIZONTAL_X, std::string(),
     331          36 :                 nNumPointsLongitudinal);
     332             :             auto poIndexingVar = GDALMDArrayRegularlySpaced::Create(
     333          54 :                 std::string(), poDim->GetName(), poDim,
     334          72 :                 poOriginX->ReadAsDouble(), poSpacingX->ReadAsDouble(), 0);
     335          18 :             poDim->SetIndexingVariable(poIndexingVar);
     336          18 :             apoDims.emplace_back(poDim);
     337          18 :             apoIndexingVars.emplace_back(poIndexingVar);
     338             :         }
     339             : 
     340          18 :         return true;
     341             :     }
     342           0 :     return false;
     343             : }
     344             : 
     345             : /************************************************************************/
     346             : /*                       S100ReadVerticalDatum()                        */
     347             : /************************************************************************/
     348             : 
     349          19 : void S100ReadVerticalDatum(GDALDataset *poDS, const GDALGroup *poRootGroup)
     350             : {
     351             :     // https://iho.int/uploads/user/pubs/standards/s-100/S-100_5.0.0_Final_Clean_Web.pdf
     352             :     // Table S100_VerticalAndSoundingDatum page 20
     353             :     static const struct
     354             :     {
     355             :         int nCode;
     356             :         const char *pszMeaning;
     357             :         const char *pszAbbrev;
     358             :     } asVerticalDatums[] = {
     359             :         {1, "meanLowWaterSprings", "MLWS"},
     360             :         {2, "meanLowerLowWaterSprings", nullptr},
     361             :         {3, "meanSeaLevel", "MSL"},
     362             :         {4, "lowestLowWater", nullptr},
     363             :         {5, "meanLowWater", "MLW"},
     364             :         {6, "lowestLowWaterSprings", nullptr},
     365             :         {7, "approximateMeanLowWaterSprings", nullptr},
     366             :         {8, "indianSpringLowWater", nullptr},
     367             :         {9, "lowWaterSprings", nullptr},
     368             :         {10, "approximateLowestAstronomicalTide", nullptr},
     369             :         {11, "nearlyLowestLowWater", nullptr},
     370             :         {12, "meanLowerLowWater", "MLLW"},
     371             :         {13, "lowWater", "LW"},
     372             :         {14, "approximateMeanLowWater", nullptr},
     373             :         {15, "approximateMeanLowerLowWater", nullptr},
     374             :         {16, "meanHighWater", "MHW"},
     375             :         {17, "meanHighWaterSprings", "MHWS"},
     376             :         {18, "highWater", "HW"},
     377             :         {19, "approximateMeanSeaLevel", nullptr},
     378             :         {20, "highWaterSprings", nullptr},
     379             :         {21, "meanHigherHighWater", "MHHW"},
     380             :         {22, "equinoctialSpringLowWater", nullptr},
     381             :         {23, "lowestAstronomicalTide", "LAT"},
     382             :         {24, "localDatum", nullptr},
     383             :         {25, "internationalGreatLakesDatum1985", nullptr},
     384             :         {26, "meanWaterLevel", nullptr},
     385             :         {27, "lowerLowWaterLargeTide", nullptr},
     386             :         {28, "higherHighWaterLargeTide", nullptr},
     387             :         {29, "nearlyHighestHighWater", nullptr},
     388             :         {30, "highestAstronomicalTide", "HAT"},
     389             :         {44, "balticSeaChartDatum2000", nullptr},
     390             :         {46, "internationalGreatLakesDatum2020", nullptr},
     391             :     };
     392             : 
     393          57 :     auto poVerticalDatum = poRootGroup->GetAttribute("verticalDatum");
     394          38 :     if (poVerticalDatum &&
     395          38 :         poVerticalDatum->GetDataType().GetClass() == GEDTC_NUMERIC)
     396             :     {
     397          19 :         bool bFound = false;
     398          19 :         const auto nVal = poVerticalDatum->ReadAsInt();
     399         228 :         for (const auto &sVerticalDatum : asVerticalDatums)
     400             :         {
     401         228 :             if (sVerticalDatum.nCode == nVal)
     402             :             {
     403          19 :                 bFound = true;
     404          19 :                 poDS->GDALDataset::SetMetadataItem("VERTICAL_DATUM_MEANING",
     405          19 :                                                    sVerticalDatum.pszMeaning);
     406          19 :                 if (sVerticalDatum.pszAbbrev)
     407          19 :                     poDS->GDALDataset::SetMetadataItem(
     408          19 :                         "VERTICAL_DATUM_ABBREV", sVerticalDatum.pszAbbrev);
     409          19 :                 break;
     410             :             }
     411             :         }
     412          19 :         if (!bFound)
     413             :         {
     414           0 :             poDS->GDALDataset::SetMetadataItem("verticalDatum",
     415             :                                                CPLSPrintf("%d", nVal));
     416             :         }
     417             :     }
     418          19 : }
     419             : 
     420             : /************************************************************************/
     421             : /*                         S100ReadMetadata()                           */
     422             : /************************************************************************/
     423             : 
     424          19 : std::string S100ReadMetadata(GDALDataset *poDS, const std::string &osFilename,
     425             :                              const GDALGroup *poRootGroup)
     426             : {
     427          19 :     std::string osMetadataFile;
     428         199 :     for (const auto &poAttr : poRootGroup->GetAttributes())
     429             :     {
     430         180 :         const auto &osName = poAttr->GetName();
     431         180 :         if (osName == "metadata")
     432             :         {
     433          19 :             const char *pszVal = poAttr->ReadAsString();
     434          19 :             if (pszVal && pszVal[0])
     435             :             {
     436             :                 osMetadataFile = CPLFormFilename(CPLGetPath(osFilename.c_str()),
     437          19 :                                                  pszVal, nullptr);
     438             :                 VSIStatBufL sStat;
     439          19 :                 if (VSIStatL(osMetadataFile.c_str(), &sStat) != 0)
     440             :                 {
     441             :                     // Test products from https://data.admiralty.co.uk/portal/apps/sites/#/marine-data-portal/pages/s-100
     442             :                     // advertise a metadata filename starting with "MD_", per the spec,
     443             :                     // but the actual filename does not start with "MD_"...
     444           0 :                     if (STARTS_WITH(pszVal, "MD_"))
     445             :                     {
     446             :                         osMetadataFile =
     447             :                             CPLFormFilename(CPLGetPath(osFilename.c_str()),
     448           0 :                                             pszVal + strlen("MD_"), nullptr);
     449           0 :                         if (VSIStatL(osMetadataFile.c_str(), &sStat) != 0)
     450             :                         {
     451           0 :                             osMetadataFile.clear();
     452             :                         }
     453             :                     }
     454             :                     else
     455             :                     {
     456           0 :                         osMetadataFile.clear();
     457             :                     }
     458             :                 }
     459             :             }
     460             :         }
     461         308 :         else if (osName != "horizontalCRS" &&
     462         289 :                  osName != "horizontalDatumReference" &&
     463         279 :                  osName != "horizontalDatumValue" &&
     464         255 :                  osName != "productSpecification" &&
     465         236 :                  osName != "eastBoundLongitude" &&
     466         236 :                  osName != "northBoundLatitude" &&
     467         236 :                  osName != "southBoundLatitude" &&
     468         354 :                  osName != "westBoundLongitude" && osName != "extentTypeCode" &&
     469         312 :                  osName != "verticalCS" && osName != "verticalCoordinateBase" &&
     470         398 :                  osName != "verticalDatumReference" &&
     471          76 :                  osName != "verticalDatum")
     472             :         {
     473          57 :             const char *pszVal = poAttr->ReadAsString();
     474          57 :             if (pszVal && pszVal[0])
     475          57 :                 poDS->GDALDataset::SetMetadataItem(osName.c_str(), pszVal);
     476             :         }
     477             :     }
     478          19 :     return osMetadataFile;
     479             : }

Generated by: LCOV version 1.14