LCOV - code coverage report
Current view: top level - apps - gdalalg_mdim_mosaic.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 385 436 88.3 %
Date: 2025-10-21 22:35:35 Functions: 6 6 100.0 %

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

Generated by: LCOV version 1.14