LCOV - code coverage report
Current view: top level - apps - gdalalg_vector_combine.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 266 290 91.7 %
Date: 2026-06-03 12:46:18 Functions: 15 16 93.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             : *
       3             :  * Project:  GDAL
       4             :  * Purpose:  "gdal vector combine" subcommand
       5             :  * Author:   Daniel Baston
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2025-2026, ISciences LLC
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdalalg_vector_combine.h"
      14             : 
      15             : #include "cpl_enumerate.h"
      16             : #include "cpl_error.h"
      17             : #include "gdal_priv.h"
      18             : #include "gdalalg_vector_geom.h"
      19             : #include "ogr_geometry.h"
      20             : 
      21             : #include <algorithm>
      22             : #include <cinttypes>
      23             : #include <optional>
      24             : 
      25             : #ifndef _
      26             : #define _(x) (x)
      27             : #endif
      28             : 
      29             : //! @cond Doxygen_Suppress
      30             : 
      31         104 : GDALVectorCombineAlgorithm::GDALVectorCombineAlgorithm(bool standaloneStep)
      32             :     : GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
      33         104 :                                       standaloneStep)
      34             : {
      35             :     auto &groupByArg =
      36             :         AddArg("group-by", 0,
      37             :                _("Names of field(s) by which inputs should be grouped"),
      38         208 :                &m_groupBy)
      39         104 :             .SetDuplicateValuesAllowed(false);
      40         104 :     SetAutoCompleteFunctionForFieldName(
      41         104 :         groupByArg, GetArg(GDAL_ARG_NAME_INPUT_LAYER),
      42             :         /* attributeFields = */ true,
      43         104 :         /* geometryFields = */ false, m_inputDataset);
      44             : 
      45             :     AddArg("keep-nested", 0,
      46             :            _("Avoid combining the components of multipart geometries"),
      47         104 :            &m_keepNested);
      48             : 
      49             :     AddArg("add-extra-fields", 0,
      50             :            _("Whether to add extra fields, depending on if they have identical "
      51             :              "values within each group"),
      52         208 :            &m_addExtraFields)
      53         104 :         .SetChoices(NO, SOMETIMES_IDENTICAL, ALWAYS_IDENTICAL)
      54         104 :         .SetDefault(m_addExtraFields)
      55             :         .AddValidationAction(
      56           4 :             [this]()
      57             :             {
      58             :                 // We check the SQLITE driver availability, because we need to
      59             :                 // issue a SQL request using the SQLITE dialect, but that works
      60             :                 // on any source dataset.
      61           8 :                 if (m_addExtraFields != NO &&
      62           4 :                     GetGDALDriverManager()->GetDriverByName("SQLITE") ==
      63             :                         nullptr)
      64             :                 {
      65           0 :                     ReportError(CE_Failure, CPLE_NotSupported,
      66             :                                 "The SQLITE driver must be available for "
      67             :                                 "add-extra-fields=%s",
      68             :                                 m_addExtraFields.c_str());
      69           0 :                     return false;
      70             :                 }
      71           4 :                 return true;
      72         104 :             });
      73         104 : }
      74             : 
      75             : namespace
      76             : {
      77             : class GDALVectorCombineOutputLayer final
      78             :     : public GDALVectorNonStreamingAlgorithmLayer
      79             : {
      80             :     /** Identify which fields have, at least for one group, the same
      81             :      * value within the rows of the group, and add them to the destination
      82             :      * feature definition, after the group-by fields.
      83             :      */
      84           2 :     void IdentifySrcFieldsThatCanBeCopied(GDALDataset &srcDS,
      85             :                                           const std::string &addExtraFields)
      86             :     {
      87           2 :         const OGRFeatureDefn *srcDefn = m_srcLayer.GetLayerDefn();
      88           2 :         if (srcDefn->GetFieldCount() > static_cast<int>(m_groupBy.size()))
      89             :         {
      90           4 :             std::vector<std::pair<std::string, int>> extraFieldCandidates;
      91             : 
      92           2 :             const auto itSrcFields = srcDefn->GetFields();
      93          20 :             for (const auto [iSrcField, srcFieldDefn] :
      94          22 :                  cpl::enumerate(itSrcFields))
      95             :             {
      96          10 :                 const char *fieldName = srcFieldDefn->GetNameRef();
      97          10 :                 if (std::find(m_groupBy.begin(), m_groupBy.end(), fieldName) ==
      98          20 :                     m_groupBy.end())
      99             :                 {
     100             :                     extraFieldCandidates.emplace_back(
     101           8 :                         fieldName, static_cast<int>(iSrcField));
     102             :                 }
     103             :             }
     104             : 
     105           4 :             std::string sql("SELECT ");
     106           2 :             bool addComma = false;
     107          10 :             for (const auto &[fieldName, _] : extraFieldCandidates)
     108             :             {
     109           8 :                 if (addComma)
     110           6 :                     sql += ", ";
     111           8 :                 addComma = true;
     112           8 :                 if (addExtraFields ==
     113             :                     GDALVectorCombineAlgorithm::ALWAYS_IDENTICAL)
     114           4 :                     sql += "MIN(";
     115             :                 else
     116           4 :                     sql += "MAX(";
     117           8 :                 sql += CPLQuotedSQLIdentifier(fieldName.c_str());
     118           8 :                 sql += ')';
     119             :             }
     120           2 :             sql += " FROM (SELECT ";
     121           2 :             addComma = false;
     122          10 :             for (const auto &[fieldName, _] : extraFieldCandidates)
     123             :             {
     124           8 :                 if (addComma)
     125           6 :                     sql += ", ";
     126           8 :                 addComma = true;
     127           8 :                 sql += "(COUNT(DISTINCT COALESCE(";
     128           8 :                 sql += CPLQuotedSQLIdentifier(fieldName.c_str());
     129           8 :                 sql += ", '__NULL__')) == 1) AS ";
     130           8 :                 sql += CPLQuotedSQLIdentifier(fieldName.c_str());
     131             :             }
     132           2 :             sql += " FROM ";
     133           2 :             sql += CPLQuotedSQLIdentifier(GetLayerDefn()->GetName());
     134           2 :             if (!m_groupBy.empty())
     135             :             {
     136           2 :                 sql += " GROUP BY ";
     137           2 :                 addComma = false;
     138           4 :                 for (const auto &fieldName : m_groupBy)
     139             :                 {
     140           2 :                     if (addComma)
     141           0 :                         sql += ", ";
     142           2 :                     addComma = true;
     143           2 :                     sql += CPLQuotedSQLIdentifier(fieldName.c_str());
     144             :                 }
     145             :             }
     146           2 :             sql += ") dummy_table_name";
     147             : 
     148           2 :             auto poSQLyr = srcDS.ExecuteSQL(sql.c_str(), nullptr, "SQLite");
     149           2 :             if (poSQLyr)
     150             :             {
     151             :                 auto poResultFeature =
     152           4 :                     std::unique_ptr<OGRFeature>(poSQLyr->GetNextFeature());
     153           2 :                 if (poResultFeature)
     154             :                 {
     155           2 :                     CPLAssert(poResultFeature->GetFieldCount() ==
     156             :                               static_cast<int>(extraFieldCandidates.size()));
     157          16 :                     for (const auto &[iSqlCol, srcFieldInfo] :
     158          18 :                          cpl::enumerate(extraFieldCandidates))
     159             :                     {
     160           8 :                         const int iSrcField = srcFieldInfo.second;
     161           8 :                         if (poResultFeature->GetFieldAsInteger(
     162           8 :                                 static_cast<int>(iSqlCol)) == 1)
     163             :                         {
     164          10 :                             m_defn->AddFieldDefn(
     165           5 :                                 srcDefn->GetFieldDefn(iSrcField));
     166           5 :                             m_srcExtraFieldIndices.push_back(iSrcField);
     167             :                         }
     168             :                         else
     169             :                         {
     170           3 :                             CPLDebugOnly(
     171             :                                 "gdalalg_vector_combine",
     172             :                                 "Field %s has the same values within a group",
     173             :                                 srcFieldInfo.first.c_str());
     174             :                         }
     175             :                     }
     176             :                 }
     177           2 :                 srcDS.ReleaseResultSet(poSQLyr);
     178             :             }
     179             :         }
     180           2 :     }
     181             : 
     182             :   public:
     183          21 :     explicit GDALVectorCombineOutputLayer(
     184             :         GDALDataset &srcDS, OGRLayer &srcLayer, int geomFieldIndex,
     185             :         const std::vector<std::string> &groupBy, bool keepNested,
     186             :         const std::string &addExtraFields)
     187          21 :         : GDALVectorNonStreamingAlgorithmLayer(srcLayer, geomFieldIndex),
     188             :           m_groupBy(groupBy), m_defn(OGRFeatureDefnRefCountedPtr::makeInstance(
     189          21 :                                   srcLayer.GetLayerDefn()->GetName())),
     190          42 :           m_keepNested(keepNested)
     191             :     {
     192          21 :         const OGRFeatureDefn *srcDefn = m_srcLayer.GetLayerDefn();
     193             : 
     194             :         // Copy field definitions for attribute fields used in
     195             :         // --group-by. All other attributes are discarded.
     196          29 :         for (const auto &fieldName : m_groupBy)
     197             :         {
     198             :             // RunStep already checked that the field exists
     199           8 :             const auto iField = srcDefn->GetFieldIndex(fieldName.c_str());
     200           8 :             CPLAssert(iField >= 0);
     201             : 
     202           8 :             m_srcGroupByFieldIndices.push_back(iField);
     203           8 :             m_defn->AddFieldDefn(srcDefn->GetFieldDefn(iField));
     204             :         }
     205             : 
     206          21 :         if (addExtraFields != GDALVectorCombineAlgorithm::NO)
     207           2 :             IdentifySrcFieldsThatCanBeCopied(srcDS, addExtraFields);
     208             : 
     209             :         // Create a new geometry field corresponding to each input geometry
     210             :         // field. An appropriate type is worked out below.
     211          21 :         m_defn->SetGeomType(wkbNone);  // Remove default geometry field
     212          43 :         for (const OGRGeomFieldDefn *srcGeomDefn : srcDefn->GetGeomFields())
     213             :         {
     214          22 :             const auto eSrcGeomType = srcGeomDefn->GetType();
     215          22 :             const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eSrcGeomType));
     216          22 :             const bool bHasM = CPL_TO_BOOL(OGR_GT_HasM(eSrcGeomType));
     217             : 
     218             :             OGRwkbGeometryType eDstGeomType =
     219          22 :                 OGR_GT_SetModifier(wkbGeometryCollection, bHasZ, bHasM);
     220             : 
     221             :             // If the layer claims to have single-part geometries, choose a more
     222             :             // specific output type like "MultiPoint" rather than "GeometryCollection"
     223          40 :             if (wkbFlatten(eSrcGeomType) != wkbUnknown &&
     224          18 :                 !OGR_GT_IsSubClassOf(wkbFlatten(eSrcGeomType),
     225             :                                      wkbGeometryCollection))
     226             :             {
     227          18 :                 eDstGeomType = OGR_GT_GetCollection(eSrcGeomType);
     228             :             }
     229             : 
     230             :             auto dstGeomDefn = std::make_unique<OGRGeomFieldDefn>(
     231          44 :                 srcGeomDefn->GetNameRef(), eDstGeomType);
     232          22 :             dstGeomDefn->SetSpatialRef(srcGeomDefn->GetSpatialRef());
     233          22 :             m_defn->AddGeomFieldDefn(std::move(dstGeomDefn));
     234             :         }
     235          21 :     }
     236             : 
     237          19 :     GIntBig GetFeatureCount(int bForce) override
     238             :     {
     239          19 :         if (m_poAttrQuery == nullptr && m_poFilterGeom == nullptr)
     240             :         {
     241          13 :             return static_cast<GIntBig>(m_features.size());
     242             :         }
     243             : 
     244           6 :         return OGRLayer::GetFeatureCount(bForce);
     245             :     }
     246             : 
     247         223 :     const OGRFeatureDefn *GetLayerDefn() const override
     248             :     {
     249         223 :         return m_defn.get();
     250             :     }
     251             : 
     252           4 :     OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
     253             :                       bool bForce) override
     254             :     {
     255           4 :         return m_srcLayer.GetExtent(iGeomField, psExtent, bForce);
     256             :     }
     257             : 
     258           0 :     OGRErr IGetExtent3D(int iGeomField, OGREnvelope3D *psExtent,
     259             :                         bool bForce) override
     260             :     {
     261           0 :         return m_srcLayer.GetExtent3D(iGeomField, psExtent, bForce);
     262             :     }
     263             : 
     264         166 :     std::unique_ptr<OGRFeature> GetNextProcessedFeature() override
     265             :     {
     266         166 :         if (!m_itFeature)
     267             :         {
     268          61 :             m_itFeature = m_features.begin();
     269             :         }
     270             : 
     271         166 :         if (m_itFeature.value() == m_features.end())
     272             :         {
     273          37 :             return nullptr;
     274             :         }
     275             : 
     276             :         std::unique_ptr<OGRFeature> feature(
     277         258 :             m_itFeature.value()->second->Clone());
     278         129 :         feature->SetFID(m_nProcessedFeaturesRead++);
     279         129 :         ++m_itFeature.value();
     280         129 :         return feature;
     281             :     }
     282             : 
     283          21 :     bool Process(GDALProgressFunc pfnProgress, void *pProgressData) override
     284             :     {
     285          21 :         const int nGeomFields = m_srcLayer.GetLayerDefn()->GetGeomFieldCount();
     286             : 
     287             :         const GIntBig nLayerFeatures =
     288          21 :             m_srcLayer.TestCapability(OLCFastFeatureCount)
     289          21 :                 ? m_srcLayer.GetFeatureCount(false)
     290          21 :                 : -1;
     291             :         const double dfInvLayerFeatures =
     292          21 :             1.0 / std::max(1.0, static_cast<double>(nLayerFeatures));
     293             : 
     294          21 :         GIntBig nFeaturesRead = 0;
     295             : 
     296             :         struct PairSourceFeatureUniqueValues
     297             :         {
     298             :             std::unique_ptr<OGRFeature> poSrcFeature{};
     299             :             std::vector<std::optional<std::string>> srcUniqueValues{};
     300             :         };
     301             : 
     302             :         std::map<OGRFeature *, PairSourceFeatureUniqueValues>
     303          42 :             mapDstFeatureToOtherFields;
     304             : 
     305          42 :         std::vector<std::string> fieldValues(m_srcGroupByFieldIndices.size());
     306             :         std::vector<std::string> extraFieldValues(
     307          42 :             m_srcExtraFieldIndices.size());
     308             : 
     309             :         std::vector<int> srcDstFieldMap(
     310          42 :             m_srcLayer.GetLayerDefn()->GetFieldCount(), -1);
     311          16 :         for (const auto [iDstField, iSrcField] :
     312          29 :              cpl::enumerate(m_srcGroupByFieldIndices))
     313             :         {
     314           8 :             srcDstFieldMap[iSrcField] = static_cast<int>(iDstField);
     315             :         }
     316             : 
     317         108 :         for (const auto &srcFeature : m_srcLayer)
     318             :         {
     319         112 :             for (const auto [iDstField, iSrcField] :
     320         199 :                  cpl::enumerate(m_srcGroupByFieldIndices))
     321             :             {
     322          56 :                 fieldValues[iDstField] =
     323          56 :                     srcFeature->GetFieldAsString(iSrcField);
     324             :             }
     325             : 
     326          40 :             for (const auto [iExtraField, iSrcField] :
     327         127 :                  cpl::enumerate(m_srcExtraFieldIndices))
     328             :             {
     329          20 :                 extraFieldValues[iExtraField] =
     330          20 :                     srcFeature->GetFieldAsString(iSrcField);
     331             :             }
     332             : 
     333             :             OGRFeature *dstFeature;
     334             : 
     335          87 :             if (auto it = m_features.find(fieldValues); it == m_features.end())
     336             :             {
     337          35 :                 it = m_features
     338          70 :                          .insert(std::pair(
     339             :                              fieldValues,
     340         105 :                              std::make_unique<OGRFeature>(m_defn.get())))
     341             :                          .first;
     342          35 :                 dstFeature = it->second.get();
     343             : 
     344          35 :                 dstFeature->SetFrom(srcFeature.get(), srcDstFieldMap.data(),
     345             :                                     false);
     346             : 
     347          71 :                 for (int iGeomField = 0; iGeomField < nGeomFields; iGeomField++)
     348             :                 {
     349             :                     OGRGeomFieldDefn *poGeomDefn =
     350          36 :                         m_defn->GetGeomFieldDefn(iGeomField);
     351          36 :                     const auto eGeomType = poGeomDefn->GetType();
     352             : 
     353             :                     std::unique_ptr<OGRGeometry> poGeom(
     354          36 :                         OGRGeometryFactory::createGeometry(eGeomType));
     355          36 :                     poGeom->assignSpatialReference(poGeomDefn->GetSpatialRef());
     356             : 
     357          36 :                     dstFeature->SetGeomField(iGeomField, std::move(poGeom));
     358             :                 }
     359             : 
     360          35 :                 if (!m_srcExtraFieldIndices.empty())
     361             :                 {
     362           8 :                     PairSourceFeatureUniqueValues pair;
     363           4 :                     pair.poSrcFeature.reset(srcFeature->Clone());
     364          14 :                     for (const std::string &s : extraFieldValues)
     365          10 :                         pair.srcUniqueValues.push_back(s);
     366           4 :                     mapDstFeatureToOtherFields[dstFeature] = std::move(pair);
     367             :                 }
     368             :             }
     369             :             else
     370             :             {
     371          52 :                 dstFeature = it->second.get();
     372             : 
     373             :                 // Check that the extra field values for that source feature
     374             :                 // are the same as for other source features of the same group.
     375             :                 // If not the case, cancel the extra field value for that group.
     376          52 :                 if (!m_srcExtraFieldIndices.empty())
     377             :                 {
     378             :                     auto iterOtherFields =
     379           4 :                         mapDstFeatureToOtherFields.find(dstFeature);
     380           4 :                     CPLAssert(iterOtherFields !=
     381             :                               mapDstFeatureToOtherFields.end());
     382             :                     auto &srcUniqueValues =
     383           4 :                         iterOtherFields->second.srcUniqueValues;
     384           4 :                     CPLAssert(srcUniqueValues.size() ==
     385             :                               extraFieldValues.size());
     386          20 :                     for (const auto &[i, sVal] :
     387          24 :                          cpl::enumerate(extraFieldValues))
     388             :                     {
     389          20 :                         if (srcUniqueValues[i].has_value() &&
     390          10 :                             *(srcUniqueValues[i]) != sVal)
     391             :                         {
     392           1 :                             srcUniqueValues[i].reset();
     393             :                         }
     394             :                     }
     395             :                 }
     396             :             }
     397             : 
     398         176 :             for (int iGeomField = 0; iGeomField < nGeomFields; iGeomField++)
     399             :             {
     400             :                 OGRGeomFieldDefn *poGeomFieldDefn =
     401          89 :                     m_defn->GetGeomFieldDefn(iGeomField);
     402             : 
     403             :                 std::unique_ptr<OGRGeometry> poSrcGeom(
     404          89 :                     srcFeature->StealGeometry(iGeomField));
     405          89 :                 if (poSrcGeom != nullptr && !poSrcGeom->IsEmpty())
     406             :                 {
     407          87 :                     const auto eSrcType = poSrcGeom->getGeometryType();
     408          87 :                     const auto bSrcIsCollection = OGR_GT_IsSubClassOf(
     409             :                         wkbFlatten(eSrcType), wkbGeometryCollection);
     410             :                     const auto bDstIsUntypedCollection =
     411          87 :                         wkbFlatten(poGeomFieldDefn->GetType()) ==
     412          87 :                         wkbGeometryCollection;
     413             : 
     414             :                     // Did this geometry unexpectedly have Z?
     415          87 :                     if (OGR_GT_HasZ(eSrcType) !=
     416          87 :                         OGR_GT_HasZ(poGeomFieldDefn->GetType()))
     417             :                     {
     418           4 :                         AddZ(iGeomField);
     419             :                     }
     420             : 
     421             :                     // Did this geometry unexpectedly have M?
     422          87 :                     if (OGR_GT_HasM(eSrcType) !=
     423          87 :                         OGR_GT_HasM(poGeomFieldDefn->GetType()))
     424             :                     {
     425          10 :                         AddM(iGeomField);
     426             :                     }
     427             : 
     428             :                     // Do we need to change the output from a typed collection
     429             :                     // like MultiPolygon to a generic GeometryCollection?
     430          87 :                     if (m_keepNested && bSrcIsCollection &&
     431           3 :                         !bDstIsUntypedCollection)
     432             :                     {
     433           2 :                         SetTypeGeometryCollection(iGeomField);
     434             :                     }
     435             : 
     436             :                     OGRGeometryCollection *poDstGeom =
     437          87 :                         cpl::down_cast<OGRGeometryCollection *>(
     438             :                             dstFeature->GetGeomFieldRef(iGeomField));
     439             : 
     440          87 :                     if (m_keepNested || !bSrcIsCollection)
     441             :                     {
     442          84 :                         if (poDstGeom->addGeometry(std::move(poSrcGeom)) !=
     443             :                             OGRERR_NONE)
     444             :                         {
     445           0 :                             CPLError(
     446             :                                 CE_Failure, CPLE_AppDefined,
     447             :                                 "Failed to add geometry of type %s to output "
     448             :                                 "feature of type %s",
     449             :                                 OGRGeometryTypeToName(eSrcType),
     450             :                                 OGRGeometryTypeToName(
     451           0 :                                     poDstGeom->getGeometryType()));
     452           0 :                             return false;
     453             :                         }
     454             :                     }
     455             :                     else
     456             :                     {
     457             :                         std::unique_ptr<OGRGeometryCollection>
     458             :                             poSrcGeomCollection(
     459           3 :                                 poSrcGeom.release()->toGeometryCollection());
     460           6 :                         if (poDstGeom->addGeometryComponents(
     461           6 :                                 std::move(poSrcGeomCollection)) != OGRERR_NONE)
     462             :                         {
     463           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
     464             :                                      "Failed to add components from geometry "
     465             :                                      "of type %s to output "
     466             :                                      "feature of type %s",
     467             :                                      OGRGeometryTypeToName(eSrcType),
     468             :                                      OGRGeometryTypeToName(
     469           0 :                                          poDstGeom->getGeometryType()));
     470           0 :                             return false;
     471             :                         }
     472             :                     }
     473             :                 }
     474             :             }
     475             : 
     476          87 :             if (pfnProgress && nLayerFeatures > 0 &&
     477           0 :                 !pfnProgress(static_cast<double>(++nFeaturesRead) *
     478             :                                  dfInvLayerFeatures,
     479             :                              "", pProgressData))
     480             :             {
     481           0 :                 CPLError(CE_Failure, CPLE_UserInterrupt, "Interrupted by user");
     482           0 :                 return false;
     483             :             }
     484             :         }
     485             : 
     486             :         // Copy extra fields from source features that have a same value
     487             :         // among each groups
     488           8 :         for (const auto &[poDstFeature, pairSourceFeatureUniqueValues] :
     489          29 :              mapDstFeatureToOtherFields)
     490             :         {
     491          20 :             for (const auto [iExtraField, iSrcField] :
     492          24 :                  cpl::enumerate(m_srcExtraFieldIndices))
     493             :             {
     494             :                 const int iDstField = static_cast<int>(
     495          10 :                     m_srcGroupByFieldIndices.size() + iExtraField);
     496          10 :                 if (pairSourceFeatureUniqueValues.srcUniqueValues[iExtraField])
     497             :                 {
     498             :                     const auto poRawField =
     499             :                         pairSourceFeatureUniqueValues.poSrcFeature
     500           9 :                             ->GetRawFieldRef(iSrcField);
     501           9 :                     poDstFeature->SetField(iDstField, poRawField);
     502             :                 }
     503             :             }
     504             :         }
     505             : 
     506          21 :         if (pfnProgress)
     507             :         {
     508           0 :             pfnProgress(1.0, "", pProgressData);
     509             :         }
     510             : 
     511          21 :         return true;
     512             :     }
     513             : 
     514          39 :     int TestCapability(const char *pszCap) const override
     515             :     {
     516          39 :         if (EQUAL(pszCap, OLCFastFeatureCount))
     517             :         {
     518           0 :             return true;
     519             :         }
     520             : 
     521          39 :         if (EQUAL(pszCap, OLCStringsAsUTF8) ||
     522          26 :             EQUAL(pszCap, OLCFastGetExtent) ||
     523          24 :             EQUAL(pszCap, OLCFastGetExtent3D) ||
     524          24 :             EQUAL(pszCap, OLCCurveGeometries) ||
     525          20 :             EQUAL(pszCap, OLCMeasuredGeometries) ||
     526          16 :             EQUAL(pszCap, OLCZGeometries))
     527             :         {
     528          26 :             return m_srcLayer.TestCapability(pszCap);
     529             :         }
     530             : 
     531          13 :         return false;
     532             :     }
     533             : 
     534          99 :     void ResetReading() override
     535             :     {
     536          99 :         m_itFeature.reset();
     537          99 :         m_nProcessedFeaturesRead = 0;
     538          99 :     }
     539             : 
     540             :     CPL_DISALLOW_COPY_ASSIGN(GDALVectorCombineOutputLayer)
     541             : 
     542             :   private:
     543          10 :     void AddM(int iGeomField)
     544             :     {
     545             :         OGRGeomFieldDefn *poGeomFieldDefn =
     546          10 :             m_defn->GetGeomFieldDefn(iGeomField);
     547          20 :         whileUnsealing(poGeomFieldDefn)
     548          10 :             ->SetType(OGR_GT_SetM(poGeomFieldDefn->GetType()));
     549             : 
     550          20 :         for (auto &[_, poFeature] : m_features)
     551             :         {
     552          10 :             poFeature->GetGeomFieldRef(iGeomField)->setMeasured(true);
     553             :         }
     554          10 :     }
     555             : 
     556           4 :     void AddZ(int iGeomField)
     557             :     {
     558             :         OGRGeomFieldDefn *poGeomFieldDefn =
     559           4 :             m_defn->GetGeomFieldDefn(iGeomField);
     560           8 :         whileUnsealing(poGeomFieldDefn)
     561           4 :             ->SetType(OGR_GT_SetZ(poGeomFieldDefn->GetType()));
     562             : 
     563           8 :         for (auto &[_, poFeature] : m_features)
     564             :         {
     565           4 :             poFeature->GetGeomFieldRef(iGeomField)->set3D(true);
     566             :         }
     567           4 :     }
     568             : 
     569           2 :     void SetTypeGeometryCollection(int iGeomField)
     570             :     {
     571             :         OGRGeomFieldDefn *poGeomFieldDefn =
     572           2 :             m_defn->GetGeomFieldDefn(iGeomField);
     573           2 :         const bool hasZ = CPL_TO_BOOL(OGR_GT_HasZ(poGeomFieldDefn->GetType()));
     574           2 :         const bool hasM = CPL_TO_BOOL(OGR_GT_HasM(poGeomFieldDefn->GetType()));
     575             : 
     576           4 :         whileUnsealing(poGeomFieldDefn)
     577           2 :             ->SetType(OGR_GT_SetModifier(wkbGeometryCollection, hasZ, hasM));
     578             : 
     579           4 :         for (auto &[_, poFeature] : m_features)
     580             :         {
     581             :             std::unique_ptr<OGRGeometry> poTmpGeom(
     582           2 :                 poFeature->StealGeometry(iGeomField));
     583           4 :             poTmpGeom = OGRGeometryFactory::forceTo(std::move(poTmpGeom),
     584           2 :                                                     poGeomFieldDefn->GetType());
     585           2 :             CPLAssert(poTmpGeom);
     586           2 :             poFeature->SetGeomField(iGeomField, std::move(poTmpGeom));
     587             :         }
     588           2 :     }
     589             : 
     590             :     const std::vector<std::string> m_groupBy{};
     591             :     std::vector<int> m_srcGroupByFieldIndices{};
     592             :     std::vector<int> m_srcExtraFieldIndices{};
     593             :     std::map<std::vector<std::string>, std::unique_ptr<OGRFeature>>
     594             :         m_features{};
     595             :     std::optional<decltype(m_features)::const_iterator> m_itFeature{};
     596             :     const OGRFeatureDefnRefCountedPtr m_defn;
     597             :     GIntBig m_nProcessedFeaturesRead = 0;
     598             :     const bool m_keepNested;
     599             : };
     600             : }  // namespace
     601             : 
     602          22 : bool GDALVectorCombineAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
     603             : {
     604          22 :     auto poSrcDS = m_inputDataset[0].GetDatasetRef();
     605             :     auto poDstDS =
     606          44 :         std::make_unique<GDALVectorNonStreamingAlgorithmDataset>(*poSrcDS);
     607             : 
     608          44 :     GDALVectorAlgorithmLayerProgressHelper progressHelper(ctxt);
     609             : 
     610          43 :     for (auto &&poSrcLayer : poSrcDS->GetLayers())
     611             :     {
     612          22 :         if (m_inputLayerNames.empty() ||
     613           0 :             std::find(m_inputLayerNames.begin(), m_inputLayerNames.end(),
     614          22 :                       poSrcLayer->GetDescription()) != m_inputLayerNames.end())
     615             :         {
     616          22 :             const auto poSrcLayerDefn = poSrcLayer->GetLayerDefn();
     617          22 :             if (poSrcLayerDefn->GetGeomFieldCount() == 0)
     618             :             {
     619           0 :                 if (m_inputLayerNames.empty())
     620           0 :                     continue;
     621           0 :                 ReportError(CE_Failure, CPLE_AppDefined,
     622             :                             "Specified layer '%s' has no geometry field",
     623           0 :                             poSrcLayer->GetDescription());
     624           0 :                 return false;
     625             :             }
     626             : 
     627             :             // Check that all attributes exist
     628          30 :             for (const auto &fieldName : m_groupBy)
     629             :             {
     630             :                 const int iSrcFieldIndex =
     631           9 :                     poSrcLayerDefn->GetFieldIndex(fieldName.c_str());
     632           9 :                 if (iSrcFieldIndex == -1)
     633             :                 {
     634           1 :                     ReportError(CE_Failure, CPLE_AppDefined,
     635             :                                 "Specified attribute field '%s' does not exist "
     636             :                                 "in layer '%s'",
     637             :                                 fieldName.c_str(),
     638           1 :                                 poSrcLayer->GetDescription());
     639           1 :                     return false;
     640             :                 }
     641             :             }
     642             : 
     643          21 :             progressHelper.AddProcessedLayer(*poSrcLayer);
     644             :         }
     645             :     }
     646             : 
     647          42 :     for ([[maybe_unused]] auto [poSrcLayer, bProcessed, layerProgressFunc,
     648          84 :                                 layerProgressData] : progressHelper)
     649             :     {
     650             :         auto poLayer = std::make_unique<GDALVectorCombineOutputLayer>(
     651           0 :             *poSrcDS, *poSrcLayer, -1, m_groupBy, m_keepNested,
     652          21 :             m_addExtraFields);
     653             : 
     654          21 :         if (!poDstDS->AddProcessedLayer(std::move(poLayer), layerProgressFunc,
     655             :                                         layerProgressData.get()))
     656             :         {
     657           0 :             return false;
     658             :         }
     659             :     }
     660             : 
     661          21 :     m_outputDataset.Set(std::move(poDstDS));
     662             : 
     663          21 :     return true;
     664             : }
     665             : 
     666             : GDALVectorCombineAlgorithmStandalone::~GDALVectorCombineAlgorithmStandalone() =
     667             :     default;
     668             : 
     669             : //! @endcond

Generated by: LCOV version 1.14