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

Generated by: LCOV version 1.14