LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_create.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 173 196 88.3 %
Date: 2025-04-16 00:42:22 Functions: 3 3 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  gdal "raster create" 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_raster_create.h"
      14             : 
      15             : #include "cpl_conv.h"
      16             : #include "gdal_priv.h"
      17             : #include "gdal_utils.h"
      18             : #include "ogr_spatialref.h"
      19             : 
      20             : //! @cond Doxygen_Suppress
      21             : 
      22             : #ifndef _
      23             : #define _(x) (x)
      24             : #endif
      25             : 
      26             : /************************************************************************/
      27             : /*           GDALRasterCreateAlgorithm::GDALRasterCreateAlgorithm()     */
      28             : /************************************************************************/
      29             : 
      30          42 : GDALRasterCreateAlgorithm::GDALRasterCreateAlgorithm()
      31          42 :     : GDALAlgorithm(NAME, DESCRIPTION, HELP_URL)
      32             : {
      33          42 :     AddProgressArg();
      34          42 :     AddOutputFormatArg(&m_outputFormat)
      35             :         .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
      36         126 :                          {GDAL_DCAP_RASTER, GDAL_DCAP_CREATE});
      37          42 :     AddOpenOptionsArg(&m_openOptions);
      38          42 :     AddInputFormatsArg(&m_inputFormats)
      39          84 :         .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_RASTER});
      40          42 :     AddInputDatasetArg(&m_inputDataset, GDAL_OF_RASTER, false).AddAlias("like");
      41          42 :     AddOutputDatasetArg(&m_outputDataset, GDAL_OF_RASTER);
      42          42 :     AddCreationOptionsArg(&m_creationOptions);
      43          42 :     const char *exclusionGroup = "overwrite-append";
      44          42 :     AddOverwriteArg(&m_overwrite).SetMutualExclusionGroup(exclusionGroup);
      45             :     AddArg("append", 0, _("Append as a subdataset to existing output"),
      46          84 :            &m_append)
      47          42 :         .SetDefault(false)
      48          42 :         .SetMutualExclusionGroup(exclusionGroup);
      49          84 :     AddArg("size", 0, _("Output size in pixels"), &m_size)
      50          42 :         .SetMinCount(2)
      51          42 :         .SetMaxCount(2)
      52          42 :         .SetMinValueIncluded(0)
      53          42 :         .SetRepeatedArgAllowed(false)
      54          42 :         .SetDisplayHintAboutRepetition(false)
      55          42 :         .SetMetaVar("<width>,<height>");
      56          84 :     AddArg("band-count", 0, _("Number of bands"), &m_bandCount)
      57          42 :         .SetDefault(m_bandCount)
      58          42 :         .SetMinValueIncluded(0);
      59          42 :     AddOutputDataTypeArg(&m_type).SetDefault(m_type);
      60             : 
      61          42 :     AddNodataDataTypeArg(&m_nodata, /* noneAllowed = */ true);
      62             : 
      63          42 :     AddArg("burn", 0, _("Burn value"), &m_burnValues);
      64          84 :     AddArg("crs", 0, _("Set CRS"), &m_crs)
      65          84 :         .AddHiddenAlias("a_srs")
      66          42 :         .SetIsCRSArg(/*noneAllowed=*/true);
      67          42 :     AddBBOXArg(&m_bbox);
      68             : 
      69             :     {
      70          84 :         auto &arg = AddArg("metadata", 0, _("Add metadata item"), &m_metadata)
      71          42 :                         .SetMetaVar("<KEY>=<VALUE>");
      72          12 :         arg.AddValidationAction([this, &arg]()
      73          54 :                                 { return ValidateKeyValue(arg); });
      74          42 :         arg.AddHiddenAlias("mo");
      75             :     }
      76             :     AddArg("copy-metadata", 0, _("Copy metadata from input dataset"),
      77          42 :            &m_copyMetadata);
      78             :     AddArg("copy-overviews", 0,
      79          42 :            _("Create same overview levels as input dataset"), &m_copyOverviews);
      80          42 : }
      81             : 
      82             : /************************************************************************/
      83             : /*                  GDALRasterCreateAlgorithm::RunImpl()                */
      84             : /************************************************************************/
      85             : 
      86          40 : bool GDALRasterCreateAlgorithm::RunImpl(GDALProgressFunc /* pfnProgress */,
      87             :                                         void * /*pProgressData */)
      88             : {
      89          40 :     if (m_outputDataset.GetDatasetRef())
      90             :     {
      91           0 :         CPLError(CE_Failure, CPLE_NotSupported,
      92             :                  "gdal raster create does not support outputting to an "
      93             :                  "already opened output dataset");
      94           0 :         return false;
      95             :     }
      96             : 
      97             :     VSIStatBufL sStat;
      98          47 :     if (!m_append && !m_outputDataset.GetName().empty() &&
      99          12 :         (VSIStatL(m_outputDataset.GetName().c_str(), &sStat) == 0 ||
     100          45 :          std::unique_ptr<GDALDataset>(
     101          10 :              GDALDataset::Open(m_outputDataset.GetName().c_str()))))
     102             :     {
     103           2 :         if (!m_overwrite)
     104             :         {
     105           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     106             :                         "File '%s' already exists. Specify the --overwrite "
     107             :                         "option to overwrite it, or --append to append to it.",
     108           1 :                         m_outputDataset.GetName().c_str());
     109           1 :             return false;
     110             :         }
     111             :         else
     112             :         {
     113           1 :             VSIUnlink(m_outputDataset.GetName().c_str());
     114             :         }
     115             :     }
     116             : 
     117          39 :     if (m_outputFormat.empty())
     118             :     {
     119             :         const auto aosFormats =
     120             :             CPLStringList(GDALGetOutputDriversForDatasetName(
     121           6 :                 m_outputDataset.GetName().c_str(), GDAL_OF_RASTER,
     122             :                 /* bSingleMatch = */ true,
     123           6 :                 /* bWarn = */ true));
     124           6 :         if (aosFormats.size() != 1)
     125             :         {
     126           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     127             :                         "Cannot guess driver for %s",
     128           1 :                         m_outputDataset.GetName().c_str());
     129           1 :             return false;
     130             :         }
     131           5 :         m_outputFormat = aosFormats[0];
     132             :     }
     133             : 
     134          76 :     OGRSpatialReference oSRS;
     135             : 
     136          38 :     double adfGT[6] = {0};
     137          38 :     bool bGTValid = false;
     138             : 
     139          38 :     auto poSrcDS = m_inputDataset.GetDatasetRef();
     140          38 :     if (poSrcDS)
     141             :     {
     142          11 :         if (m_size.empty())
     143             :         {
     144          27 :             m_size = std::vector<int>{poSrcDS->GetRasterXSize(),
     145          18 :                                       poSrcDS->GetRasterYSize()};
     146             :         }
     147             : 
     148          11 :         if (!GetArg("band-count")->IsExplicitlySet())
     149             :         {
     150          10 :             m_bandCount = poSrcDS->GetRasterCount();
     151             :         }
     152             : 
     153          11 :         if (!GetArg("datatype")->IsExplicitlySet())
     154             :         {
     155          10 :             if (m_bandCount > 0)
     156             :             {
     157             :                 m_type = GDALGetDataTypeName(
     158          10 :                     poSrcDS->GetRasterBand(1)->GetRasterDataType());
     159             :             }
     160             :         }
     161             : 
     162          11 :         if (m_crs.empty())
     163             :         {
     164           9 :             if (const auto poSRS = poSrcDS->GetSpatialRef())
     165           9 :                 oSRS = *poSRS;
     166             :         }
     167             : 
     168          11 :         if (m_bbox.empty())
     169             :         {
     170          10 :             bGTValid = poSrcDS->GetGeoTransform(adfGT) == CE_None;
     171             :         }
     172             : 
     173          11 :         if (m_nodata.empty() && m_bandCount > 0)
     174             :         {
     175          10 :             int bNoData = false;
     176             :             const double dfNoData =
     177          10 :                 poSrcDS->GetRasterBand(1)->GetNoDataValue(&bNoData);
     178          10 :             if (bNoData)
     179          10 :                 m_nodata = CPLSPrintf("%.17g", dfNoData);
     180             :         }
     181             :     }
     182             : 
     183          38 :     if (m_size.empty())
     184             :     {
     185           1 :         ReportError(CE_Failure, CPLE_IllegalArg,
     186             :                     "Argument 'size' should be specified, or 'like' dataset "
     187             :                     "should be specified");
     188           1 :         return false;
     189             :     }
     190             : 
     191          51 :     if (!m_burnValues.empty() && m_burnValues.size() != 1 &&
     192          14 :         static_cast<int>(m_burnValues.size()) != m_bandCount)
     193             :     {
     194           2 :         if (m_bandCount == 1)
     195             :         {
     196           1 :             ReportError(CE_Failure, CPLE_IllegalArg,
     197             :                         "One value should be provided for argument "
     198             :                         "'burn', given there is one band");
     199             :         }
     200             :         else
     201             :         {
     202           1 :             ReportError(CE_Failure, CPLE_IllegalArg,
     203             :                         "One or %d values should be provided for argument "
     204             :                         "'burn', given there are %d bands",
     205             :                         m_bandCount, m_bandCount);
     206             :         }
     207           2 :         return false;
     208             :     }
     209             : 
     210             :     auto poDriver =
     211          35 :         GetGDALDriverManager()->GetDriverByName(m_outputFormat.c_str());
     212          35 :     if (!poDriver)
     213             :     {
     214             :         // shouldn't happen given checks done in GDALAlgorithm
     215           0 :         ReportError(CE_Failure, CPLE_AppDefined, "Cannot find driver %s",
     216             :                     m_outputFormat.c_str());
     217           0 :         return false;
     218             :     }
     219             : 
     220          35 :     if (m_append)
     221             :     {
     222           2 :         if (poDriver->GetMetadataItem(GDAL_DCAP_CREATE_SUBDATASETS) == nullptr)
     223             :         {
     224           1 :             ReportError(CE_Failure, CPLE_NotSupported,
     225             :                         "-append option not supported for driver %s",
     226           1 :                         poDriver->GetDescription());
     227           1 :             return false;
     228             :         }
     229           1 :         m_creationOptions.push_back("APPEND_SUBDATASET=YES");
     230             :     }
     231             : 
     232             :     auto poRetDS = std::unique_ptr<GDALDataset>(poDriver->Create(
     233         102 :         m_outputDataset.GetName().c_str(), m_size[0], m_size[1], m_bandCount,
     234             :         GDALGetDataTypeByName(m_type.c_str()),
     235          68 :         CPLStringList(m_creationOptions).List()));
     236          34 :     if (!poRetDS)
     237             :     {
     238           1 :         return false;
     239             :     }
     240             : 
     241          33 :     if (!m_crs.empty() && m_crs != "none" && m_crs != "null")
     242             :     {
     243          13 :         oSRS.SetFromUserInput(m_crs.c_str());
     244          13 :         oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     245             :     }
     246             : 
     247          33 :     if (!oSRS.IsEmpty())
     248             :     {
     249          22 :         if (poRetDS->SetSpatialRef(&oSRS) != CE_None)
     250             :         {
     251           0 :             ReportError(CE_Failure, CPLE_AppDefined, "Setting CRS failed");
     252           0 :             return false;
     253             :         }
     254             :     }
     255             : 
     256          33 :     if (!m_bbox.empty())
     257             :     {
     258          13 :         if (poRetDS->GetRasterXSize() == 0 || poRetDS->GetRasterYSize() == 0)
     259             :         {
     260           0 :             ReportError(CE_Failure, CPLE_AppDefined,
     261             :                         "Cannot set extent because one of dataset height or "
     262             :                         "width is null");
     263           0 :             return false;
     264             :         }
     265          13 :         bGTValid = true;
     266          13 :         adfGT[0] = m_bbox[0];
     267          13 :         adfGT[1] = (m_bbox[2] - m_bbox[0]) / poRetDS->GetRasterXSize();
     268          13 :         adfGT[2] = 0;
     269          13 :         adfGT[3] = m_bbox[3];
     270          13 :         adfGT[4] = 0;
     271          13 :         adfGT[5] = -(m_bbox[3] - m_bbox[1]) / poRetDS->GetRasterYSize();
     272             :     }
     273          33 :     if (bGTValid)
     274             :     {
     275          23 :         if (poRetDS->SetGeoTransform(adfGT) != CE_None)
     276             :         {
     277           0 :             ReportError(CE_Failure, CPLE_AppDefined, "Setting extent failed");
     278           0 :             return false;
     279             :         }
     280             :     }
     281             : 
     282          33 :     if (!m_nodata.empty() && !EQUAL(m_nodata.c_str(), "none"))
     283             :     {
     284          68 :         for (int i = 0; i < poRetDS->GetRasterCount(); ++i)
     285             :         {
     286          45 :             bool bCannotBeExactlyRepresented = false;
     287          45 :             if (poRetDS->GetRasterBand(i + 1)->SetNoDataValueAsString(
     288          45 :                     m_nodata.c_str(), &bCannotBeExactlyRepresented) != CE_None)
     289             :             {
     290           1 :                 if (bCannotBeExactlyRepresented)
     291             :                 {
     292           1 :                     ReportError(CE_Failure, CPLE_AppDefined,
     293             :                                 "Setting nodata value failed as it cannot be "
     294             :                                 "represented on its data type");
     295             :                 }
     296             :                 else
     297             :                 {
     298           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
     299             :                                 "Setting nodata value failed");
     300             :                 }
     301           1 :                 return false;
     302             :             }
     303             :         }
     304             :     }
     305             : 
     306          32 :     if (m_copyMetadata)
     307             :     {
     308           2 :         if (!poSrcDS)
     309             :         {
     310           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     311             :                         "Argument 'copy-metadata' can only be set when an "
     312             :                         "input dataset is set");
     313           1 :             return false;
     314             :         }
     315             :         {
     316           1 :             const CPLStringList aosDomains(poSrcDS->GetMetadataDomainList());
     317           4 :             for (const char *domain : aosDomains)
     318             :             {
     319           3 :                 if (!EQUAL(domain, "IMAGE_STRUCTURE"))
     320             :                 {
     321           4 :                     if (poRetDS->SetMetadata(poSrcDS->GetMetadata(domain),
     322           4 :                                              domain) != CE_None)
     323             :                     {
     324           0 :                         ReportError(CE_Failure, CPLE_AppDefined,
     325             :                                     "Cannot copy '%s' metadata domain", domain);
     326           0 :                         return false;
     327             :                     }
     328             :                 }
     329             :             }
     330             :         }
     331           3 :         for (int i = 0; i < m_bandCount; ++i)
     332             :         {
     333             :             const CPLStringList aosDomains(
     334           2 :                 poSrcDS->GetRasterBand(i + 1)->GetMetadataDomainList());
     335           3 :             for (const char *domain : aosDomains)
     336             :             {
     337           1 :                 if (!EQUAL(domain, "IMAGE_STRUCTURE"))
     338             :                 {
     339           2 :                     if (poRetDS->GetRasterBand(i + 1)->SetMetadata(
     340           1 :                             poSrcDS->GetRasterBand(i + 1)->GetMetadata(domain),
     341           2 :                             domain) != CE_None)
     342             :                     {
     343           0 :                         ReportError(
     344             :                             CE_Failure, CPLE_AppDefined,
     345             :                             "Cannot copy '%s' metadata domain for band %d",
     346             :                             domain, i + 1);
     347           0 :                         return false;
     348             :                     }
     349             :                 }
     350             :             }
     351             :         }
     352             :     }
     353             : 
     354          62 :     const CPLStringList aosMD(m_metadata);
     355          43 :     for (const auto &[key, value] : cpl::IterateNameValue(aosMD))
     356             :     {
     357          12 :         if (poRetDS->SetMetadataItem(key, value) != CE_None)
     358             :         {
     359           0 :             ReportError(CE_Failure, CPLE_AppDefined,
     360             :                         "SetMetadataItem('%s', '%s') failed", key, value);
     361           0 :             return false;
     362             :         }
     363             :     }
     364             : 
     365          31 :     if (m_copyOverviews && m_bandCount > 0)
     366             :     {
     367           3 :         if (!poSrcDS)
     368             :         {
     369           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     370             :                         "Argument 'copy-overviews' can only be set when an "
     371             :                         "input dataset is set");
     372           2 :             return false;
     373             :         }
     374           3 :         if (poSrcDS->GetRasterXSize() != poRetDS->GetRasterXSize() ||
     375           1 :             poSrcDS->GetRasterYSize() != poRetDS->GetRasterYSize())
     376             :         {
     377           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     378             :                         "Argument 'copy-overviews' can only be set when the "
     379             :                         "input and output datasets have the same dimension");
     380           1 :             return false;
     381             :         }
     382             :         const int nOverviewCount =
     383           1 :             poSrcDS->GetRasterBand(1)->GetOverviewCount();
     384           1 :         std::vector<int> anLevels;
     385           2 :         for (int i = 0; i < nOverviewCount; ++i)
     386             :         {
     387           1 :             const auto poOvrBand = poSrcDS->GetRasterBand(1)->GetOverview(i);
     388           1 :             const int nOvrFactor = GDALComputeOvFactor(
     389             :                 poOvrBand->GetXSize(), poSrcDS->GetRasterXSize(),
     390           1 :                 poOvrBand->GetYSize(), poSrcDS->GetRasterYSize());
     391           1 :             anLevels.push_back(nOvrFactor);
     392             :         }
     393           2 :         if (poRetDS->BuildOverviews(
     394           1 :                 "NONE", nOverviewCount, anLevels.data(),
     395             :                 /* nListBands = */ 0, /* panBandList = */ nullptr,
     396             :                 /* pfnProgress = */ nullptr, /* pProgressData = */ nullptr,
     397           1 :                 /* options = */ nullptr) != CE_None)
     398             :         {
     399           0 :             ReportError(CE_Failure, CPLE_AppDefined,
     400             :                         "Creating overview(s) failed");
     401           0 :             return false;
     402             :         }
     403             :     }
     404             : 
     405          29 :     if (!m_burnValues.empty())
     406             :     {
     407          36 :         for (int i = 0; i < m_bandCount; ++i)
     408             :         {
     409          24 :             const int burnValueIdx = m_burnValues.size() == 1 ? 0 : i;
     410          24 :             const auto poDstBand = poRetDS->GetRasterBand(i + 1);
     411             :             // cppcheck-suppress negativeContainerIndex
     412          24 :             if (poDstBand->Fill(m_burnValues[burnValueIdx]) != CE_None)
     413             :             {
     414           0 :                 ReportError(CE_Failure, CPLE_AppDefined,
     415             :                             "Setting burn value failed");
     416           0 :                 return false;
     417             :             }
     418             :         }
     419          12 :         if (poRetDS->FlushCache(false) != CE_None)
     420             :         {
     421           0 :             ReportError(CE_Failure, CPLE_AppDefined,
     422             :                         "Setting burn value failed");
     423           0 :             return false;
     424             :         }
     425             :     }
     426             : 
     427          29 :     m_outputDataset.Set(std::move(poRetDS));
     428             : 
     429          29 :     return true;
     430             : }
     431             : 
     432             : //! @endcond

Generated by: LCOV version 1.14