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

Generated by: LCOV version 1.14