LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_edit.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 146 146 100.0 %
Date: 2025-05-15 13:16:46 Functions: 4 4 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             : 
      18             : //! @cond Doxygen_Suppress
      19             : 
      20             : #ifndef _
      21             : #define _(x) (x)
      22             : #endif
      23             : 
      24             : /************************************************************************/
      25             : /*          GDALRasterEditAlgorithm::GDALRasterEditAlgorithm()          */
      26             : /************************************************************************/
      27             : 
      28          35 : GDALRasterEditAlgorithm::GDALRasterEditAlgorithm(bool standaloneStep)
      29             :     : GDALRasterPipelineStepAlgorithm(
      30             :           NAME, DESCRIPTION, HELP_URL,
      31             :           // Avoid automatic addition of input/output arguments
      32          35 :           /*standaloneStep = */ false)
      33             : {
      34          35 :     if (standaloneStep)
      35             :     {
      36          21 :         AddProgressArg();
      37             : 
      38             :         AddArg("dataset", 0,
      39             :                _("Dataset (to be updated in-place, unless --auxiliary)"),
      40          42 :                &m_dataset, GDAL_OF_RASTER | GDAL_OF_UPDATE)
      41          21 :             .SetPositional()
      42          21 :             .SetRequired();
      43             :         AddArg("auxiliary", 0,
      44             :                _("Ask for an auxiliary .aux.xml file to be edited"),
      45          42 :                &m_readOnly)
      46          42 :             .AddHiddenAlias("ro")
      47          21 :             .AddHiddenAlias(GDAL_ARG_NAME_READ_ONLY);
      48             : 
      49          21 :         m_standaloneStep = true;
      50             :     }
      51             : 
      52          70 :     AddArg("crs", 0, _("Override CRS (without reprojection)"), &m_overrideCrs)
      53          70 :         .AddHiddenAlias("a_srs")
      54          35 :         .SetIsCRSArg(/*noneAllowed=*/true);
      55             : 
      56          35 :     AddBBOXArg(&m_bbox);
      57             : 
      58          35 :     AddNodataDataTypeArg(&m_nodata, /* noneAllowed = */ true);
      59             : 
      60             :     {
      61             :         auto &arg = AddArg("metadata", 0, _("Add/update dataset metadata item"),
      62          70 :                            &m_metadata)
      63          70 :                         .SetMetaVar("<KEY>=<VALUE>")
      64          35 :                         .SetPackedValuesAllowed(false);
      65           3 :         arg.AddValidationAction([this, &arg]()
      66          38 :                                 { return ParseAndValidateKeyValue(arg); });
      67          35 :         arg.AddHiddenAlias("mo");
      68             :     }
      69             : 
      70             :     AddArg("unset-metadata", 0, _("Remove dataset metadata item"),
      71          70 :            &m_unsetMetadata)
      72          35 :         .SetMetaVar("<KEY>");
      73             : 
      74          35 :     if (standaloneStep)
      75             :     {
      76          42 :         AddArg("stats", 0, _("Compute statistics, using all pixels"), &m_stats)
      77          21 :             .SetMutualExclusionGroup("stats");
      78             :         AddArg("approx-stats", 0,
      79             :                _("Compute statistics, using a subset of pixels"),
      80          42 :                &m_approxStats)
      81          21 :             .SetMutualExclusionGroup("stats");
      82          21 :         AddArg("hist", 0, _("Compute histogram"), &m_hist);
      83             :     }
      84          35 : }
      85             : 
      86             : /************************************************************************/
      87             : /*                GDALRasterEditAlgorithm::RunImpl()                    */
      88             : /************************************************************************/
      89             : 
      90          24 : bool GDALRasterEditAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
      91             :                                       void *pProgressData)
      92             : {
      93          24 :     if (m_standaloneStep)
      94             :     {
      95          17 :         auto poDS = m_dataset.GetDatasetRef();
      96          17 :         CPLAssert(poDS);
      97          17 :         if (poDS->GetAccess() != GA_Update && !m_readOnly)
      98             :         {
      99           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     100             :                         "Dataset should be opened in update mode unless "
     101             :                         "--auxiliary is set");
     102           1 :             return false;
     103             :         }
     104             : 
     105          16 :         if (m_overrideCrs == "null" || m_overrideCrs == "none")
     106             :         {
     107           2 :             if (poDS->SetSpatialRef(nullptr) != CE_None)
     108             :             {
     109           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     110             :                             "SetSpatialRef(%s) failed", m_overrideCrs.c_str());
     111           1 :                 return false;
     112             :             }
     113             :         }
     114          14 :         else if (!m_overrideCrs.empty())
     115             :         {
     116           2 :             OGRSpatialReference oSRS;
     117           2 :             oSRS.SetFromUserInput(m_overrideCrs.c_str());
     118           2 :             oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     119           2 :             if (poDS->SetSpatialRef(&oSRS) != CE_None)
     120             :             {
     121           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     122             :                             "SetSpatialRef(%s) failed", m_overrideCrs.c_str());
     123           1 :                 return false;
     124             :             }
     125             :         }
     126             : 
     127          14 :         if (!m_bbox.empty())
     128             :         {
     129           3 :             if (poDS->GetRasterXSize() == 0 || poDS->GetRasterYSize() == 0)
     130             :             {
     131           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     132             :                             "Cannot set extent because one of dataset height "
     133             :                             "or width is null");
     134           2 :                 return false;
     135             :             }
     136             :             double adfGT[6];
     137           2 :             adfGT[0] = m_bbox[0];
     138           2 :             adfGT[1] = (m_bbox[2] - m_bbox[0]) / poDS->GetRasterXSize();
     139           2 :             adfGT[2] = 0;
     140           2 :             adfGT[3] = m_bbox[3];
     141           2 :             adfGT[4] = 0;
     142           2 :             adfGT[5] = -(m_bbox[3] - m_bbox[1]) / poDS->GetRasterYSize();
     143           2 :             if (poDS->SetGeoTransform(adfGT) != CE_None)
     144             :             {
     145           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     146             :                             "Setting extent failed");
     147           1 :                 return false;
     148             :             }
     149             :         }
     150             : 
     151          12 :         if (!m_nodata.empty())
     152             :         {
     153           4 :             for (int i = 0; i < poDS->GetRasterCount(); ++i)
     154             :             {
     155           2 :                 if (EQUAL(m_nodata.c_str(), "none"))
     156           1 :                     poDS->GetRasterBand(i + 1)->DeleteNoDataValue();
     157             :                 else
     158           2 :                     poDS->GetRasterBand(i + 1)->SetNoDataValue(
     159           1 :                         CPLAtof(m_nodata.c_str()));
     160             :             }
     161             :         }
     162             : 
     163          24 :         const CPLStringList aosMD(m_metadata);
     164          14 :         for (const auto &[key, value] : cpl::IterateNameValue(aosMD))
     165             :         {
     166           3 :             if (poDS->SetMetadataItem(key, value) != CE_None)
     167             :             {
     168           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     169             :                             "SetMetadataItem('%s', '%s') failed", key, value);
     170           1 :                 return false;
     171             :             }
     172             :         }
     173             : 
     174          12 :         for (const std::string &key : m_unsetMetadata)
     175             :         {
     176           2 :             if (poDS->SetMetadataItem(key.c_str(), nullptr) != CE_None)
     177             :             {
     178           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     179             :                             "SetMetadataItem('%s', NULL) failed", key.c_str());
     180           1 :                 return false;
     181             :             }
     182             :         }
     183             : 
     184          10 :         const int nBands = poDS->GetRasterCount();
     185          10 :         int nCurProgress = 0;
     186          10 :         const double dfTotalProgress =
     187          10 :             ((m_stats || m_approxStats) ? nBands : 0) + (m_hist ? nBands : 0);
     188          10 :         bool ret = true;
     189          10 :         if (m_stats || m_approxStats)
     190             :         {
     191           4 :             for (int i = 0; (i < nBands) && ret; ++i)
     192             :             {
     193           4 :                 void *pScaledProgress = GDALCreateScaledProgress(
     194             :                     nCurProgress / dfTotalProgress,
     195           2 :                     (nCurProgress + 1) / dfTotalProgress, pfnProgress,
     196             :                     pProgressData);
     197           2 :                 ++nCurProgress;
     198           2 :                 double dfMin = 0.0;
     199           2 :                 double dfMax = 0.0;
     200           2 :                 double dfMean = 0.0;
     201           2 :                 double dfStdDev = 0.0;
     202           2 :                 ret = poDS->GetRasterBand(i + 1)->ComputeStatistics(
     203           2 :                           m_approxStats, &dfMin, &dfMax, &dfMean, &dfStdDev,
     204           2 :                           GDALScaledProgress, pScaledProgress) == CE_None;
     205           2 :                 GDALDestroyScaledProgress(pScaledProgress);
     206             :             }
     207             :         }
     208          10 :         if (m_hist)
     209             :         {
     210           2 :             for (int i = 0; (i < nBands) && ret; ++i)
     211             :             {
     212           2 :                 void *pScaledProgress = GDALCreateScaledProgress(
     213             :                     nCurProgress / dfTotalProgress,
     214           1 :                     (nCurProgress + 1) / dfTotalProgress, pfnProgress,
     215             :                     pProgressData);
     216           1 :                 ++nCurProgress;
     217           1 :                 double dfMin = 0.0;
     218           1 :                 double dfMax = 0.0;
     219           1 :                 int nBucketCount = 0;
     220           1 :                 GUIntBig *panHistogram = nullptr;
     221           1 :                 ret = poDS->GetRasterBand(i + 1)->GetDefaultHistogram(
     222             :                           &dfMin, &dfMax, &nBucketCount, &panHistogram, TRUE,
     223           1 :                           GDALScaledProgress, pScaledProgress) == CE_None;
     224           1 :                 if (ret)
     225             :                 {
     226           2 :                     ret = poDS->GetRasterBand(i + 1)->SetDefaultHistogram(
     227           1 :                               dfMin, dfMax, nBucketCount, panHistogram) ==
     228             :                           CE_None;
     229             :                 }
     230           1 :                 CPLFree(panHistogram);
     231           1 :                 GDALDestroyScaledProgress(pScaledProgress);
     232             :             }
     233             :         }
     234             : 
     235          10 :         return ret;
     236             :     }
     237             :     else
     238             :     {
     239           7 :         return RunStep(pfnProgress, pProgressData);
     240             :     }
     241             : }
     242             : 
     243             : /************************************************************************/
     244             : /*                GDALRasterEditAlgorithm::RunStep()                    */
     245             : /************************************************************************/
     246             : 
     247           7 : bool GDALRasterEditAlgorithm::RunStep(GDALProgressFunc, void *)
     248             : {
     249           7 :     CPLAssert(m_inputDataset.GetDatasetRef());
     250           7 :     CPLAssert(m_outputDataset.GetName().empty());
     251           7 :     CPLAssert(!m_outputDataset.GetDatasetRef());
     252             : 
     253           7 :     CPLStringList aosOptions;
     254           7 :     aosOptions.AddString("-of");
     255           7 :     aosOptions.AddString("VRT");
     256           7 :     if (!m_overrideCrs.empty())
     257             :     {
     258           2 :         aosOptions.AddString("-a_srs");
     259           2 :         aosOptions.AddString(m_overrideCrs.c_str());
     260             :     }
     261           7 :     if (!m_bbox.empty())
     262             :     {
     263           1 :         aosOptions.AddString("-a_ullr");
     264           1 :         aosOptions.AddString(CPLSPrintf("%.17g", m_bbox[0]));  // upper-left X
     265           1 :         aosOptions.AddString(CPLSPrintf("%.17g", m_bbox[3]));  // upper-left Y
     266           1 :         aosOptions.AddString(CPLSPrintf("%.17g", m_bbox[2]));  // lower-right X
     267           1 :         aosOptions.AddString(CPLSPrintf("%.17g", m_bbox[1]));  // lower-right Y
     268             :     }
     269             : 
     270           9 :     for (const auto &val : m_metadata)
     271             :     {
     272           2 :         aosOptions.AddString("-mo");
     273           2 :         aosOptions.AddString(val.c_str());
     274             :     }
     275             : 
     276           8 :     for (const std::string &key : m_unsetMetadata)
     277             :     {
     278           1 :         aosOptions.AddString("-mo");
     279           1 :         aosOptions.AddString((key + "=").c_str());
     280             :     }
     281             : 
     282           7 :     if (!m_nodata.empty())
     283             :     {
     284           2 :         aosOptions.AddString("-a_nodata");
     285           2 :         aosOptions.AddString(m_nodata);
     286             :     }
     287             : 
     288             :     GDALTranslateOptions *psOptions =
     289           7 :         GDALTranslateOptionsNew(aosOptions.List(), nullptr);
     290             : 
     291           7 :     GDALDatasetH hSrcDS = GDALDataset::ToHandle(m_inputDataset.GetDatasetRef());
     292             :     auto poRetDS =
     293           7 :         GDALDataset::FromHandle(GDALTranslate("", hSrcDS, psOptions, nullptr));
     294           7 :     GDALTranslateOptionsFree(psOptions);
     295           7 :     const bool ok = poRetDS != nullptr;
     296           7 :     if (ok)
     297           7 :         m_outputDataset.Set(std::unique_ptr<GDALDataset>(poRetDS));
     298             : 
     299          14 :     return ok;
     300             : }
     301             : 
     302             : //! @endcond

Generated by: LCOV version 1.14