LCOV - code coverage report
Current view: top level - apps - gdalalg_mdim_mosaic.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 409 461 88.7 %
Date: 2026-04-03 14:38:35 Functions: 6 6 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  gdal "mdim mosaic" subcommand
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdalalg_mdim_mosaic.h"
      14             : 
      15             : #include "cpl_conv.h"
      16             : #include "cpl_vsi_virtual.h"
      17             : #include "gdal_priv.h"
      18             : #include "vrtdataset.h"
      19             : 
      20             : #include <algorithm>
      21             : #include <cmath>
      22             : #include <optional>
      23             : 
      24             : //! @cond Doxygen_Suppress
      25             : 
      26             : #ifndef _
      27             : #define _(x) (x)
      28             : #endif
      29             : 
      30             : /************************************************************************/
      31             : /*          GDALMdimMosaicAlgorithm::GDALMdimMosaicAlgorithm()          */
      32             : /************************************************************************/
      33             : 
      34          76 : GDALMdimMosaicAlgorithm::GDALMdimMosaicAlgorithm()
      35          76 :     : GDALAlgorithm(NAME, DESCRIPTION, HELP_URL)
      36             : {
      37          76 :     AddProgressArg();
      38          76 :     AddOutputFormatArg(&m_outputFormat)
      39             :         .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
      40         152 :                          {GDAL_DCAP_CREATE_MULTIDIMENSIONAL});
      41          76 :     AddOpenOptionsArg(&m_openOptions);
      42          76 :     AddInputFormatsArg(&m_inputFormats)
      43             :         .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
      44         152 :                          {GDAL_ALG_DCAP_RASTER_OR_MULTIDIM_RASTER});
      45          76 :     AddInputDatasetArg(&m_inputDatasets, GDAL_OF_MULTIDIM_RASTER)
      46          76 :         .SetDatasetInputFlags(GADV_NAME)
      47          76 :         .SetDatasetOutputFlags(0)
      48          76 :         .SetAutoOpenDataset(false)
      49          76 :         .SetMinCount(1);
      50          76 :     AddOutputDatasetArg(&m_outputDataset, GDAL_OF_MULTIDIM_RASTER);
      51          76 :     AddCreationOptionsArg(&m_creationOptions);
      52          76 :     AddOverwriteArg(&m_overwrite);
      53          76 :     AddArrayNameArg(&m_array, _("Name of array(s) to mosaic."));
      54          76 : }
      55             : 
      56             : /************************************************************************/
      57             : /*                          GetDimensionDesc()                          */
      58             : /************************************************************************/
      59             : 
      60             : std::optional<GDALMdimMosaicAlgorithm::DimensionDesc>
      61          73 : GDALMdimMosaicAlgorithm::GetDimensionDesc(
      62             :     const std::string &osDSName,
      63             :     const std::shared_ptr<GDALDimension> &poDim) const
      64             : {
      65         146 :     auto poVar = poDim->GetIndexingVariable();
      66          73 :     if (poVar)
      67             :     {
      68          68 :         if (poVar->GetDimensionCount() != 1)
      69             :         {
      70           0 :             ReportError(
      71             :                 CE_Failure, CPLE_AppDefined,
      72             :                 "Dataset %s: indexing variable %s of dimension %s is not 1D",
      73           0 :                 osDSName.c_str(), poVar->GetName().c_str(),
      74           0 :                 poDim->GetName().c_str());
      75           0 :             return std::nullopt;
      76             :         }
      77          68 :         if (poVar->GetDataType().GetClass() != GEDTC_NUMERIC)
      78             :         {
      79           2 :             ReportError(
      80             :                 CE_Failure, CPLE_AppDefined,
      81             :                 "Dataset %s: indexing variable %s of dimension %s has a "
      82             :                 "non-numeric data type",
      83           1 :                 osDSName.c_str(), poVar->GetName().c_str(),
      84           1 :                 poDim->GetName().c_str());
      85           1 :             return std::nullopt;
      86             :         }
      87             :     }
      88         144 :     DimensionDesc desc;
      89          72 :     desc.osName = poDim->GetName();
      90          72 :     desc.osType = poDim->GetType();
      91          72 :     desc.osDirection = poDim->GetDirection();
      92          72 :     const auto nSize = poDim->GetSize();
      93          72 :     if (poVar)
      94          67 :         desc.attributes = poVar->GetAttributes();
      95          72 :     CPLAssert(nSize > 0);
      96          72 :     desc.nSize = nSize;
      97          72 :     desc.bHasIndexingVar = poVar != nullptr;
      98          72 :     if (!poVar)
      99             :     {
     100           5 :         desc.dfStart = 0;
     101           5 :         desc.dfIncrement = 1;
     102             :     }
     103         101 :     else if (nSize <= 2 ||
     104          34 :              !poVar->IsRegularlySpaced(desc.dfStart, desc.dfIncrement))
     105             :     {
     106          45 :         constexpr uint64_t LIMIT = 100 * 1000 * 1000;
     107          45 :         if (nSize > LIMIT)
     108             :         {
     109           0 :             ReportError(CE_Failure, CPLE_AppDefined,
     110             :                         "Dataset %s: indexing variable %s of dimension %s has "
     111             :                         "too large size",
     112           0 :                         osDSName.c_str(), poVar->GetName().c_str(),
     113             :                         desc.osName.c_str());
     114           0 :             return std::nullopt;
     115             :         }
     116          45 :         std::vector<double> adfValues(static_cast<size_t>(nSize));
     117          45 :         GUInt64 arrayStartIdx[] = {0};
     118          45 :         size_t anCount[] = {adfValues.size()};
     119          45 :         GInt64 arrayStep[] = {1};
     120          45 :         GPtrDiff_t bufferStride[] = {1};
     121          90 :         if (!poVar->Read(arrayStartIdx, anCount, arrayStep, bufferStride,
     122          90 :                          GDALExtendedDataType::Create(GDT_Float64),
     123          45 :                          adfValues.data()))
     124             :         {
     125           0 :             return std::nullopt;
     126             :         }
     127          45 :         if (std::isnan(adfValues[0]))
     128             :         {
     129           0 :             ReportError(CE_Failure, CPLE_AppDefined,
     130             :                         "Dataset %s: indexing variable %s of dimension %s has "
     131             :                         "NaN values",
     132           0 :                         osDSName.c_str(), poVar->GetName().c_str(),
     133             :                         desc.osName.c_str());
     134           0 :             return std::nullopt;
     135             :         }
     136          45 :         if (nSize > 1)
     137             :         {
     138          13 :             const int nSign = adfValues[1] > adfValues[0] ? 1 : -1;
     139          13 :             if (std::isnan(adfValues[1]))
     140             :             {
     141           0 :                 ReportError(CE_Failure, CPLE_AppDefined,
     142             :                             "Dataset %s: indexing variable %s of dimension %s "
     143             :                             "has NaN values",
     144           0 :                             osDSName.c_str(), poVar->GetName().c_str(),
     145             :                             desc.osName.c_str());
     146           0 :                 return std::nullopt;
     147             :             }
     148          25 :             for (size_t i = 2; i < nSize; ++i)
     149             :             {
     150          12 :                 if (std::isnan(adfValues[i]))
     151             :                 {
     152           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
     153             :                                 "Dataset %s: indexing variable %s of dimension "
     154             :                                 "%s has NaN values",
     155           0 :                                 osDSName.c_str(), poVar->GetName().c_str(),
     156             :                                 desc.osName.c_str());
     157           0 :                     return std::nullopt;
     158             :                 }
     159          12 :                 const int nSign2 = adfValues[i] > adfValues[i - 1] ? 1 : -1;
     160          12 :                 if (nSign != nSign2)
     161             :                 {
     162           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
     163             :                                 "Dataset %s: indexing variable %s of dimension "
     164             :                                 "%s is not strictly increasing or decreasing",
     165           0 :                                 osDSName.c_str(), poVar->GetName().c_str(),
     166             :                                 desc.osName.c_str());
     167           0 :                     return std::nullopt;
     168             :                 }
     169             :             }
     170          13 :             desc.nProgressionSign = nSign;
     171             :         }
     172          45 :         std::sort(adfValues.begin(), adfValues.end());
     173          45 :         desc.aaValues.push_back(std::move(adfValues));
     174             :     }
     175          72 :     return desc;
     176             : }
     177             : 
     178             : /************************************************************************/
     179             : /*           GDALMdimMosaicAlgorithm::BuildArrayParameters()            */
     180             : /************************************************************************/
     181             : 
     182          33 : bool GDALMdimMosaicAlgorithm::BuildArrayParameters(
     183             :     const CPLStringList &aosInputDatasetNames,
     184             :     std::vector<ArrayParameters> &aoArrayParameters)
     185             : {
     186          72 :     for (const char *pszDatasetName : cpl::Iterate(aosInputDatasetNames))
     187             :     {
     188             :         auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
     189             :             pszDatasetName, GDAL_OF_MULTIDIM_RASTER | GDAL_OF_VERBOSE_ERROR,
     190         122 :             CPLStringList(m_inputFormats).List(),
     191         122 :             CPLStringList(m_openOptions).List(), nullptr));
     192          61 :         if (!poDS)
     193           0 :             return false;
     194          61 :         auto poRG = poDS->GetRootGroup();
     195          61 :         if (!poRG)
     196             :         {
     197           0 :             ReportError(CE_Failure, CPLE_AppDefined,
     198             :                         "Cannot get root group for dataset %s", pszDatasetName);
     199           0 :             return false;
     200             :         }
     201          61 :         std::vector<std::shared_ptr<GDALMDArray>> apoArrays;
     202          61 :         if (!m_array.empty())
     203             :         {
     204         110 :             for (const auto &array : m_array)
     205             :             {
     206          58 :                 auto poArray = poRG->OpenMDArrayFromFullname(array);
     207          58 :                 if (!poArray)
     208             :                 {
     209           2 :                     ReportError(CE_Failure, CPLE_AppDefined,
     210             :                                 "Cannot find array %s in dataset %s",
     211             :                                 array.c_str(), pszDatasetName);
     212           2 :                     return false;
     213             :                 }
     214          56 :                 apoArrays.push_back(std::move(poArray));
     215             :             }
     216             :         }
     217             :         else
     218             :         {
     219          21 :             for (const std::string &arrayName :
     220          49 :                  poRG->GetMDArrayFullNamesRecursive())
     221             :             {
     222          21 :                 auto poArray = poRG->OpenMDArrayFromFullname(arrayName);
     223          21 :                 if (!poArray)
     224             :                 {
     225           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
     226             :                                 "Cannot open array %s of dataset %s",
     227             :                                 arrayName.c_str(), pszDatasetName);
     228           0 :                     return false;
     229             :                 }
     230          21 :                 if (poArray->GetDimensionCount() < 2)
     231          14 :                     continue;
     232           7 :                 m_array.push_back(arrayName);
     233           7 :                 apoArrays.push_back(std::move(poArray));
     234             :             }
     235           7 :             if (apoArrays.empty())
     236             :             {
     237           1 :                 ReportError(
     238             :                     CE_Failure, CPLE_AppDefined,
     239             :                     "No array of dimension count >= 2 found in dataset %s",
     240             :                     pszDatasetName);
     241           1 :                 return false;
     242             :             }
     243             :         }
     244             : 
     245          58 :         if (aoArrayParameters.empty())
     246          30 :             aoArrayParameters.resize(apoArrays.size());
     247             : 
     248         101 :         for (size_t iArray = 0; iArray < apoArrays.size(); ++iArray)
     249             :         {
     250          62 :             auto &arrayParameters = aoArrayParameters[iArray];
     251          62 :             auto &poArray = apoArrays[iArray];
     252          62 :             if (poArray->GetDimensionCount() == 0)
     253             :             {
     254           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     255             :                             "Array %s of dataset %s has no dimensions",
     256           1 :                             poArray->GetName().c_str(), pszDatasetName);
     257          19 :                 return false;
     258             :             }
     259             : 
     260          61 :             std::vector<SourceShortDimDesc> aoSourceShortDimDesc;
     261             :             const auto AddToSourceShortDimDesc =
     262          72 :                 [&aoSourceShortDimDesc](const DimensionDesc &dimDesc,
     263          72 :                                         uint64_t nSize)
     264             :             {
     265          72 :                 SourceShortDimDesc sourceDesc;
     266          72 :                 sourceDesc.nSize = nSize;
     267          72 :                 sourceDesc.bIsRegularlySpaced = dimDesc.aaValues.empty();
     268          72 :                 if (sourceDesc.bIsRegularlySpaced)
     269          27 :                     sourceDesc.dfStart = dimDesc.dfStart;
     270             :                 else
     271          45 :                     sourceDesc.dfStart = dimDesc.aaValues[0][0];
     272          72 :                 aoSourceShortDimDesc.push_back(std::move(sourceDesc));
     273          72 :             };
     274             : 
     275          61 :             const auto anBlockSize = poArray->GetBlockSize();
     276          61 :             CPLAssert(anBlockSize.size() == poArray->GetDimensionCount());
     277             : 
     278          61 :             if (!arrayParameters.poFirstSourceArray)
     279             :             {
     280          31 :                 arrayParameters.poFirstSourceArray = poArray;
     281          31 :                 CPLAssert(arrayParameters.mosaicDimensions.empty());
     282          31 :                 size_t iDim = 0;
     283          70 :                 for (auto &poDim : poArray->GetDimensions())
     284             :                 {
     285          80 :                     auto descOpt = GetDimensionDesc(pszDatasetName, poDim);
     286          40 :                     if (!descOpt.has_value())
     287           1 :                         return false;
     288          39 :                     auto &desc = descOpt.value();
     289          39 :                     AddToSourceShortDimDesc(desc, poDim->GetSize());
     290          39 :                     desc.nBlockSize = anBlockSize[iDim];
     291          39 :                     arrayParameters.mosaicDimensions.push_back(std::move(desc));
     292          39 :                     ++iDim;
     293             :                 }
     294             :             }
     295             :             else
     296             :             {
     297          60 :                 if (poArray->GetDimensionCount() !=
     298          30 :                     arrayParameters.mosaicDimensions.size())
     299             :                 {
     300           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
     301             :                                 "Array %s of dataset %s does not have the same "
     302             :                                 "number of dimensions as in other datasets",
     303           0 :                                 poArray->GetName().c_str(), pszDatasetName);
     304          17 :                     return false;
     305             :                 }
     306          30 :                 if (poArray->GetDataType() !=
     307          30 :                     arrayParameters.poFirstSourceArray->GetDataType())
     308             :                 {
     309           1 :                     ReportError(CE_Failure, CPLE_AppDefined,
     310             :                                 "Array %s of dataset %s does not have the same "
     311             :                                 "data type as in other datasets",
     312           1 :                                 poArray->GetName().c_str(), pszDatasetName);
     313           1 :                     return false;
     314             :                 }
     315             :                 const void *poFirstRawNoData =
     316          29 :                     arrayParameters.poFirstSourceArray->GetRawNoDataValue();
     317          29 :                 const void *poRawNoData = poArray->GetRawNoDataValue();
     318          31 :                 if (!((!poFirstRawNoData && !poRawNoData) ||
     319           2 :                       (poFirstRawNoData && poRawNoData &&
     320           1 :                        memcmp(poFirstRawNoData, poRawNoData,
     321           1 :                               poArray->GetDataType().GetSize()) == 0)))
     322             :                 {
     323           3 :                     ReportError(CE_Failure, CPLE_AppDefined,
     324             :                                 "Array %s of dataset %s does not have the same "
     325             :                                 "nodata value as in other datasets",
     326           3 :                                 poArray->GetName().c_str(), pszDatasetName);
     327           3 :                     return false;
     328             :                 }
     329             :                 const std::vector<std::shared_ptr<GDALDimension>> apoDims =
     330          26 :                     poArray->GetDimensions();
     331          47 :                 for (size_t iDim = 0;
     332          47 :                      iDim < arrayParameters.mosaicDimensions.size(); ++iDim)
     333             :                 {
     334             :                     DimensionDesc &desc =
     335          34 :                         arrayParameters.mosaicDimensions[iDim];
     336          34 :                     auto &poDim = apoDims[iDim];
     337          34 :                     if (poDim->GetName() != desc.osName)
     338             :                     {
     339           1 :                         ReportError(
     340             :                             CE_Failure, CPLE_AppDefined,
     341             :                             "Dimension %d of array %s of dataset %s does "
     342             :                             "not have the same name as in other datasets",
     343           1 :                             static_cast<int>(iDim), poArray->GetName().c_str(),
     344             :                             pszDatasetName);
     345          13 :                         return false;
     346             :                     }
     347          33 :                     if (desc.nBlockSize != anBlockSize[iDim])
     348           4 :                         desc.nBlockSize = 0;
     349             : 
     350             :                     auto descThisDatasetOpt =
     351          66 :                         GetDimensionDesc(pszDatasetName, poDim);
     352          33 :                     if (!descThisDatasetOpt.has_value())
     353           0 :                         return false;
     354          33 :                     auto &descThisDataset = descThisDatasetOpt.value();
     355          33 :                     AddToSourceShortDimDesc(descThisDataset, poDim->GetSize());
     356          33 :                     if (descThisDataset.bHasIndexingVar &&
     357          31 :                         !desc.bHasIndexingVar)
     358             :                     {
     359           2 :                         ReportError(CE_Failure, CPLE_AppDefined,
     360             :                                     "Dimension %s of array %s of dataset %s "
     361             :                                     "has an indexing variable, contrary "
     362             :                                     "to other datasets",
     363           1 :                                     poDim->GetName().c_str(),
     364           1 :                                     poArray->GetName().c_str(), pszDatasetName);
     365           1 :                         return false;
     366             :                     }
     367          32 :                     else if (!descThisDataset.bHasIndexingVar &&
     368           2 :                              desc.bHasIndexingVar)
     369             :                     {
     370           2 :                         ReportError(
     371             :                             CE_Failure, CPLE_AppDefined,
     372             :                             "Dimension %s of array %s of dataset %s "
     373             :                             "does not have an indexing variable, contrary "
     374             :                             "to other datasets",
     375           1 :                             poDim->GetName().c_str(),
     376           1 :                             poArray->GetName().c_str(), pszDatasetName);
     377           1 :                         return false;
     378             :                     }
     379          31 :                     else if (!desc.bHasIndexingVar &&
     380           1 :                              descThisDataset.nSize != desc.nSize)
     381             :                     {
     382           2 :                         ReportError(
     383             :                             CE_Failure, CPLE_AppDefined,
     384             :                             "Dimension %s of array %s of dataset %s "
     385             :                             "does not have the same size as in other datasets",
     386           1 :                             poDim->GetName().c_str(),
     387           1 :                             poArray->GetName().c_str(), pszDatasetName);
     388           1 :                         return false;
     389             :                     }
     390          30 :                     else if (desc.bHasIndexingVar && desc.aaValues.empty())
     391             :                     {
     392          10 :                         if (!descThisDataset.aaValues.empty())
     393             :                         {
     394           2 :                             ReportError(
     395             :                                 CE_Failure, CPLE_AppDefined,
     396             :                                 "Dimension %s of array %s of dataset %s "
     397             :                                 "has irregularly-spaced values, contrary "
     398             :                                 "to other datasets",
     399           1 :                                 poDim->GetName().c_str(),
     400           1 :                                 poArray->GetName().c_str(), pszDatasetName);
     401           1 :                             return false;
     402             :                         }
     403           9 :                         if (std::fabs(descThisDataset.dfIncrement -
     404           9 :                                       desc.dfIncrement) >
     405           9 :                             1e-10 * std::fabs(desc.dfIncrement))
     406             :                         {
     407           2 :                             ReportError(
     408             :                                 CE_Failure, CPLE_AppDefined,
     409             :                                 "Dimension %s of array %s of dataset %s is "
     410             :                                 "indexed by a variable with spacing %g, "
     411             :                                 "whereas it is %g in other datasets",
     412           1 :                                 poDim->GetName().c_str(),
     413           1 :                                 poArray->GetName().c_str(), pszDatasetName,
     414             :                                 descThisDataset.dfIncrement, desc.dfIncrement);
     415           1 :                             return false;
     416             :                         }
     417           8 :                         const double dfPos =
     418           8 :                             (descThisDataset.dfStart - desc.dfStart) /
     419           8 :                             desc.dfIncrement;
     420           8 :                         if (std::fabs(std::round(dfPos) - dfPos) > 1e-3)
     421             :                         {
     422           2 :                             ReportError(CE_Failure, CPLE_AppDefined,
     423             :                                         "Dimension %s of array %s of dataset "
     424             :                                         "%s is indexed "
     425             :                                         "by a variable whose start value is "
     426             :                                         "not aligned "
     427             :                                         "with the one of other datasets",
     428           1 :                                         poDim->GetName().c_str(),
     429           1 :                                         poArray->GetName().c_str(),
     430             :                                         pszDatasetName);
     431           1 :                             return false;
     432             :                         }
     433             :                         const double dfEnd = std::max(
     434          14 :                             desc.dfStart + static_cast<double>(desc.nSize) *
     435           7 :                                                desc.dfIncrement,
     436          14 :                             descThisDataset.dfStart +
     437           7 :                                 static_cast<double>(descThisDataset.nSize) *
     438           7 :                                     descThisDataset.dfIncrement);
     439           7 :                         desc.dfStart =
     440           7 :                             std::min(desc.dfStart, descThisDataset.dfStart);
     441           7 :                         const double dfSize =
     442           7 :                             (dfEnd - desc.dfStart) / desc.dfIncrement;
     443           7 :                         constexpr double MAX_INTEGER_REPRESENTABLE =
     444             :                             static_cast<double>(1ULL << 53);
     445           7 :                         if (dfSize > MAX_INTEGER_REPRESENTABLE)
     446             :                         {
     447           0 :                             ReportError(
     448             :                                 CE_Failure, CPLE_AppDefined,
     449             :                                 "Dimension %s of array %s of dataset %s "
     450             :                                 "would be too large if merged.",
     451           0 :                                 poDim->GetName().c_str(),
     452           0 :                                 poArray->GetName().c_str(), pszDatasetName);
     453           0 :                             return false;
     454             :                         }
     455           7 :                         desc.nSize = static_cast<uint64_t>(dfSize + 0.5);
     456             :                     }
     457          20 :                     else if (desc.bHasIndexingVar)
     458             :                     {
     459          20 :                         if (descThisDataset.aaValues.empty())
     460             :                         {
     461           2 :                             ReportError(
     462             :                                 CE_Failure, CPLE_AppDefined,
     463             :                                 "Dimension %s of array %s of dataset %s "
     464             :                                 "has regularly spaced labels, contrary to "
     465             :                                 "other datasets",
     466           1 :                                 poDim->GetName().c_str(),
     467           1 :                                 poArray->GetName().c_str(), pszDatasetName);
     468           1 :                             return false;
     469             :                         }
     470          19 :                         if (descThisDataset.nProgressionSign !=
     471          19 :                             desc.nProgressionSign)
     472             :                         {
     473           2 :                             ReportError(
     474             :                                 CE_Failure, CPLE_AppDefined,
     475             :                                 "Dataset %s: values in indexing variable %s of "
     476             :                                 "dimension %s must be either increasing or "
     477             :                                 "decreasing in all input datasets.",
     478             :                                 pszDatasetName,
     479           2 :                                 poDim->GetIndexingVariable()->GetName().c_str(),
     480             :                                 desc.osName.c_str());
     481           1 :                             return false;
     482             :                         }
     483          18 :                         CPLAssert(descThisDataset.aaValues.size() == 1);
     484          36 :                         if (descThisDataset.aaValues[0][0] <
     485          18 :                             desc.aaValues[0][0])
     486             :                         {
     487          18 :                             if (descThisDataset.aaValues[0].back() >=
     488           9 :                                 desc.aaValues[0][0])
     489             :                             {
     490           2 :                                 ReportError(
     491             :                                     CE_Failure, CPLE_AppDefined,
     492             :                                     "Dataset %s: values in indexing variable "
     493             :                                     "%s of "
     494             :                                     "dimension %s are not the same as in other "
     495             :                                     "datasets",
     496             :                                     pszDatasetName,
     497           2 :                                     poDim->GetIndexingVariable()
     498           1 :                                         ->GetName()
     499             :                                         .c_str(),
     500             :                                     desc.osName.c_str());
     501           1 :                                 return false;
     502             :                             }
     503             :                             desc.aaValues.insert(
     504           8 :                                 desc.aaValues.begin(),
     505          16 :                                 std::move(descThisDataset.aaValues[0]));
     506             :                         }
     507             :                         else
     508             :                         {
     509          13 :                             for (size_t i = 0; i < desc.aaValues.size(); ++i)
     510             :                             {
     511          18 :                                 if (descThisDataset.aaValues[0][0] ==
     512           9 :                                     desc.aaValues[i][0])
     513             :                                 {
     514           5 :                                     if (descThisDataset.aaValues[0] !=
     515           5 :                                         desc.aaValues[i])
     516             :                                     {
     517           2 :                                         ReportError(
     518             :                                             CE_Failure, CPLE_AppDefined,
     519             :                                             "Dataset %s: values in indexing "
     520             :                                             "variable %s of dimension %s are "
     521             :                                             "not "
     522             :                                             "the same as in other datasets",
     523             :                                             pszDatasetName,
     524           2 :                                             poDim->GetIndexingVariable()
     525           1 :                                                 ->GetName()
     526             :                                                 .c_str(),
     527             :                                             desc.osName.c_str());
     528           1 :                                         return false;
     529             :                                     }
     530             :                                 }
     531           4 :                                 else if (descThisDataset.aaValues[0][0] >
     532           9 :                                              desc.aaValues[i][0] &&
     533           4 :                                          (i + 1 == desc.aaValues.size() ||
     534           2 :                                           descThisDataset.aaValues[0][0] <
     535           1 :                                               desc.aaValues[i + 1][0]))
     536             :                                 {
     537           4 :                                     if (descThisDataset.aaValues[0][0] <=
     538           4 :                                         desc.aaValues[i].back())
     539             :                                     {
     540           2 :                                         ReportError(
     541             :                                             CE_Failure, CPLE_AppDefined,
     542             :                                             "Dataset %s: values in indexing "
     543             :                                             "variable %s of dimension %s are "
     544             :                                             "overlapping with the ones of "
     545             :                                             "other "
     546             :                                             "datasets",
     547             :                                             pszDatasetName,
     548           2 :                                             poDim->GetIndexingVariable()
     549           1 :                                                 ->GetName()
     550             :                                                 .c_str(),
     551             :                                             desc.osName.c_str());
     552           1 :                                         return false;
     553             :                                     }
     554           4 :                                     if (i + 1 < desc.aaValues.size() &&
     555           2 :                                         descThisDataset.aaValues[0].back() >=
     556           1 :                                             desc.aaValues[i + 1][0])
     557             :                                     {
     558           2 :                                         ReportError(
     559             :                                             CE_Failure, CPLE_AppDefined,
     560             :                                             "Dataset %s: values in indexing "
     561             :                                             "variable %s of dimension %s are "
     562             :                                             "overlapping with the ones of "
     563             :                                             "other "
     564             :                                             "datasets",
     565             :                                             pszDatasetName,
     566           2 :                                             poDim->GetIndexingVariable()
     567           1 :                                                 ->GetName()
     568             :                                                 .c_str(),
     569             :                                             desc.osName.c_str());
     570           1 :                                         return false;
     571             :                                     }
     572             :                                     desc.aaValues.insert(
     573           2 :                                         desc.aaValues.begin() + i + 1,
     574           4 :                                         std::move(descThisDataset.aaValues[0]));
     575           2 :                                     break;
     576             :                                 }
     577             :                             }
     578             :                         }
     579             :                     }
     580             :                 }
     581             :             }
     582             : 
     583          43 :             arrayParameters.aaoSourceShortDimDesc.push_back(
     584          43 :                 std::move(aoSourceShortDimDesc));
     585             :         }
     586             :     }
     587             : 
     588          11 :     return true;
     589             : }
     590             : 
     591             : /************************************************************************/
     592             : /*           GDALMdimMosaicAlgorithm::GetInputDatasetNames()            */
     593             : /************************************************************************/
     594             : 
     595          33 : bool GDALMdimMosaicAlgorithm::GetInputDatasetNames(
     596             :     GDALProgressFunc pfnProgress, void *pProgressData,
     597             :     CPLStringList &aosInputDatasetNames) const
     598             : {
     599          88 :     for (auto &ds : m_inputDatasets)
     600             :     {
     601          55 :         if (ds.GetName()[0] == '@')
     602             :         {
     603             :             auto f = VSIVirtualHandleUniquePtr(
     604           1 :                 VSIFOpenL(ds.GetName().c_str() + 1, "r"));
     605           1 :             if (!f)
     606             :             {
     607           0 :                 ReportError(CE_Failure, CPLE_FileIO, "Cannot open %s",
     608           0 :                             ds.GetName().c_str() + 1);
     609           0 :                 return false;
     610             :             }
     611           3 :             while (const char *filename = CPLReadLineL(f.get()))
     612             :             {
     613           2 :                 aosInputDatasetNames.push_back(filename);
     614           2 :             }
     615             :         }
     616          54 :         else if (ds.GetName().find_first_of("*?[") != std::string::npos)
     617             :         {
     618           5 :             CPLStringList aosMatches(VSIGlob(ds.GetName().c_str(), nullptr,
     619          10 :                                              pfnProgress, pProgressData));
     620          16 :             for (const char *pszStr : aosMatches)
     621             :             {
     622          11 :                 aosInputDatasetNames.push_back(pszStr);
     623             :             }
     624             :         }
     625             :         else
     626             :         {
     627          98 :             std::string osDatasetName = ds.GetName();
     628          49 :             if (!GetReferencePathForRelativePaths().empty())
     629             :             {
     630           0 :                 osDatasetName = GDALDataset::BuildFilename(
     631             :                     osDatasetName.c_str(),
     632           0 :                     GetReferencePathForRelativePaths().c_str(), true);
     633             :             }
     634          49 :             aosInputDatasetNames.push_back(osDatasetName.c_str());
     635             :         }
     636             :     }
     637          33 :     return true;
     638             : }
     639             : 
     640             : /************************************************************************/
     641             : /*                  GDALMdimMosaicAlgorithm::RunImpl()                  */
     642             : /************************************************************************/
     643             : 
     644          33 : bool GDALMdimMosaicAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
     645             :                                       void *pProgressData)
     646             : {
     647          33 :     CPLAssert(!m_outputDataset.GetDatasetRef());
     648             : 
     649          33 :     if (m_outputFormat.empty())
     650             :     {
     651             :         const auto aosFormats =
     652             :             CPLStringList(GDALGetOutputDriversForDatasetName(
     653           5 :                 m_outputDataset.GetName().c_str(), GDAL_OF_MULTIDIM_RASTER,
     654             :                 /* bSingleMatch = */ true,
     655           5 :                 /* bWarn = */ true));
     656           5 :         if (aosFormats.size() != 1)
     657             :         {
     658           0 :             ReportError(CE_Failure, CPLE_AppDefined,
     659             :                         "Cannot guess driver for %s",
     660           0 :                         m_outputDataset.GetName().c_str());
     661           0 :             return false;
     662             :         }
     663           5 :         m_outputFormat = aosFormats[0];
     664             :     }
     665             :     auto poOutDrv =
     666          33 :         GetGDALDriverManager()->GetDriverByName(m_outputFormat.c_str());
     667          33 :     if (!poOutDrv)
     668             :     {
     669           0 :         ReportError(CE_Failure, CPLE_AppDefined, "Driver %s does not exist",
     670             :                     m_outputFormat.c_str());
     671           0 :         return false;
     672             :     }
     673             : 
     674          33 :     const bool bIsVRT = EQUAL(m_outputFormat.c_str(), "VRT");
     675             : 
     676          66 :     CPLStringList aosInputDatasetNames;
     677          33 :     const double dfIntermediatePercentage = bIsVRT ? 1.0 : 0.1;
     678             :     std::unique_ptr<void, decltype(&GDALDestroyScaledProgress)> pScaledData(
     679             :         GDALCreateScaledProgress(0.0, dfIntermediatePercentage, pfnProgress,
     680             :                                  pProgressData),
     681          66 :         GDALDestroyScaledProgress);
     682          33 :     if (!GetInputDatasetNames(GDALScaledProgress, pScaledData.get(),
     683             :                               aosInputDatasetNames))
     684           0 :         return false;
     685             : 
     686          61 :     for (std::string &name : m_array)
     687             :     {
     688          28 :         if (!name.empty() && name[0] != '/')
     689          28 :             name = "/" + name;
     690             :     }
     691             : 
     692          66 :     std::vector<ArrayParameters> aoArrayParameters;
     693          33 :     if (!BuildArrayParameters(aosInputDatasetNames, aoArrayParameters))
     694             :     {
     695          22 :         return false;
     696             :     }
     697             : 
     698          22 :     auto poVRTDS = VRTDataset::CreateVRTMultiDimensional("", nullptr, nullptr);
     699          11 :     CPLAssert(poVRTDS);
     700             : 
     701          22 :     auto poDstGroup = poVRTDS->GetRootVRTGroup();
     702          11 :     CPLAssert(poDstGroup);
     703             : 
     704             :     std::map<std::string, std::shared_ptr<GDALDimension>>
     705          22 :         oMapAlreadyCreatedDims;
     706             : 
     707             :     // Iterate over arrays
     708          24 :     for (auto &arrayParameters : aoArrayParameters)
     709             :     {
     710          13 :         auto &poFirstSourceArray = arrayParameters.poFirstSourceArray;
     711          13 :         CPLAssert(poFirstSourceArray);
     712          13 :         auto &aaoSourceShortDimDesc = arrayParameters.aaoSourceShortDimDesc;
     713          13 :         CPLAssert(aaoSourceShortDimDesc.size() ==
     714             :                   static_cast<size_t>(aosInputDatasetNames.size()));
     715             : 
     716             :         // Create mosaic array dimensions
     717          13 :         std::vector<std::shared_ptr<GDALDimension>> apoDstDims;
     718          35 :         for (auto &desc : arrayParameters.mosaicDimensions)
     719             :         {
     720          22 :             auto oIterCreatedDims = oMapAlreadyCreatedDims.find(desc.osName);
     721          22 :             if (oIterCreatedDims != oMapAlreadyCreatedDims.end())
     722             :             {
     723           4 :                 apoDstDims.push_back(oIterCreatedDims->second);
     724             :             }
     725             :             else
     726             :             {
     727          18 :                 uint64_t nDimSize64 = desc.nSize;
     728          18 :                 if (!desc.aaValues.empty())
     729             :                 {
     730           8 :                     nDimSize64 = 0;
     731          23 :                     for (const auto &aValues : desc.aaValues)
     732          15 :                         nDimSize64 += aValues.size();
     733             :                 }
     734             :                 auto dstDim = poDstGroup->CreateDimension(
     735          18 :                     desc.osName, desc.osType, desc.osDirection, nDimSize64);
     736          18 :                 if (!dstDim)
     737           0 :                     return false;
     738             : 
     739          18 :                 if (desc.bHasIndexingVar)
     740             :                 {
     741             :                     auto var = poDstGroup->CreateVRTMDArray(
     742          17 :                         desc.osName, {dstDim},
     743          68 :                         GDALExtendedDataType::Create(GDT_Float64));
     744          17 :                     if (!var)
     745           0 :                         return false;
     746             : 
     747          43 :                     for (const auto &attr : desc.attributes)
     748             :                     {
     749          52 :                         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     750             :                         auto dstAttr = var->CreateAttribute(
     751          26 :                             attr->GetName(), attr->GetDimensionsSize(),
     752          78 :                             attr->GetDataType());
     753          26 :                         if (dstAttr)
     754             :                         {
     755          26 :                             auto raw(attr->ReadAsRaw());
     756          26 :                             CPL_IGNORE_RET_VAL(
     757          26 :                                 dstAttr->Write(raw.data(), raw.size()));
     758             :                         }
     759             :                     }
     760             : 
     761          17 :                     if (desc.aaValues.empty())
     762             :                     {
     763             :                         auto poSource =
     764             :                             std::make_unique<VRTMDArraySourceRegularlySpaced>(
     765           9 :                                 desc.dfStart, desc.dfIncrement);
     766           9 :                         var->AddSource(std::move(poSource));
     767             :                     }
     768             :                     else
     769             :                     {
     770           8 :                         const size_t nDimSize = static_cast<size_t>(nDimSize64);
     771          16 :                         std::vector<GUInt64> anOffset = {0};
     772          16 :                         std::vector<size_t> anCount = {nDimSize};
     773          16 :                         std::vector<double> adfValues;
     774           8 :                         adfValues.reserve(nDimSize);
     775           8 :                         if (desc.nProgressionSign >= 0)
     776             :                         {
     777          23 :                             for (const auto &aValues : desc.aaValues)
     778          15 :                                 adfValues.insert(adfValues.end(),
     779             :                                                  aValues.begin(),
     780          30 :                                                  aValues.end());
     781             :                         }
     782             :                         else
     783             :                         {
     784           0 :                             for (auto oIter = desc.aaValues.rbegin();
     785           0 :                                  oIter != desc.aaValues.rend(); ++oIter)
     786             :                             {
     787           0 :                                 adfValues.insert(adfValues.end(),
     788           0 :                                                  oIter->rbegin(),
     789           0 :                                                  oIter->rend());
     790             :                             }
     791             :                         }
     792          16 :                         std::vector<GByte> abyValues(nDimSize * sizeof(double));
     793           8 :                         memcpy(abyValues.data(), adfValues.data(),
     794             :                                nDimSize * sizeof(double));
     795             :                         auto poSource =
     796             :                             std::make_unique<VRTMDArraySourceInlinedValues>(
     797           0 :                                 var.get(),
     798           8 :                                 /* bIsConstantValue = */ false,
     799           8 :                                 std::move(anOffset), std::move(anCount),
     800          16 :                                 std::move(abyValues));
     801           8 :                         var->AddSource(std::move(poSource));
     802             :                     }
     803          17 :                     dstDim->SetIndexingVariable(std::move(var));
     804             :                 }
     805             : 
     806          18 :                 oMapAlreadyCreatedDims[dstDim->GetName()] = dstDim;
     807          18 :                 apoDstDims.push_back(std::move(dstDim));
     808             :             }
     809             :         }
     810             : 
     811             :         // Create mosaic array
     812          13 :         CPLStringList aosArrayCO;
     813          13 :         std::string osBlockSize;
     814          35 :         for (size_t i = 0; i < apoDstDims.size(); ++i)
     815             :         {
     816          22 :             if (i > 0)
     817           9 :                 osBlockSize += ',';
     818             :             osBlockSize +=
     819          22 :                 std::to_string(arrayParameters.mosaicDimensions[i].nBlockSize);
     820             :         }
     821          13 :         if (!osBlockSize.empty())
     822          13 :             aosArrayCO.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
     823             : 
     824             :         auto poDstArray = poDstGroup->CreateVRTMDArray(
     825          13 :             CPLGetFilename(poFirstSourceArray->GetName().c_str()), apoDstDims,
     826          39 :             poFirstSourceArray->GetDataType(), aosArrayCO);
     827          13 :         if (!poDstArray)
     828           0 :             return false;
     829             : 
     830          13 :         GUInt64 nCurCost = 0;
     831          13 :         poDstArray->CopyFromAllExceptValues(poFirstSourceArray.get(), false,
     832             :                                             nCurCost, 0, nullptr, nullptr);
     833             : 
     834             :         // Add sources to mosaic array
     835          38 :         for (int iDS = 0; iDS < aosInputDatasetNames.size(); ++iDS)
     836             :         {
     837          25 :             const auto &aoSourceShortDimDesc = aaoSourceShortDimDesc[iDS];
     838             : 
     839          25 :             const auto dimCount = arrayParameters.mosaicDimensions.size();
     840          50 :             std::vector<GUInt64> anSrcOffset(dimCount);
     841          50 :             std::vector<GUInt64> anCount(dimCount);
     842          50 :             std::vector<GUInt64> anDstOffset;
     843          25 :             CPLAssert(aoSourceShortDimDesc.size() == dimCount);
     844             : 
     845          67 :             for (size_t iDim = 0; iDim < dimCount; ++iDim)
     846             :             {
     847             :                 const DimensionDesc &desc =
     848          42 :                     arrayParameters.mosaicDimensions[iDim];
     849             :                 const SourceShortDimDesc &sourceDesc =
     850          42 :                     aoSourceShortDimDesc[iDim];
     851          42 :                 if (sourceDesc.bIsRegularlySpaced)
     852             :                 {
     853          17 :                     const double dfPos =
     854          17 :                         (sourceDesc.dfStart - desc.dfStart) / desc.dfIncrement;
     855          17 :                     anDstOffset.push_back(static_cast<uint64_t>(dfPos + 0.5));
     856             :                 }
     857             :                 else
     858             :                 {
     859          25 :                     uint64_t nPos = 0;
     860          25 :                     bool bFound = false;
     861          35 :                     for (size_t i = 0; i < desc.aaValues.size(); ++i)
     862             :                     {
     863          35 :                         if (sourceDesc.dfStart == desc.aaValues[i][0])
     864             :                         {
     865          25 :                             bFound = true;
     866          25 :                             break;
     867             :                         }
     868             :                         else
     869             :                         {
     870          10 :                             nPos += desc.aaValues[i].size();
     871             :                         }
     872             :                     }
     873          25 :                     CPLAssert(bFound);
     874          25 :                     CPL_IGNORE_RET_VAL(bFound);
     875             : 
     876          25 :                     anDstOffset.push_back(nPos);
     877             :                 }
     878             : 
     879          42 :                 anCount[iDim] = sourceDesc.nSize;
     880             :             }
     881             : 
     882          50 :             std::vector<GUInt64> anStep(dimCount, 1);
     883          50 :             std::vector<int> anTransposedAxis;
     884             :             auto poSource = std::make_unique<VRTMDArraySourceFromArray>(
     885          50 :                 poDstArray.get(), false, false, aosInputDatasetNames[iDS],
     886          50 :                 poFirstSourceArray->GetFullName(), std::string(),
     887          25 :                 std::move(anTransposedAxis),
     888          25 :                 std::string(),  // viewExpr
     889          25 :                 std::move(anSrcOffset), std::move(anCount), std::move(anStep),
     890          50 :                 std::move(anDstOffset));
     891          25 :             poDstArray->AddSource(std::move(poSource));
     892             :         }
     893             :     }
     894             : 
     895          11 :     pScaledData.reset(GDALCreateScaledProgress(dfIntermediatePercentage, 1.0,
     896             :                                                pfnProgress, pProgressData));
     897             :     auto poOutDS = std::unique_ptr<GDALDataset>(
     898          11 :         poOutDrv->CreateCopy(m_outputDataset.GetName().c_str(), poVRTDS.get(),
     899          11 :                              false, CPLStringList(m_creationOptions).List(),
     900          22 :                              GDALScaledProgress, pScaledData.get()));
     901             : 
     902          11 :     if (poOutDS)
     903          11 :         m_outputDataset.Set(std::move(poOutDS));
     904             : 
     905          11 :     return m_outputDataset.GetDatasetRef() != nullptr;
     906             : }
     907             : 
     908             : //! @endcond

Generated by: LCOV version 1.14