LCOV - code coverage report
Current view: top level - apps - gdalalg_vector_update.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 156 166 94.0 %
Date: 2025-12-01 18:11:08 Functions: 3 3 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  "update" step of "vector pipeline"
       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_vector_update.h"
      14             : 
      15             : #include "ogr_p.h"
      16             : #include "ogrsf_frmts.h"
      17             : 
      18             : //! @cond Doxygen_Suppress
      19             : 
      20             : #ifndef _
      21             : #define _(x) (x)
      22             : #endif
      23             : 
      24             : /************************************************************************/
      25             : /*        GDALVectorUpdateAlgorithm::GDALVectorUpdateAlgorithm()        */
      26             : /************************************************************************/
      27             : 
      28          55 : GDALVectorUpdateAlgorithm::GDALVectorUpdateAlgorithm(bool standaloneStep)
      29             :     : GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
      30           0 :                                       ConstructorOptions()
      31          55 :                                           .SetStandaloneStep(standaloneStep)
      32          55 :                                           .SetInputDatasetMaxCount(1)
      33          55 :                                           .SetAddInputLayerNameArgument(false)
      34         110 :                                           .SetAddDefaultArguments(false))
      35             : {
      36          55 :     if (standaloneStep)
      37             :     {
      38          21 :         AddVectorInputArgs(false);
      39             :     }
      40             :     {
      41             :         auto &layerArg = AddArg(GDAL_ARG_NAME_INPUT_LAYER, 0,
      42         110 :                                 _("Input layer name"), &m_inputLayerNames)
      43          55 :                              .SetMaxCount(1);
      44          55 :         auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
      45          55 :         if (inputArg)
      46          21 :             SetAutoCompleteFunctionForLayerName(layerArg, *inputArg);
      47             :     }
      48             : 
      49          55 :     AddProgressArg();
      50             : 
      51          55 :     AddOutputDatasetArg(&m_outputDataset, GDAL_OF_VECTOR)
      52          55 :         .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT);
      53          55 :     AddOutputOpenOptionsArg(&m_outputOpenOptions);
      54          55 :     AddOutputLayerNameArg(&m_outputLayerName);
      55             : 
      56          55 :     m_update = true;
      57          55 :     AddUpdateArg(&m_update).SetDefault(true).SetHidden();
      58             : 
      59         110 :     AddArg("mode", 0, _("Set update mode"), &m_mode)
      60          55 :         .SetDefault(m_mode)
      61          55 :         .SetChoices(MODE_MERGE, MODE_UPDATE_ONLY, MODE_APPEND_ONLY);
      62             : 
      63         110 :     AddArg("key", 0, _("Field(s) used as a key to identify features"), &m_key)
      64          55 :         .SetPackedValuesAllowed(false);
      65          55 : }
      66             : 
      67             : /************************************************************************/
      68             : /*                  GDALVectorUpdateAlgorithm::RunStep()                */
      69             : /************************************************************************/
      70             : 
      71          17 : bool GDALVectorUpdateAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
      72             : {
      73          17 :     auto poSrcDS = m_inputDataset[0].GetDatasetRef();
      74          17 :     CPLAssert(poSrcDS);
      75             : 
      76          17 :     auto poDstDS = m_outputDataset.GetDatasetRef();
      77          17 :     CPLAssert(poDstDS);
      78          17 :     CPLAssert(poDstDS->GetAccess() == GA_Update);
      79             : 
      80          17 :     auto poSrcDriver = poSrcDS->GetDriver();
      81          17 :     auto poDstDriver = poDstDS->GetDriver();
      82          33 :     if (poSrcDS == poDstDS ||
      83          16 :         (poSrcDriver && poDstDriver &&
      84          16 :          !EQUAL(poSrcDriver->GetDescription(), "MEM") &&
      85           1 :          !EQUAL(poDstDriver->GetDescription(), "MEM") &&
      86           1 :          strcmp(poSrcDS->GetDescription(), poDstDS->GetDescription()) == 0))
      87             :     {
      88           2 :         ReportError(CE_Failure, CPLE_NotSupported,
      89             :                     "Input and output datasets must be different");
      90           2 :         return false;
      91             :     }
      92             : 
      93          15 :     if (m_inputLayerNames.empty() && poSrcDS->GetLayerCount() == 1)
      94             :     {
      95          12 :         m_inputLayerNames.push_back(poSrcDS->GetLayer(0)->GetName());
      96             :     }
      97          15 :     if (m_outputLayerName.empty() && poDstDS->GetLayerCount() == 1)
      98             :     {
      99          10 :         m_outputLayerName = poDstDS->GetLayer(0)->GetName();
     100             :     }
     101             : 
     102          15 :     if (m_inputLayerNames.empty())
     103             :     {
     104           2 :         if (!m_outputLayerName.empty())
     105             :         {
     106           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     107             :                         "Please specify the 'input-layer' argument.");
     108           1 :             return false;
     109             :         }
     110             :         else
     111             :         {
     112           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     113             :                         "Please specify the 'input-layer' and 'output-layer' "
     114             :                         "arguments.");
     115           1 :             return false;
     116             :         }
     117             :     }
     118             : 
     119          13 :     auto poSrcLayer = poSrcDS->GetLayerByName(m_inputLayerNames[0].c_str());
     120          13 :     if (!poSrcLayer)
     121             :     {
     122           1 :         ReportError(CE_Failure, CPLE_AppDefined,
     123             :                     "No layer named '%s' in input dataset.",
     124           1 :                     m_inputLayerNames[0].c_str());
     125           1 :         return false;
     126             :     }
     127             : 
     128          12 :     if (m_outputLayerName.empty())
     129             :     {
     130           1 :         ReportError(CE_Failure, CPLE_AppDefined,
     131             :                     "Please specify the 'output-layer' argument.");
     132           1 :         return false;
     133             :     }
     134             : 
     135          11 :     auto poDstLayer = poDstDS->GetLayerByName(m_outputLayerName.c_str());
     136          11 :     if (!poDstLayer)
     137             :     {
     138           1 :         ReportError(CE_Failure, CPLE_AppDefined,
     139             :                     "No layer named '%s' in output dataset",
     140             :                     m_outputLayerName.c_str());
     141           1 :         return false;
     142             :     }
     143             : 
     144          20 :     std::vector<int> srcKeyFieldIndices;
     145          20 :     std::vector<OGRFieldType> keyFieldTypes;
     146          10 :     if (m_key.empty())
     147           6 :         m_key.push_back(SpecialFieldNames[SPF_FID]);
     148          20 :     for (const std::string &key : m_key)
     149             :     {
     150          13 :         if (EQUAL(key.c_str(), SpecialFieldNames[SPF_FID]))
     151             :         {
     152           6 :             srcKeyFieldIndices.push_back(
     153           6 :                 poSrcLayer->GetLayerDefn()->GetFieldCount() + SPF_FID);
     154           6 :             keyFieldTypes.push_back(OFTInteger64);
     155           6 :             continue;
     156             :         }
     157             : 
     158             :         const int nSrcIdx =
     159           7 :             poSrcLayer->GetLayerDefn()->GetFieldIndex(key.c_str());
     160           7 :         if (nSrcIdx < 0)
     161             :         {
     162           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     163             :                         "Cannot find field '%s' in input layer", key.c_str());
     164           3 :             return false;
     165             :         }
     166           6 :         srcKeyFieldIndices.push_back(nSrcIdx);
     167             :         const auto poSrcFieldDefn =
     168           6 :             poSrcLayer->GetLayerDefn()->GetFieldDefn(nSrcIdx);
     169           6 :         const auto eType = poSrcFieldDefn->GetType();
     170           6 :         const OGRFieldType aeAllowedTypes[] = {OFTString, OFTInteger,
     171             :                                                OFTInteger64, OFTReal};
     172           6 :         if (std::find(std::begin(aeAllowedTypes), std::end(aeAllowedTypes),
     173           6 :                       eType) == std::end(aeAllowedTypes))
     174             :         {
     175           1 :             ReportError(CE_Failure, CPLE_NotSupported,
     176             :                         "Type of field '%s' is not one of those supported for "
     177             :                         "a key field: String, Integer, Integer64, Real",
     178             :                         key.c_str());
     179           1 :             return false;
     180             :         }
     181             : 
     182             :         const int nDstIdx =
     183           5 :             poDstLayer->GetLayerDefn()->GetFieldIndex(key.c_str());
     184           5 :         if (nDstIdx < 0)
     185             :         {
     186           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     187             :                         "Cannot find field '%s' in output layer", key.c_str());
     188           1 :             return false;
     189             :         }
     190             :         const auto poDstFieldDefn =
     191           4 :             poDstLayer->GetLayerDefn()->GetFieldDefn(nDstIdx);
     192           4 :         if (poDstFieldDefn->GetType() != eType)
     193             :         {
     194           0 :             ReportError(
     195             :                 CE_Failure, CPLE_NotSupported,
     196             :                 "Type of field '%s' is not the same in input and output layers",
     197             :                 key.c_str());
     198           0 :             return false;
     199             :         }
     200           4 :         keyFieldTypes.push_back(eType);
     201             :     }
     202             : 
     203          13 :     const bool bFIDMatch = m_key.size() == 1 &&
     204           6 :                            EQUAL(m_key[0].c_str(), SpecialFieldNames[SPF_FID]);
     205             :     const GIntBig nFeatureCount =
     206           7 :         ctxt.m_pfnProgress ? poSrcLayer->GetFeatureCount(true) : -1;
     207             : 
     208           7 :     std::string osFilter;
     209           7 :     int nIter = 0;
     210           7 :     bool bRet = true;
     211          21 :     for (const auto &poSrcFeature : *poSrcLayer)
     212             :     {
     213          14 :         ++nIter;
     214          20 :         if (ctxt.m_pfnProgress && nFeatureCount > 0 &&
     215           6 :             !ctxt.m_pfnProgress(static_cast<double>(nIter) / nFeatureCount, "",
     216             :                                 ctxt.m_pProgressData))
     217             :         {
     218           1 :             ReportError(CE_Failure, CPLE_UserInterrupt, "Interrupted by user");
     219           1 :             bRet = false;
     220           1 :             break;
     221             :         }
     222             : 
     223           0 :         std::unique_ptr<OGRFeature> poDstFeature;
     224          13 :         if (bFIDMatch)
     225             :         {
     226          16 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     227           8 :             poDstFeature.reset(poDstLayer->GetFeature(poSrcFeature->GetFID()));
     228             :         }
     229             :         else
     230             :         {
     231           5 :             bool bSkip = false;
     232           5 :             osFilter.clear();
     233          21 :             for (size_t iField = 0; iField < srcKeyFieldIndices.size();
     234             :                  ++iField)
     235             :             {
     236          17 :                 const int nSrcFieldIdx = srcKeyFieldIndices[iField];
     237          17 :                 if (!poSrcFeature->IsFieldSet(nSrcFieldIdx))
     238             :                 {
     239           1 :                     bSkip = true;
     240           1 :                     break;
     241             :                 }
     242          16 :                 if (!osFilter.empty())
     243          12 :                     osFilter += " AND ";
     244          16 :                 osFilter += CPLString(m_key[iField]).SQLQuotedIdentifier();
     245          16 :                 osFilter += " = ";
     246          16 :                 switch (keyFieldTypes[iField])
     247             :                 {
     248           4 :                     case OFTString:
     249             :                     {
     250           4 :                         osFilter += CPLString(poSrcFeature->GetFieldAsString(
     251             :                                                   nSrcFieldIdx))
     252           4 :                                         .SQLQuotedLiteral();
     253           4 :                         break;
     254             :                     }
     255             : 
     256           4 :                     case OFTReal:
     257             :                     {
     258             :                         osFilter += CPLSPrintf(
     259             :                             "%.17g",
     260           4 :                             poSrcFeature->GetFieldAsDouble(nSrcFieldIdx));
     261           4 :                         break;
     262             :                     }
     263             : 
     264           8 :                     default:
     265             :                     {
     266             :                         osFilter += CPLSPrintf(
     267             :                             CPL_FRMT_GIB,
     268           8 :                             poSrcFeature->GetFieldAsInteger64(nSrcFieldIdx));
     269           8 :                         break;
     270             :                     }
     271             :                 }
     272             :             }
     273           5 :             if (bSkip)
     274           1 :                 continue;
     275           4 :             if (poDstLayer->SetAttributeFilter(osFilter.c_str()) != OGRERR_NONE)
     276             :             {
     277           0 :                 bRet = false;
     278           0 :                 break;
     279             :             }
     280           4 :             poDstFeature.reset(poDstLayer->GetNextFeature());
     281           4 :             if (poDstFeature)
     282             :             {
     283             :                 // Check there is only one feature matching the criterion
     284           3 :                 if (std::unique_ptr<OGRFeature>(poDstLayer->GetNextFeature()))
     285             :                 {
     286           1 :                     poDstFeature.reset();
     287             :                 }
     288             :                 else
     289             :                 {
     290           2 :                     CPLDebugOnly("GDAL",
     291             :                                  "Updating output feature " CPL_FRMT_GIB
     292             :                                  " with src input " CPL_FRMT_GIB,
     293             :                                  poDstFeature->GetFID(),
     294             :                                  poSrcFeature->GetFID());
     295             :                 }
     296             :             }
     297             :         }
     298             : 
     299          12 :         if (poDstFeature)
     300             :         {
     301           6 :             if (m_mode != MODE_APPEND_ONLY)
     302             :             {
     303             :                 auto poDstFeatureOri =
     304           5 :                     std::unique_ptr<OGRFeature>(poDstFeature->Clone());
     305           5 :                 const auto nDstFID = poDstFeature->GetFID();
     306           5 :                 poDstFeature->SetFrom(poSrcFeature.get());
     307             :                 // restore FID unset by SetFrom()
     308           5 :                 poDstFeature->SetFID(nDstFID);
     309          10 :                 if (!poDstFeature->Equal(poDstFeatureOri.get()) &&
     310           5 :                     poDstLayer->SetFeature(poDstFeature.get()) != OGRERR_NONE)
     311             :                 {
     312           0 :                     bRet = false;
     313           0 :                     break;
     314             :                 }
     315             :             }
     316             :         }
     317           6 :         else if (m_mode != MODE_UPDATE_ONLY)
     318             :         {
     319             :             poDstFeature =
     320           3 :                 std::make_unique<OGRFeature>(poDstLayer->GetLayerDefn());
     321           3 :             poDstFeature->SetFrom(poSrcFeature.get());
     322           3 :             if (poDstLayer->CreateFeature(poDstFeature.get()) != OGRERR_NONE)
     323             :             {
     324           0 :                 bRet = false;
     325           0 :                 break;
     326             :             }
     327             :         }
     328             :     }
     329             : 
     330           7 :     poDstLayer->SetAttributeFilter(nullptr);
     331             : 
     332           7 :     return bRet;
     333             : }
     334             : 
     335             : /************************************************************************/
     336             : /*                  ~GDALVectorUpdateAlgorithmStandalone()              */
     337             : /************************************************************************/
     338             : 
     339             : GDALVectorUpdateAlgorithmStandalone::~GDALVectorUpdateAlgorithmStandalone() =
     340             :     default;
     341             : 
     342             : /************************************************************************/
     343             : /*              GDALVectorUpdateAlgorithmStandalone::RunImpl()          */
     344             : /************************************************************************/
     345             : 
     346          16 : bool GDALVectorUpdateAlgorithmStandalone::RunImpl(GDALProgressFunc pfnProgress,
     347             :                                                   void *pProgressData)
     348             : {
     349          16 :     GDALPipelineStepRunContext stepCtxt;
     350          16 :     stepCtxt.m_pfnProgress = pfnProgress;
     351          16 :     stepCtxt.m_pProgressData = pProgressData;
     352          32 :     return RunStep(stepCtxt);
     353             : }
     354             : 
     355             : //! @endcond

Generated by: LCOV version 1.14