LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_edit.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 359 368 97.6 %
Date: 2026-04-23 19:47:11 Functions: 12 12 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  "edit" step of "raster pipeline"
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdalalg_raster_edit.h"
      14             : 
      15             : #include "gdal_priv.h"
      16             : #include "gdal_utils.h"
      17             : #include "ogrsf_frmts.h"
      18             : 
      19             : #include <optional>
      20             : 
      21             : //! @cond Doxygen_Suppress
      22             : 
      23             : #ifndef _
      24             : #define _(x) (x)
      25             : #endif
      26             : 
      27             : /************************************************************************/
      28             : /*                           GetGCPFilename()                           */
      29             : /************************************************************************/
      30             : 
      31          29 : static std::string GetGCPFilename(const std::vector<std::string> &gcps)
      32             : {
      33          29 :     if (gcps.size() == 1 && !gcps[0].empty() && gcps[0][0] == '@')
      34             :     {
      35          12 :         return gcps[0].substr(1);
      36             :     }
      37          17 :     return std::string();
      38             : }
      39             : 
      40             : /************************************************************************/
      41             : /*          GDALRasterEditAlgorithm::GDALRasterEditAlgorithm()          */
      42             : /************************************************************************/
      43             : 
      44         208 : GDALRasterEditAlgorithm::GDALRasterEditAlgorithm(bool standaloneStep)
      45             :     : GDALRasterPipelineStepAlgorithm(
      46             :           NAME, DESCRIPTION, HELP_URL,
      47         208 :           ConstructorOptions().SetAddDefaultArguments(false))
      48             : {
      49         208 :     if (standaloneStep)
      50             :     {
      51         118 :         AddProgressArg();
      52             : 
      53             :         AddArg("dataset", 0,
      54             :                _("Dataset (to be updated in-place, unless --auxiliary)"),
      55         236 :                &m_dataset, GDAL_OF_RASTER | GDAL_OF_UPDATE)
      56         118 :             .SetPositional()
      57         118 :             .SetRequired()
      58         118 :             .SetAvailableInPipelineStep(false);
      59         118 :         AddOpenOptionsArg(&m_openOptions).SetAvailableInPipelineStep(false);
      60             :         AddArg("auxiliary", 0,
      61             :                _("Ask for an auxiliary .aux.xml file to be edited"),
      62         236 :                &m_readOnly)
      63         236 :             .AddHiddenAlias("ro")
      64         236 :             .AddHiddenAlias(GDAL_ARG_NAME_READ_ONLY)
      65         118 :             .SetAvailableInPipelineStep(false);
      66             :     }
      67             :     else
      68             :     {
      69          90 :         AddRasterHiddenInputDatasetArg();
      70             :     }
      71             : 
      72         416 :     AddArg("crs", 0, _("Override CRS (without reprojection)"), &m_overrideCrs)
      73         416 :         .AddHiddenAlias("a_srs")
      74         416 :         .AddHiddenAlias("srs")
      75         208 :         .SetIsCRSArg(/*noneAllowed=*/true);
      76             : 
      77         208 :     AddBBOXArg(&m_bbox);
      78             : 
      79         208 :     AddNodataArg(&m_nodata, /* noneAllowed = */ true);
      80             : 
      81             :     AddArg("color-interpretation", 0, _("Set band color interpretation"),
      82         416 :            &m_colorInterpretation)
      83         416 :         .SetMetaVar("[all|<BAND>=]<COLOR-INTEPRETATION>")
      84             :         .SetAutoCompleteFunction(
      85           5 :             [this](const std::string &s)
      86             :             {
      87           3 :                 std::vector<std::string> ret;
      88           3 :                 int nValues = 0;
      89           3 :                 const auto paeVals = GDALGetColorInterpretationList(&nValues);
      90           3 :                 if (s.find('=') == std::string::npos)
      91             :                 {
      92           2 :                     ret.push_back("all=");
      93           2 :                     if (auto poDS = m_dataset.GetDatasetRef())
      94             :                     {
      95           2 :                         for (int i = 0; i < poDS->GetRasterCount(); ++i)
      96           1 :                             ret.push_back(std::to_string(i + 1).append("="));
      97             :                     }
      98          70 :                     for (int i = 0; i < nValues; ++i)
      99         136 :                         ret.push_back(
     100          68 :                             GDALGetColorInterpretationName(paeVals[i]));
     101             :                 }
     102             :                 else
     103             :                 {
     104          35 :                     for (int i = 0; i < nValues; ++i)
     105          68 :                         ret.push_back(
     106          34 :                             GDALGetColorInterpretationName(paeVals[i]));
     107             :                 }
     108           6 :                 return ret;
     109         208 :             });
     110             : 
     111             :     const auto ValidationActionScaleOffset =
     112          44 :         [this](const char *argName, const std::vector<std::string> &values)
     113             :     {
     114         106 :         for (const std::string &s : values)
     115             :         {
     116          74 :             bool valid = true;
     117          74 :             const auto nPos = s.find('=');
     118          74 :             if (nPos != std::string::npos)
     119             :             {
     120          40 :                 if (CPLGetValueType(s.substr(0, nPos).c_str()) !=
     121          58 :                         CPL_VALUE_INTEGER ||
     122          38 :                     CPLGetValueType(s.substr(nPos + 1).c_str()) ==
     123             :                         CPL_VALUE_STRING)
     124             :                 {
     125           4 :                     valid = false;
     126             :                 }
     127             :             }
     128          54 :             else if (CPLGetValueType(s.c_str()) == CPL_VALUE_STRING)
     129             :             {
     130           2 :                 valid = false;
     131             :             }
     132          74 :             if (!valid)
     133             :             {
     134           6 :                 ReportError(CE_Failure, CPLE_IllegalArg,
     135             :                             "Invalid value '%s' for '%s'", s.c_str(), argName);
     136           6 :                 return false;
     137             :             }
     138             :         }
     139          32 :         return true;
     140         208 :     };
     141             : 
     142         416 :     AddArg("scale", 0, _("Override band scale factor"), &m_scale)
     143         416 :         .SetMetaVar("[<BAND>=]<SCALE>")
     144             :         .AddValidationAction(
     145          19 :             [this, ValidationActionScaleOffset]()
     146         227 :             { return ValidationActionScaleOffset("scale", m_scale); });
     147             : 
     148         416 :     AddArg("offset", 0, _("Override band offset constant"), &m_offset)
     149         416 :         .SetMetaVar("[<BAND>=]<OFFSET>")
     150             :         .AddValidationAction(
     151          19 :             [this, ValidationActionScaleOffset]()
     152         227 :             { return ValidationActionScaleOffset("offset", m_offset); });
     153             : 
     154             :     {
     155             :         auto &arg = AddArg("metadata", 0, _("Add/update dataset metadata item"),
     156         416 :                            &m_metadata)
     157         416 :                         .SetMetaVar("<KEY>=<VALUE>")
     158         208 :                         .SetPackedValuesAllowed(false);
     159          46 :         arg.AddValidationAction([this, &arg]()
     160         254 :                                 { return ParseAndValidateKeyValue(arg); });
     161         208 :         arg.AddHiddenAlias("mo");
     162             :     }
     163             : 
     164             :     AddArg("unset-metadata", 0, _("Remove dataset metadata item(s)"),
     165         416 :            &m_unsetMetadata)
     166         208 :         .SetMetaVar("<KEY>");
     167             : 
     168             :     AddArg("unset-metadata-domain", 0, _("Remove dataset metadata domain(s)"),
     169         416 :            &m_unsetMetadataDomain)
     170         208 :         .SetMetaVar("<DOMAIN>");
     171             : 
     172             :     AddArg("gcp", 0,
     173             :            _("Add ground control point, formatted as "
     174             :              "pixel,line,easting,northing[,elevation], or @filename"),
     175         416 :            &m_gcps)
     176         208 :         .SetPackedValuesAllowed(false)
     177             :         .AddValidationAction(
     178          36 :             [this]()
     179             :             {
     180          21 :                 if (GetGCPFilename(m_gcps).empty())
     181             :                 {
     182          28 :                     for (const std::string &gcp : m_gcps)
     183             :                     {
     184             :                         const CPLStringList aosTokens(
     185          17 :                             CSLTokenizeString2(gcp.c_str(), ",", 0));
     186          17 :                         if (aosTokens.size() != 4 && aosTokens.size() != 5)
     187             :                         {
     188           1 :                             ReportError(CE_Failure, CPLE_IllegalArg,
     189             :                                         "Bad format for %s", gcp.c_str());
     190           1 :                             return false;
     191             :                         }
     192          85 :                         for (int i = 0; i < aosTokens.size(); ++i)
     193             :                         {
     194          70 :                             if (CPLGetValueType(aosTokens[i]) ==
     195             :                                 CPL_VALUE_STRING)
     196             :                             {
     197           1 :                                 ReportError(CE_Failure, CPLE_IllegalArg,
     198             :                                             "Bad format for %s", gcp.c_str());
     199           1 :                                 return false;
     200             :                             }
     201             :                         }
     202             :                     }
     203             :                 }
     204          19 :                 return true;
     205         208 :             });
     206             : 
     207         208 :     if (standaloneStep)
     208             :     {
     209         236 :         AddArg("stats", 0, _("Compute statistics, using all pixels"), &m_stats)
     210         118 :             .SetMutualExclusionGroup("stats");
     211             :         AddArg("approx-stats", 0,
     212             :                _("Compute statistics, using a subset of pixels"),
     213         236 :                &m_approxStats)
     214         118 :             .SetMutualExclusionGroup("stats");
     215         118 :         AddArg("hist", 0, _("Compute histogram"), &m_hist);
     216             :     }
     217         208 : }
     218             : 
     219             : /************************************************************************/
     220             : /*         GDALRasterEditAlgorithm::~GDALRasterEditAlgorithm()          */
     221             : /************************************************************************/
     222             : 
     223             : GDALRasterEditAlgorithm::~GDALRasterEditAlgorithm() = default;
     224             : 
     225             : /************************************************************************/
     226             : /*                             ParseGCPs()                              */
     227             : /************************************************************************/
     228             : 
     229           8 : std::vector<gdal::GCP> GDALRasterEditAlgorithm::ParseGCPs() const
     230             : {
     231           8 :     std::vector<gdal::GCP> ret;
     232          16 :     const std::string osGCPFilename = GetGCPFilename(m_gcps);
     233           8 :     if (!osGCPFilename.empty())
     234             :     {
     235             :         auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
     236           4 :             osGCPFilename.c_str(), GDAL_OF_VECTOR | GDAL_OF_VERBOSE_ERROR));
     237           4 :         if (!poDS)
     238           1 :             return ret;
     239           3 :         if (poDS->GetLayerCount() != 1)
     240             :         {
     241           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     242             :                         "GCPs can only be specified for single-layer datasets");
     243           1 :             return ret;
     244             :         }
     245           2 :         auto poLayer = poDS->GetLayer(0);
     246           2 :         const auto poLayerDefn = poLayer->GetLayerDefn();
     247           2 :         int nIdIdx = -1, nInfoIdx = -1, nColIdx = -1, nLineIdx = -1, nXIdx = -1,
     248           2 :             nYIdx = -1, nZIdx = -1;
     249             : 
     250             :         const struct
     251             :         {
     252             :             int &idx;
     253             :             const char *name;
     254             :             bool required;
     255           2 :         } aFields[] = {
     256             :             {nIdIdx, "id", false},     {nInfoIdx, "info", false},
     257             :             {nColIdx, "column", true}, {nLineIdx, "line", true},
     258             :             {nXIdx, "x", true},        {nYIdx, "y", true},
     259             :             {nZIdx, "z", false},
     260           2 :         };
     261             : 
     262          11 :         for (auto &field : aFields)
     263             :         {
     264          10 :             field.idx = poLayerDefn->GetFieldIndex(field.name);
     265          10 :             if (field.idx < 0 && field.required)
     266             :             {
     267           3 :                 ReportError(CE_Failure, CPLE_AppDefined,
     268           1 :                             "Field '%s' cannot be found in '%s'", field.name,
     269           1 :                             poDS->GetDescription());
     270           1 :                 return ret;
     271             :             }
     272             :         }
     273           2 :         for (auto &&poFeature : poLayer)
     274             :         {
     275           2 :             gdal::GCP gcp;
     276           1 :             if (nIdIdx >= 0)
     277           1 :                 gcp.SetId(poFeature->GetFieldAsString(nIdIdx));
     278           1 :             if (nInfoIdx >= 0)
     279           1 :                 gcp.SetInfo(poFeature->GetFieldAsString(nInfoIdx));
     280           1 :             gcp.Pixel() = poFeature->GetFieldAsDouble(nColIdx);
     281           1 :             gcp.Line() = poFeature->GetFieldAsDouble(nLineIdx);
     282           1 :             gcp.X() = poFeature->GetFieldAsDouble(nXIdx);
     283           1 :             gcp.Y() = poFeature->GetFieldAsDouble(nYIdx);
     284           1 :             if (nZIdx >= 0 && poFeature->IsFieldSetAndNotNull(nZIdx))
     285           1 :                 gcp.Z() = poFeature->GetFieldAsDouble(nZIdx);
     286           1 :             ret.push_back(std::move(gcp));
     287             :         }
     288             :     }
     289             :     else
     290             :     {
     291          10 :         for (const std::string &gcpStr : m_gcps)
     292             :         {
     293             :             const CPLStringList aosTokens(
     294          12 :                 CSLTokenizeString2(gcpStr.c_str(), ",", 0));
     295             :             // Verified by validation action
     296           6 :             CPLAssert(aosTokens.size() == 4 || aosTokens.size() == 5);
     297          12 :             gdal::GCP gcp;
     298           6 :             gcp.Pixel() = CPLAtof(aosTokens[0]);
     299           6 :             gcp.Line() = CPLAtof(aosTokens[1]);
     300           6 :             gcp.X() = CPLAtof(aosTokens[2]);
     301           6 :             gcp.Y() = CPLAtof(aosTokens[3]);
     302           6 :             if (aosTokens.size() == 5)
     303           3 :                 gcp.Z() = CPLAtof(aosTokens[4]);
     304           6 :             ret.push_back(std::move(gcp));
     305             :         }
     306             :     }
     307           5 :     return ret;
     308             : }
     309             : 
     310             : /************************************************************************/
     311             : /*                  GDALRasterEditAlgorithm::RunStep()                  */
     312             : /************************************************************************/
     313             : 
     314          79 : bool GDALRasterEditAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
     315             : {
     316          79 :     GDALDataset *poDS = m_dataset.GetDatasetRef();
     317          79 :     if (poDS)
     318             :     {
     319             :         // Standalone mode
     320          60 :         if (poDS->GetAccess() != GA_Update && !m_readOnly)
     321             :         {
     322           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     323             :                         "Dataset should be opened in update mode unless "
     324             :                         "--auxiliary is set");
     325           1 :             return false;
     326             :         }
     327             :     }
     328             :     else
     329             :     {
     330             :         // Pipeline mode
     331          19 :         const auto poSrcDS = m_inputDataset[0].GetDatasetRef();
     332          19 :         CPLAssert(poSrcDS);
     333          19 :         CPLAssert(m_outputDataset.GetName().empty());
     334          19 :         CPLAssert(!m_outputDataset.GetDatasetRef());
     335          19 :         auto poDriver = poSrcDS->GetDriver();
     336          23 :         if (poDriver && EQUAL(poDriver->GetDescription(), "VRT") &&
     337           4 :             poSrcDS->GetDescription()[0] == '\0')
     338             :         {
     339             :             // We can directly edit an anonymous input VRT file
     340             :             // and we actually need to do that since the generic code path
     341             :             // in the other branch will try to serialize and deserialize a XML
     342             :             // file pointing to an anonymous source.
     343           4 :             m_outputDataset.Set(poSrcDS);
     344             :         }
     345             :         else
     346             :         {
     347             :             // Create a in-memory VRT to avoid modifying the source dataset.
     348          15 :             CPLStringList aosOptions;
     349          15 :             aosOptions.push_back("-of");
     350          15 :             aosOptions.push_back("VRT");
     351             :             GDALTranslateOptions *psOptions =
     352          15 :                 GDALTranslateOptionsNew(aosOptions.List(), nullptr);
     353          15 :             GDALDatasetH hSrcDS = GDALDataset::ToHandle(poSrcDS);
     354          15 :             auto poRetDS = GDALDataset::FromHandle(
     355             :                 GDALTranslate("", hSrcDS, psOptions, nullptr));
     356          15 :             GDALTranslateOptionsFree(psOptions);
     357          15 :             m_outputDataset.Set(std::unique_ptr<GDALDataset>(poRetDS));
     358             :         }
     359          19 :         poDS = m_outputDataset.GetDatasetRef();
     360             :     }
     361             : 
     362          78 :     bool ret = poDS != nullptr;
     363             : 
     364          78 :     if (poDS)
     365             :     {
     366          78 :         if (m_overrideCrs == "null" || m_overrideCrs == "none")
     367             :         {
     368           3 :             if (poDS->SetSpatialRef(nullptr) != CE_None)
     369             :             {
     370           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     371             :                             "SetSpatialRef(%s) failed", m_overrideCrs.c_str());
     372          32 :                 return false;
     373             :             }
     374             :         }
     375          75 :         else if (!m_overrideCrs.empty() && m_gcps.empty())
     376             :         {
     377           4 :             OGRSpatialReference oSRS;
     378           4 :             oSRS.SetFromUserInput(m_overrideCrs.c_str());
     379           4 :             oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     380           4 :             if (poDS->SetSpatialRef(&oSRS) != CE_None)
     381             :             {
     382           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     383             :                             "SetSpatialRef(%s) failed", m_overrideCrs.c_str());
     384           1 :                 return false;
     385             :             }
     386             :         }
     387             : 
     388          76 :         if (!m_bbox.empty())
     389             :         {
     390           4 :             if (poDS->GetRasterXSize() == 0 || poDS->GetRasterYSize() == 0)
     391             :             {
     392           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     393             :                             "Cannot set extent because one of dataset height "
     394             :                             "or width is null");
     395           2 :                 return false;
     396             :             }
     397           3 :             GDALGeoTransform gt;
     398           3 :             gt.xorig = m_bbox[0];
     399           3 :             gt.xscale = (m_bbox[2] - m_bbox[0]) / poDS->GetRasterXSize();
     400           3 :             gt.xrot = 0;
     401           3 :             gt.yorig = m_bbox[3];
     402           3 :             gt.yrot = 0;
     403           3 :             gt.yscale = -(m_bbox[3] - m_bbox[1]) / poDS->GetRasterYSize();
     404           3 :             if (poDS->SetGeoTransform(gt) != CE_None)
     405             :             {
     406           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     407             :                             "Setting extent failed");
     408           1 :                 return false;
     409             :             }
     410             :         }
     411             : 
     412          74 :         if (!m_nodata.empty())
     413             :         {
     414          18 :             for (int i = 0; i < poDS->GetRasterCount(); ++i)
     415             :             {
     416          10 :                 if (EQUAL(m_nodata.c_str(), "none"))
     417           2 :                     poDS->GetRasterBand(i + 1)->DeleteNoDataValue();
     418             :                 else
     419          16 :                     poDS->GetRasterBand(i + 1)->SetNoDataValue(
     420           8 :                         CPLAtof(m_nodata.c_str()));
     421             :             }
     422             :         }
     423             : 
     424          74 :         if (!m_colorInterpretation.empty())
     425             :         {
     426             :             const auto GetColorInterp =
     427          28 :                 [this](const char *pszStr) -> std::optional<GDALColorInterp>
     428             :             {
     429          25 :                 if (EQUAL(pszStr, "undefined"))
     430           0 :                     return GCI_Undefined;
     431             :                 const GDALColorInterp eInterp =
     432          25 :                     GDALGetColorInterpretationByName(pszStr);
     433          25 :                 if (eInterp != GCI_Undefined)
     434          22 :                     return eInterp;
     435           3 :                 ReportError(CE_Failure, CPLE_NotSupported,
     436             :                             "Unsupported color interpretation: %s", pszStr);
     437           3 :                 return {};
     438          18 :             };
     439             : 
     440          18 :             if (m_colorInterpretation.size() == 1 &&
     441          20 :                 poDS->GetRasterCount() > 1 &&
     442           2 :                 !cpl::starts_with(m_colorInterpretation[0], "all="))
     443             :             {
     444           1 :                 ReportError(
     445             :                     CE_Failure, CPLE_NotSupported,
     446             :                     "With several bands, specify as many color interpretation "
     447             :                     "as bands, one or many values of the form "
     448             :                     "<band_number>=<color> or a single value all=<color>");
     449          12 :                 return false;
     450             :             }
     451             :             else
     452             :             {
     453          17 :                 int nBandIter = 0;
     454          17 :                 bool bSyntaxAll = false;
     455          17 :                 bool bSyntaxExplicitBand = false;
     456          17 :                 bool bSyntaxImplicitBand = false;
     457          39 :                 for (const std::string &token : m_colorInterpretation)
     458             :                 {
     459             :                     const CPLStringList aosTokens(
     460          29 :                         CSLTokenizeString2(token.c_str(), "=", 0));
     461          29 :                     if (aosTokens.size() == 2 && EQUAL(aosTokens[0], "all"))
     462             :                     {
     463           4 :                         bSyntaxAll = true;
     464           4 :                         const auto eColorInterp = GetColorInterp(aosTokens[1]);
     465           4 :                         if (!eColorInterp)
     466             :                         {
     467           1 :                             return false;
     468             :                         }
     469             :                         else
     470             :                         {
     471          10 :                             for (int i = 0; i < poDS->GetRasterCount(); ++i)
     472             :                             {
     473           7 :                                 if (poDS->GetRasterBand(i + 1)
     474           7 :                                         ->SetColorInterpretation(
     475          14 :                                             *eColorInterp) != CE_None)
     476           0 :                                     return false;
     477             :                             }
     478             :                         }
     479             :                     }
     480          25 :                     else if (aosTokens.size() == 2)
     481             :                     {
     482           9 :                         bSyntaxExplicitBand = true;
     483           9 :                         const int nBand = atoi(aosTokens[0]);
     484           9 :                         if (nBand <= 0 || nBand > poDS->GetRasterCount())
     485             :                         {
     486           2 :                             ReportError(CE_Failure, CPLE_NotSupported,
     487             :                                         "Invalid band number '%s' in '%s'",
     488             :                                         aosTokens[0], token.c_str());
     489           3 :                             return false;
     490             :                         }
     491           7 :                         const auto eColorInterp = GetColorInterp(aosTokens[1]);
     492           7 :                         if (!eColorInterp)
     493             :                         {
     494           1 :                             return false;
     495             :                         }
     496           6 :                         else if (poDS->GetRasterBand(nBand)
     497           6 :                                      ->SetColorInterpretation(*eColorInterp) !=
     498             :                                  CE_None)
     499             :                         {
     500           0 :                             return false;
     501             :                         }
     502             :                     }
     503             :                     else
     504             :                     {
     505          16 :                         bSyntaxImplicitBand = true;
     506          16 :                         ++nBandIter;
     507          16 :                         if (nBandIter > poDS->GetRasterCount())
     508             :                         {
     509           2 :                             ReportError(CE_Failure, CPLE_IllegalArg,
     510             :                                         "More color interpretation values "
     511             :                                         "specified than bands in the dataset");
     512           3 :                             return false;
     513             :                         }
     514          14 :                         const auto eColorInterp = GetColorInterp(token.c_str());
     515          14 :                         if (!eColorInterp)
     516             :                         {
     517           1 :                             return false;
     518             :                         }
     519          13 :                         else if (poDS->GetRasterBand(nBandIter)
     520          13 :                                      ->SetColorInterpretation(*eColorInterp) !=
     521             :                                  CE_None)
     522             :                         {
     523           0 :                             return false;
     524             :                         }
     525             :                     }
     526             :                 }
     527          20 :                 if ((bSyntaxAll ? 1 : 0) + (bSyntaxExplicitBand ? 1 : 0) +
     528          10 :                         (bSyntaxImplicitBand ? 1 : 0) !=
     529             :                     1)
     530             :                 {
     531           3 :                     ReportError(CE_Failure, CPLE_IllegalArg,
     532             :                                 "Mix of different syntaxes to specify color "
     533             :                                 "interpretation");
     534           3 :                     return false;
     535             :                 }
     536           7 :                 if (bSyntaxImplicitBand && nBandIter != poDS->GetRasterCount())
     537             :                 {
     538           1 :                     ReportError(CE_Failure, CPLE_IllegalArg,
     539             :                                 "Less color interpretation values specified "
     540             :                                 "than bands in the dataset");
     541           1 :                     return false;
     542             :                 }
     543             :             }
     544             :         }
     545             : 
     546             :         const auto ScaleOffsetSetterLambda =
     547          16 :             [this, poDS](const char *argName,
     548             :                          const std::vector<std::string> &values,
     549          84 :                          CPLErr (GDALRasterBand::*Setter)(double))
     550             :         {
     551          16 :             if (values.size() == 1 && values[0].find('=') == std::string::npos)
     552             :             {
     553           2 :                 const double dfScale = CPLAtof(values[0].c_str());
     554           8 :                 for (int i = 0; i < poDS->GetRasterCount(); ++i)
     555             :                 {
     556           6 :                     if ((poDS->GetRasterBand(i + 1)->*Setter)(dfScale) !=
     557             :                         CE_None)
     558           0 :                         return false;
     559             :                 }
     560             :             }
     561             :             else
     562             :             {
     563          14 :                 int nBandIter = 0;
     564          14 :                 bool bSyntaxExplicitBand = false;
     565          14 :                 bool bSyntaxImplicitBand = false;
     566          40 :                 for (const std::string &token : values)
     567             :                 {
     568             :                     const CPLStringList aosTokens(
     569          32 :                         CSLTokenizeString2(token.c_str(), "=", 0));
     570          32 :                     if (aosTokens.size() == 2)
     571             :                     {
     572           8 :                         bSyntaxExplicitBand = true;
     573           8 :                         const int nBand = atoi(aosTokens[0]);
     574           8 :                         if (nBand <= 0 || nBand > poDS->GetRasterCount())
     575             :                         {
     576           4 :                             ReportError(CE_Failure, CPLE_NotSupported,
     577             :                                         "Invalid band number '%s' in '%s'",
     578             :                                         aosTokens[0], token.c_str());
     579           4 :                             return false;
     580             :                         }
     581           4 :                         const double dfScale = CPLAtof(aosTokens[1]);
     582           4 :                         if ((poDS->GetRasterBand(nBand)->*Setter)(dfScale) !=
     583             :                             CE_None)
     584             :                         {
     585           0 :                             return false;
     586             :                         }
     587             :                     }
     588             :                     else
     589             :                     {
     590          24 :                         bSyntaxImplicitBand = true;
     591          24 :                         ++nBandIter;
     592          24 :                         if (nBandIter > poDS->GetRasterCount())
     593             :                         {
     594           2 :                             ReportError(CE_Failure, CPLE_IllegalArg,
     595             :                                         "More %s values "
     596             :                                         "specified than bands in the dataset",
     597             :                                         argName);
     598           2 :                             return false;
     599             :                         }
     600          22 :                         const double dfScale = CPLAtof(token.c_str());
     601          22 :                         if ((poDS->GetRasterBand(nBandIter)->*Setter)(
     602          22 :                                 dfScale) != CE_None)
     603             :                         {
     604           0 :                             return false;
     605             :                         }
     606             :                     }
     607             :                 }
     608          16 :                 if (((bSyntaxExplicitBand ? 1 : 0) +
     609           8 :                      (bSyntaxImplicitBand ? 1 : 0)) != 1)
     610             :                 {
     611           2 :                     ReportError(CE_Failure, CPLE_IllegalArg,
     612             :                                 "Mix of different syntaxes to specify %s",
     613             :                                 argName);
     614           2 :                     return false;
     615             :                 }
     616           6 :                 if (bSyntaxImplicitBand && nBandIter != poDS->GetRasterCount())
     617             :                 {
     618           2 :                     ReportError(CE_Failure, CPLE_IllegalArg,
     619             :                                 "Less %s values specified "
     620             :                                 "than bands in the dataset",
     621             :                                 argName);
     622           2 :                     return false;
     623             :                 }
     624             :             }
     625             : 
     626           6 :             return true;
     627          62 :         };
     628             : 
     629          62 :         if (!m_scale.empty())
     630             :         {
     631           8 :             if (!ScaleOffsetSetterLambda("scale", m_scale,
     632             :                                          &GDALRasterBand::SetScale))
     633           5 :                 return false;
     634             :         }
     635             : 
     636          57 :         if (!m_offset.empty())
     637             :         {
     638           8 :             if (!ScaleOffsetSetterLambda("offset", m_offset,
     639             :                                          &GDALRasterBand::SetOffset))
     640           5 :                 return false;
     641             :         }
     642             : 
     643          52 :         const CPLStringList aosMD(m_metadata);
     644          64 :         for (const auto &[key, value] : cpl::IterateNameValue(aosMD))
     645             :         {
     646          13 :             if (poDS->SetMetadataItem(key, value) != CE_None)
     647             :             {
     648           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     649             :                             "SetMetadataItem('%s', '%s') failed", key, value);
     650           1 :                 return false;
     651             :             }
     652             :         }
     653             : 
     654          53 :         for (const std::string &key : m_unsetMetadata)
     655             :         {
     656           3 :             if (poDS->SetMetadataItem(key.c_str(), nullptr) != CE_None)
     657             :             {
     658           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     659             :                             "SetMetadataItem('%s', NULL) failed", key.c_str());
     660           1 :                 return false;
     661             :             }
     662             :         }
     663             : 
     664          51 :         for (const std::string &domain : m_unsetMetadataDomain)
     665             :         {
     666           1 :             if (poDS->SetMetadata(nullptr, domain.c_str()) != CE_None)
     667             :             {
     668           0 :                 ReportError(CE_Failure, CPLE_AppDefined,
     669             :                             "SetMetadata(NULL, '%s') failed", domain.c_str());
     670           0 :                 return false;
     671             :             }
     672             :         }
     673             : 
     674          50 :         if (!m_gcps.empty())
     675             :         {
     676           8 :             const auto gcps = ParseGCPs();
     677           8 :             if (gcps.empty())
     678           3 :                 return false;  // error already emitted by ParseGCPs()
     679             : 
     680           5 :             OGRSpatialReference oSRS;
     681           5 :             if (!m_overrideCrs.empty())
     682             :             {
     683           1 :                 oSRS.SetFromUserInput(m_overrideCrs.c_str());
     684           1 :                 oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     685             :             }
     686             : 
     687           5 :             if (poDS->SetGCPs(static_cast<int>(gcps.size()), gcps[0].c_ptr(),
     688          10 :                               oSRS.IsEmpty() ? nullptr : &oSRS) != CE_None)
     689             :             {
     690           1 :                 ReportError(CE_Failure, CPLE_AppDefined, "Setting GCPs failed");
     691           1 :                 return false;
     692             :             }
     693             :         }
     694             : 
     695          46 :         const int nBands = poDS->GetRasterCount();
     696          46 :         int nCurProgress = 0;
     697          46 :         const double dfTotalProgress =
     698          46 :             ((m_stats || m_approxStats) ? nBands : 0) + (m_hist ? nBands : 0);
     699          46 :         if (m_stats || m_approxStats)
     700             :         {
     701           4 :             for (int i = 0; (i < nBands) && ret; ++i)
     702             :             {
     703           4 :                 void *pScaledProgress = GDALCreateScaledProgress(
     704             :                     nCurProgress / dfTotalProgress,
     705           2 :                     (nCurProgress + 1) / dfTotalProgress, ctxt.m_pfnProgress,
     706             :                     ctxt.m_pProgressData);
     707           2 :                 ++nCurProgress;
     708           2 :                 double dfMin = 0.0;
     709           2 :                 double dfMax = 0.0;
     710           2 :                 double dfMean = 0.0;
     711           2 :                 double dfStdDev = 0.0;
     712           4 :                 ret = poDS->GetRasterBand(i + 1)->ComputeStatistics(
     713           2 :                           m_approxStats, &dfMin, &dfMax, &dfMean, &dfStdDev,
     714             :                           pScaledProgress ? GDALScaledProgress : nullptr,
     715           2 :                           pScaledProgress) == CE_None;
     716           2 :                 GDALDestroyScaledProgress(pScaledProgress);
     717             :             }
     718             :         }
     719             : 
     720          46 :         if (m_hist)
     721             :         {
     722           2 :             for (int i = 0; (i < nBands) && ret; ++i)
     723             :             {
     724           2 :                 void *pScaledProgress = GDALCreateScaledProgress(
     725             :                     nCurProgress / dfTotalProgress,
     726           1 :                     (nCurProgress + 1) / dfTotalProgress, ctxt.m_pfnProgress,
     727             :                     ctxt.m_pProgressData);
     728           1 :                 ++nCurProgress;
     729           1 :                 double dfMin = 0.0;
     730           1 :                 double dfMax = 0.0;
     731           1 :                 int nBucketCount = 0;
     732           1 :                 GUIntBig *panHistogram = nullptr;
     733           2 :                 ret = poDS->GetRasterBand(i + 1)->GetDefaultHistogram(
     734             :                           &dfMin, &dfMax, &nBucketCount, &panHistogram, TRUE,
     735             :                           pScaledProgress ? GDALScaledProgress : nullptr,
     736           1 :                           pScaledProgress) == CE_None;
     737           1 :                 if (ret)
     738             :                 {
     739           2 :                     ret = poDS->GetRasterBand(i + 1)->SetDefaultHistogram(
     740           1 :                               dfMin, dfMax, nBucketCount, panHistogram) ==
     741             :                           CE_None;
     742             :                 }
     743           1 :                 CPLFree(panHistogram);
     744           1 :                 GDALDestroyScaledProgress(pScaledProgress);
     745             :             }
     746             :         }
     747             :     }
     748             : 
     749          46 :     return poDS != nullptr;
     750             : }
     751             : 
     752             : GDALRasterEditAlgorithmStandalone::~GDALRasterEditAlgorithmStandalone() =
     753             :     default;
     754             : 
     755             : //! @endcond

Generated by: LCOV version 1.14