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

Generated by: LCOV version 1.14