LCOV - code coverage report
Current view: top level - apps - gdalalg_vector_filter.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 253 297 85.2 %
Date: 2026-08-28 21:47:33 Functions: 11 12 91.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  "filter" step of "vector pipeline"
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdalalg_vector_filter.h"
      14             : 
      15             : #include "gdal_priv.h"
      16             : #include "ogrsf_frmts.h"
      17             : #include "ogr_p.h"
      18             : 
      19             : #include <set>
      20             : 
      21             : //! @cond Doxygen_Suppress
      22             : 
      23             : #ifndef _
      24             : #define _(x) (x)
      25             : #endif
      26             : 
      27             : /************************************************************************/
      28             : /*        GDALVectorFilterAlgorithm::GDALVectorFilterAlgorithm()        */
      29             : /************************************************************************/
      30             : 
      31         127 : GDALVectorFilterAlgorithm::GDALVectorFilterAlgorithm(bool standaloneStep)
      32             :     : GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
      33         127 :                                       standaloneStep)
      34             : {
      35         127 :     auto &layerArg = AddActiveLayerArg(&m_activeLayer);
      36         127 :     AddBBOXArg(&m_bbox).SetMutualExclusionGroup("bbox-geometry");
      37         254 :     AddArg("bbox-crs", 0, _("CRS of bounding box filter"), &m_bboxCrs)
      38         254 :         .SetIsCRSArg()
      39         127 :         .AddHiddenAlias("bbox_srs");
      40         254 :     AddArg("geometry", 0, _("Filter geometry (WKT or GeoJSON)"), &m_geometry)
      41         127 :         .SetMutualExclusionGroup("bbox-geometry");
      42         254 :     AddArg("geometry-crs", 0, _("CRS of filter geometry"), &m_geometryCrs)
      43         254 :         .SetIsCRSArg()
      44         127 :         .AddHiddenAlias("geometry_srs");
      45             :     AddArg("where", 0,
      46             :            _("Attribute query in a restricted form of the queries used in the "
      47             :              "SQL WHERE statement"),
      48         254 :            &m_where)
      49         127 :         .SetReadFromFileAtSyntaxAllowed()
      50         254 :         .SetMetaVar("<WHERE>|@<filename>")
      51         127 :         .SetRemoveSQLCommentsEnabled()
      52             :         .SetAutoCompleteFunction(
      53          11 :             [this, &layerArg](const std::string &currentValue)
      54         138 :             { return CompleteWhere(layerArg, currentValue); });
      55             :     AddArg("update-extent", 0,
      56             :            _("Update layer extent to take into account the filter"),
      57         127 :            &m_updateExtent);
      58         127 : }
      59             : 
      60             : /************************************************************************/
      61             : /*              GDALVectorFilterAlgorithmLayerChangeExtent              */
      62             : /************************************************************************/
      63             : 
      64             : constexpr const char *const SQL_OPERATORS[] = {
      65             :     "=", "<>", "<", "<=", ">", ">=", "AND", "OR", "LIKE", "BETWEEN"};
      66             : 
      67           7 : static bool IsSQLOperator(const char *pszStr)
      68             : {
      69           7 :     return std::find_if(std::begin(SQL_OPERATORS), std::end(SQL_OPERATORS),
      70          31 :                         [pszStr](const char *pszStr2)
      71          31 :                         { return EQUAL(pszStr, pszStr2); }) !=
      72           7 :            std::end(SQL_OPERATORS);
      73             : }
      74             : 
      75          24 : static std::string GetSQLIdentifier(const std::string &name)
      76             : {
      77          24 :     if (name.find_first_of("'\" ") != std::string::npos)
      78             :     {
      79           0 :         char *pszEscaped = CPLEscapeString(name.c_str(), -1, CPLES_SQLI);
      80           0 :         std::string ret = std::string("\"").append(pszEscaped).append("\"");
      81           0 :         CPLFree(pszEscaped);
      82           0 :         return ret;
      83             :     }
      84             :     else
      85             :     {
      86          24 :         return name;
      87             :     }
      88             : }
      89             : 
      90          30 : static std::string GetSQLStringLiteral(const char *val)
      91             : {
      92          30 :     char *pszEscaped = CPLEscapeString(val, -1, CPLES_SQL);
      93          60 :     std::string ret = std::string("'").append(pszEscaped).append("'");
      94          30 :     CPLFree(pszEscaped);
      95          30 :     return ret;
      96             : }
      97             : 
      98             : std::vector<std::string>
      99          11 : GDALVectorFilterAlgorithm::CompleteWhere(const GDALAlgorithmArg &layerArg,
     100             :                                          const std::string &currentValue) const
     101             : {
     102          11 :     std::vector<std::string> ret;
     103          22 :     if (currentValue.empty() || currentValue[0] != '"' ||
     104          11 :         m_inputDataset.empty())
     105           0 :         return ret;
     106             : 
     107             :     auto poDS = std::unique_ptr<GDALDataset>(
     108          11 :         GDALDataset::Open(m_inputDataset[0].GetName().c_str(),
     109          22 :                           GDAL_OF_VECTOR | GDAL_OF_READONLY));
     110          11 :     if (!poDS)
     111           0 :         return ret;
     112             : 
     113             :     // Collect field names
     114          22 :     std::string layerName;
     115          11 :     if (layerArg.IsExplicitlySet())
     116           2 :         layerName = layerArg.Get<std::string>();
     117          22 :     std::map<std::string, std::vector<OGRLayer *>> fieldNames;
     118          11 :     if (layerName.empty())
     119             :     {
     120          18 :         for (auto *poLayer : poDS->GetLayers())
     121             :         {
     122          37 :             for (const auto *poFieldDefn : poLayer->GetLayerDefn()->GetFields())
     123             :             {
     124          28 :                 fieldNames[poFieldDefn->GetNameRef()].push_back(poLayer);
     125             :             }
     126             :         }
     127             :     }
     128           2 :     else if (auto *poLayer = poDS->GetLayerByName(layerName.c_str()))
     129             :     {
     130           5 :         for (const auto *poFieldDefn : poLayer->GetLayerDefn()->GetFields())
     131             :         {
     132           4 :             fieldNames[poFieldDefn->GetNameRef()].push_back(poLayer);
     133             :         }
     134             :     }
     135          11 :     if (fieldNames.empty())
     136           1 :         return ret;
     137             : 
     138             :     const CPLStringList aosTokens(CSLTokenizeString2(
     139          10 :         currentValue.c_str() + 1, " ",
     140             :         CSLT_HONOURSTRINGS | CSLT_HONOURSINGLEQUOTES | CSLT_PRESERVEQUOTES |
     141          20 :             CSLT_PRESERVEESCAPES | CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES));
     142             : 
     143          20 :     std::string prefix;
     144          10 :     const int nTokens = aosTokens.size();
     145          10 :     if (nTokens > 0 && cpl::contains(fieldNames, aosTokens[nTokens - 1]))
     146             :     {
     147           1 :         prefix = currentValue.substr(1);
     148           1 :         if (!prefix.empty() && prefix.back() != ' ')
     149           0 :             prefix += ' ';
     150          11 :         for (const char *op : SQL_OPERATORS)
     151             :         {
     152          10 :             if (nTokens > 1 && strcmp(op, "AND") == 0)
     153           0 :                 break;
     154          10 :             ret.push_back(prefix + op);
     155             :         }
     156             :     }
     157             :     else
     158             :     {
     159             :         const bool bLastTokenIsSQLOperator =
     160           9 :             nTokens > 0 && IsSQLOperator(aosTokens[nTokens - 1]);
     161           9 :         const int nCompleteTokens =
     162           9 :             nTokens + (bLastTokenIsSQLOperator ? 0 : -1);
     163          23 :         for (int i = 0; i < nCompleteTokens; ++i)
     164             :         {
     165          14 :             if (!prefix.empty())
     166           8 :                 prefix += ' ';
     167          14 :             prefix += aosTokens[i];
     168             :         }
     169           9 :         if (!prefix.empty())
     170           6 :             prefix += ' ';
     171             : 
     172           9 :         const char *pszLastFieldName = nullptr;
     173           9 :         OGRLayer *poLastFieldLayer = nullptr;
     174           9 :         OGRFieldType eLastFieldType = OFTString;
     175           9 :         if (nCompleteTokens >= 2)
     176             :         {
     177           6 :             const char *pszFieldName = aosTokens[nCompleteTokens - 2];
     178           6 :             const auto oIter = fieldNames.find(pszFieldName);
     179           6 :             if (oIter != fieldNames.end() && oIter->second.size() == 1)
     180             :             {
     181             :                 const int nIdx =
     182           5 :                     oIter->second[0]->GetLayerDefn()->GetFieldIndex(
     183           5 :                         pszFieldName);
     184           5 :                 if (nIdx >= 0)
     185             :                 {
     186           5 :                     pszLastFieldName = pszFieldName;
     187           5 :                     poLastFieldLayer = oIter->second[0];
     188           5 :                     eLastFieldType = poLastFieldLayer->GetLayerDefn()
     189           5 :                                          ->GetFieldDefn(nIdx)
     190           5 :                                          ->GetType();
     191             :                 }
     192             :             }
     193             :         }
     194             : 
     195          38 :         for (const auto &[name, layers] : fieldNames)
     196             :         {
     197          29 :             bool canAdd = false;
     198          29 :             if (!pszLastFieldName)
     199             :             {
     200          12 :                 canAdd = true;
     201             :             }
     202          17 :             else if (name != pszLastFieldName)
     203             :             {
     204          12 :                 if (layers.size() > 1)
     205             :                 {
     206           0 :                     canAdd = true;
     207             :                 }
     208          12 :                 else if (layers[0]
     209          12 :                              ->GetLayerDefn()
     210             :                              ->GetFieldDefn(
     211          12 :                                  layers[0]->GetLayerDefn()->GetFieldIndex(
     212          12 :                                      name.c_str()))
     213          12 :                              ->GetType() == eLastFieldType)
     214             :                 {
     215           2 :                     canAdd = true;
     216             :                 }
     217             :             }
     218          29 :             if (canAdd)
     219             :             {
     220          14 :                 ret.push_back(prefix + GetSQLIdentifier(name));
     221             :             }
     222             :         }
     223             : 
     224           9 :         if (pszLastFieldName && poLastFieldLayer)
     225             :         {
     226           5 :             auto poLayer = poLastFieldLayer;
     227           5 :             constexpr int NOT_TOO_LARGE = 1000;
     228           5 :             const auto nFeatureCount = poLayer->GetFeatureCount();
     229           5 :             if (nFeatureCount > 0 && nFeatureCount < NOT_TOO_LARGE)
     230             :             {
     231           5 :                 constexpr int VALUES_COUNT = 10;
     232             :                 const std::string osSQLField =
     233          15 :                     GetSQLIdentifier(pszLastFieldName);
     234             :                 const std::string osSQLLayer =
     235          15 :                     GetSQLIdentifier(poLayer->GetName());
     236           5 :                 if (eLastFieldType == OFTString)
     237             :                 {
     238           6 :                     CPLString osSQL;
     239           3 :                     const char *pszDialect = nullptr;
     240           3 :                     if (GetGDALDriverManager()->GetDriverByName("SQLite"))
     241             :                     {
     242           3 :                         pszDialect = "SQLite";
     243             :                         // Find 10 most frequent strings
     244             :                         osSQL.Printf("SELECT %s, COUNT(%s) cnt FROM %s GROUP "
     245             :                                      "BY %s ORDER BY cnt DESC, %s ASC LIMIT %d",
     246             :                                      osSQLField.c_str(), osSQLField.c_str(),
     247             :                                      osSQLLayer.c_str(), osSQLField.c_str(),
     248           3 :                                      osSQLField.c_str(), VALUES_COUNT + 1);
     249             :                     }
     250             :                     else
     251             :                     {
     252             :                         osSQL.Printf("SELECT DISTINCT %s FROM %s LIMIT %d",
     253             :                                      osSQLField.c_str(), osSQLLayer.c_str(),
     254           0 :                                      VALUES_COUNT + 1);
     255             :                     }
     256             :                     auto poSQLLayer =
     257           3 :                         poDS->ExecuteSQL(osSQL.c_str(), nullptr, pszDialect);
     258           3 :                     if (poSQLLayer)
     259             :                     {
     260           3 :                         int nCount = 0;
     261          34 :                         for (auto &&poFeature : poSQLLayer)
     262             :                         {
     263          31 :                             if (nCount == VALUES_COUNT)
     264             :                             {
     265           1 :                                 ret.push_back(prefix + "'...other values...");
     266           1 :                                 break;
     267             :                             }
     268          30 :                             ret.push_back(prefix +
     269          60 :                                           GetSQLStringLiteral(
     270             :                                               poFeature->GetFieldAsString(0)));
     271          30 :                             nCount++;
     272             :                         }
     273           3 :                         poDS->ReleaseResultSet(poSQLLayer);
     274             :                     }
     275             :                 }
     276           2 :                 else if (eLastFieldType == OFTInteger ||
     277           0 :                          eLastFieldType == OFTInteger64 ||
     278             :                          eLastFieldType == OFTReal)
     279             :                 {
     280           4 :                     CPLString osSQL;
     281           2 :                     const char *pszDialect = nullptr;
     282           3 :                     if (nFeatureCount > VALUES_COUNT + 2 &&
     283           1 :                         GetGDALDriverManager()->GetDriverByName("SQLite"))
     284             :                     {
     285           1 :                         pszDialect = "SQLite";
     286             :                         // Collect lowest and highest values
     287             :                         osSQL.Printf("SELECT DISTINCT %s FROM ("
     288             :                                      "SELECT * FROM (SELECT DISTINCT %s FROM "
     289             :                                      "%s ORDER BY %s ASC LIMIT %d) UNION ALL "
     290             :                                      "SELECT * FROM (SELECT DISTINCT %s FROM "
     291             :                                      "%s ORDER BY %s DESC LIMIT %d)"
     292             :                                      ") x ORDER BY %s",
     293             :                                      osSQLField.c_str(), osSQLField.c_str(),
     294             :                                      osSQLLayer.c_str(), osSQLField.c_str(),
     295             :                                      VALUES_COUNT / 2, osSQLField.c_str(),
     296             :                                      osSQLLayer.c_str(), osSQLField.c_str(),
     297           1 :                                      VALUES_COUNT / 2 + 1, osSQLField.c_str());
     298             :                     }
     299             :                     else
     300             :                     {
     301             :                         osSQL.Printf("SELECT DISTINCT %s FROM %s LIMIT %d",
     302             :                                      osSQLField.c_str(), osSQLLayer.c_str(),
     303           1 :                                      VALUES_COUNT + 1);
     304             :                     }
     305             :                     auto poSQLLayer =
     306           2 :                         poDS->ExecuteSQL(osSQL.c_str(), nullptr, pszDialect);
     307           2 :                     if (poSQLLayer)
     308             :                     {
     309           2 :                         int nCount = 0;
     310          23 :                         for (auto &&poFeature : poSQLLayer)
     311             :                         {
     312          21 :                             if (nCount == VALUES_COUNT)
     313             :                             {
     314           1 :                                 if (pszDialect)
     315             :                                 {
     316           1 :                                     ret.erase(ret.begin() + VALUES_COUNT / 2 +
     317           1 :                                               1);
     318           1 :                                     ret.push_back(
     319           2 :                                         prefix +
     320             :                                         poFeature->GetFieldAsString(0));
     321             :                                 }
     322           1 :                                 ret.push_back(prefix + "...other values...");
     323           1 :                                 break;
     324             :                             }
     325          20 :                             ret.push_back(prefix +
     326             :                                           poFeature->GetFieldAsString(0));
     327          20 :                             nCount++;
     328             :                         }
     329           2 :                         poDS->ReleaseResultSet(poSQLLayer);
     330             :                     }
     331             :                 }
     332             :             }
     333             :         }
     334             :     }
     335          10 :     return ret;
     336             : }
     337             : 
     338             : /************************************************************************/
     339             : /*              GDALVectorFilterAlgorithmLayerChangeExtent              */
     340             : /************************************************************************/
     341             : 
     342             : namespace
     343             : {
     344             : class GDALVectorFilterAlgorithmLayerChangeExtent final
     345             :     : public GDALVectorPipelinePassthroughLayer
     346             : {
     347             :   public:
     348           1 :     GDALVectorFilterAlgorithmLayerChangeExtent(
     349             :         OGRLayer &oSrcLayer, const OGREnvelope3D &sLayerEnvelope)
     350           1 :         : GDALVectorPipelinePassthroughLayer(oSrcLayer),
     351           1 :           m_sLayerEnvelope(sLayerEnvelope)
     352             :     {
     353           1 :     }
     354             : 
     355           1 :     OGRErr IGetExtent(int /*iGeomField*/, OGREnvelope *psExtent,
     356             :                       bool /* bForce */) override
     357             :     {
     358           1 :         if (m_sLayerEnvelope.IsInit())
     359             :         {
     360           1 :             *psExtent = m_sLayerEnvelope;
     361           1 :             return OGRERR_NONE;
     362             :         }
     363             :         else
     364             :         {
     365           0 :             return OGRERR_FAILURE;
     366             :         }
     367             :     }
     368             : 
     369           1 :     OGRErr IGetExtent3D(int /*iGeomField*/, OGREnvelope3D *psExtent,
     370             :                         bool /* bForce */) override
     371             :     {
     372           1 :         if (m_sLayerEnvelope.IsInit())
     373             :         {
     374           1 :             *psExtent = m_sLayerEnvelope;
     375           1 :             return OGRERR_NONE;
     376             :         }
     377             :         else
     378             :         {
     379           0 :             return OGRERR_FAILURE;
     380             :         }
     381             :     }
     382             : 
     383           0 :     int TestCapability(const char *pszCap) const override
     384             :     {
     385           0 :         if (EQUAL(pszCap, OLCFastGetExtent))
     386           0 :             return true;
     387           0 :         return m_srcLayer.TestCapability(pszCap);
     388             :     }
     389             : 
     390             :   private:
     391             :     const OGREnvelope3D m_sLayerEnvelope;
     392             : };
     393             : 
     394             : }  // namespace
     395             : 
     396             : /************************************************************************/
     397             : /*                 GDALVectorFilterAlgorithm::RunStep()                 */
     398             : /************************************************************************/
     399             : 
     400          30 : bool GDALVectorFilterAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
     401             : {
     402          30 :     auto poSrcDS = m_inputDataset[0].GetDatasetRef();
     403          30 :     CPLAssert(poSrcDS);
     404             : 
     405          30 :     CPLAssert(m_outputDataset.GetName().empty());
     406          30 :     CPLAssert(!m_outputDataset.GetDatasetRef());
     407             : 
     408          30 :     const int nLayerCount = poSrcDS->GetLayerCount();
     409             : 
     410          60 :     OGRSpatialReference oBBOX_SRS;
     411          30 :     if (!m_bboxCrs.empty())
     412             :     {
     413           2 :         oBBOX_SRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     414             :         // Already validated
     415           2 :         CPL_IGNORE_RET_VAL(oBBOX_SRS.SetFromUserInput(m_bboxCrs.c_str()));
     416             :     }
     417             : 
     418          30 :     bool ret = true;
     419          30 :     if (m_bbox.size() == 4)
     420             :     {
     421           6 :         const double xmin = m_bbox[0];
     422           6 :         const double ymin = m_bbox[1];
     423           6 :         const double xmax = m_bbox[2];
     424           6 :         const double ymax = m_bbox[3];
     425          11 :         for (int i = 0; i < nLayerCount; ++i)
     426             :         {
     427           6 :             auto poSrcLayer = poSrcDS->GetLayer(i);
     428           6 :             ret = ret && (poSrcLayer != nullptr);
     429           6 :             if (poSrcLayer && (m_activeLayer.empty() ||
     430           0 :                                m_activeLayer == poSrcLayer->GetDescription()))
     431             :             {
     432           6 :                 const auto poLayerSRS = poSrcLayer->GetSpatialRef();
     433           6 :                 if (poLayerSRS && !oBBOX_SRS.IsEmpty())
     434             :                 {
     435             :                     auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
     436             :                         OGRCreateCoordinateTransformation(&oBBOX_SRS,
     437           2 :                                                           poLayerSRS));
     438           2 :                     if (!poCT)
     439           0 :                         return false;
     440             :                     double xMinLayerSRS;
     441             :                     double yMinLayerSRS;
     442             :                     double xMaxLayerSRS;
     443             :                     double yMaxLayerSRS;
     444           4 :                     if (!poCT->TransformBounds(
     445             :                             xmin, ymin, xmax, ymax, &xMinLayerSRS,
     446           2 :                             &yMinLayerSRS, &xMaxLayerSRS, &yMaxLayerSRS, 21))
     447             :                     {
     448           1 :                         ReportError(CE_Failure, CPLE_AppDefined,
     449             :                                     "Bounding box reprojection failed");
     450           1 :                         return false;
     451             :                     }
     452           1 :                     poSrcLayer->SetSpatialFilterRect(
     453             :                         xMinLayerSRS, yMinLayerSRS, xMaxLayerSRS, yMaxLayerSRS);
     454             :                 }
     455             :                 else
     456             :                 {
     457           4 :                     poSrcLayer->SetSpatialFilterRect(xmin, ymin, xmax, ymax);
     458             :                 }
     459             :             }
     460             :         }
     461             :     }
     462          24 :     else if (!m_geometry.empty())
     463             :     {
     464           0 :         std::unique_ptr<OGRGeometry> poGeom;
     465             :         {
     466           8 :             CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
     467           4 :             auto [poGeomTmp, eErr] =
     468           8 :                 OGRGeometryFactory::createFromWkt(m_geometry.c_str());
     469           4 :             if (eErr == OGRERR_NONE)
     470             :             {
     471           2 :                 poGeom = std::move(poGeomTmp);
     472             :             }
     473             :             else
     474             :             {
     475           2 :                 poGeom.reset(
     476             :                     OGRGeometryFactory::createFromGeoJson(m_geometry.c_str()));
     477           2 :                 if (poGeom && poGeom->getSpatialReference() == nullptr)
     478             :                 {
     479             :                     auto poSRS =
     480           0 :                         OGRSpatialReferenceRefCountedPtr::makeInstance();
     481           0 :                     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     482           0 :                     CPL_IGNORE_RET_VAL(poSRS->SetFromUserInput("WGS84"));
     483           0 :                     poGeom->assignSpatialReference(poSRS.get());
     484             :                 }
     485             :             }
     486             :         }
     487           4 :         if (!poGeom)
     488             :         {
     489           1 :             CPLError(
     490             :                 CE_Failure, CPLE_IllegalArg,
     491             :                 "Filter geometry is neither a valid WKT or GeoJSON geometry");
     492           1 :             return false;
     493             :         }
     494             : 
     495           3 :         if (!m_geometryCrs.empty())
     496             :         {
     497           2 :             auto poSRS = OGRSpatialReferenceRefCountedPtr::makeInstance();
     498           1 :             poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     499             :             // Validity of CRS already checked by GDALAlgorithm
     500           1 :             CPL_IGNORE_RET_VAL(poSRS->SetFromUserInput(m_geometryCrs.c_str()));
     501           1 :             poGeom->assignSpatialReference(poSRS.get());
     502             :         }
     503             : 
     504           6 :         for (int i = 0; i < nLayerCount; ++i)
     505             :         {
     506           3 :             auto poSrcLayer = poSrcDS->GetLayer(i);
     507           3 :             ret = ret && (poSrcLayer != nullptr);
     508           3 :             if (poSrcLayer && (m_activeLayer.empty() ||
     509           0 :                                m_activeLayer == poSrcLayer->GetDescription()))
     510             :             {
     511           3 :                 const auto poLayerSRS = poSrcLayer->GetSpatialRef();
     512           5 :                 if (poLayerSRS && poGeom->getSpatialReference() &&
     513           2 :                     !poLayerSRS->IsSame(poGeom->getSpatialReference()))
     514             :                 {
     515           2 :                     std::unique_ptr<OGRGeometry> poGeomClone(poGeom->clone());
     516           2 :                     if (poGeomClone->transformTo(poLayerSRS) == OGRERR_FAILURE)
     517             :                     {
     518           0 :                         ReportError(CE_Failure, CPLE_AppDefined,
     519             :                                     "Geometry reprojection failed");
     520           0 :                         return false;
     521             :                     }
     522           2 :                     poSrcLayer->SetSpatialFilter(poGeomClone.get());
     523             :                 }
     524             :                 else
     525             :                 {
     526           1 :                     poSrcLayer->SetSpatialFilter(poGeom.get());
     527             :                 }
     528             :             }
     529             :         }
     530             :     }
     531             : 
     532          28 :     if (ret && !m_where.empty())
     533             :     {
     534          40 :         for (int i = 0; i < nLayerCount; ++i)
     535             :         {
     536          22 :             auto poSrcLayer = poSrcDS->GetLayer(i);
     537          22 :             ret = ret && (poSrcLayer != nullptr);
     538          28 :             if (ret && (m_activeLayer.empty() ||
     539           6 :                         m_activeLayer == poSrcLayer->GetDescription()))
     540             :             {
     541          19 :                 ret = poSrcLayer->SetAttributeFilter(m_where.c_str()) ==
     542             :                       OGRERR_NONE;
     543             :             }
     544             :         }
     545             :     }
     546             : 
     547          28 :     if (ret)
     548             :     {
     549             :         auto outDS =
     550          27 :             std::make_unique<GDALVectorPipelineOutputDataset>(*poSrcDS);
     551             : 
     552          27 :         int64_t nTotalFeatures = 0;
     553          27 :         if (m_updateExtent && ctxt.m_pfnProgress)
     554             :         {
     555           0 :             for (int i = 0; ret && i < nLayerCount; ++i)
     556             :             {
     557           0 :                 auto poSrcLayer = poSrcDS->GetLayer(i);
     558           0 :                 ret = (poSrcLayer != nullptr);
     559           0 :                 if (ret)
     560             :                 {
     561           0 :                     if (m_activeLayer.empty() ||
     562           0 :                         m_activeLayer == poSrcLayer->GetDescription())
     563             :                     {
     564           0 :                         if (poSrcLayer->TestCapability(OLCFastFeatureCount))
     565             :                         {
     566           0 :                             const auto nFC = poSrcLayer->GetFeatureCount(false);
     567           0 :                             if (nFC < 0)
     568             :                             {
     569           0 :                                 nTotalFeatures = 0;
     570           0 :                                 break;
     571             :                             }
     572           0 :                             nTotalFeatures += nFC;
     573             :                         }
     574             :                     }
     575             :                 }
     576             :             }
     577             :         }
     578             : 
     579          27 :         int64_t nFeatureCounter = 0;
     580          58 :         for (int i = 0; ret && i < nLayerCount; ++i)
     581             :         {
     582          31 :             auto poSrcLayer = poSrcDS->GetLayer(i);
     583          31 :             ret = (poSrcLayer != nullptr);
     584          31 :             if (ret)
     585             :             {
     586          35 :                 if (m_updateExtent &&
     587           4 :                     (m_activeLayer.empty() ||
     588           2 :                      m_activeLayer == poSrcLayer->GetDescription()))
     589             :                 {
     590           1 :                     OGREnvelope3D sLayerEnvelope, sFeatureEnvelope;
     591           2 :                     for (auto &&poFeature : poSrcLayer)
     592             :                     {
     593           1 :                         const auto poGeom = poFeature->GetGeometryRef();
     594           1 :                         if (poGeom && !poGeom->IsEmpty())
     595             :                         {
     596           1 :                             poGeom->getEnvelope(&sFeatureEnvelope);
     597           1 :                             sLayerEnvelope.Merge(sFeatureEnvelope);
     598             :                         }
     599             : 
     600           1 :                         ++nFeatureCounter;
     601           1 :                         if (nTotalFeatures > 0 && ctxt.m_pfnProgress &&
     602           0 :                             !ctxt.m_pfnProgress(
     603           0 :                                 static_cast<double>(nFeatureCounter) /
     604           0 :                                     static_cast<double>(nTotalFeatures),
     605             :                                 "", ctxt.m_pProgressData))
     606             :                         {
     607           0 :                             ReportError(CE_Failure, CPLE_UserInterrupt,
     608             :                                         "Interrupted by user");
     609           0 :                             return false;
     610             :                         }
     611             :                     }
     612           2 :                     outDS->AddLayer(
     613             :                         *poSrcLayer,
     614             :                         std::make_unique<
     615           2 :                             GDALVectorFilterAlgorithmLayerChangeExtent>(
     616             :                             *poSrcLayer, sLayerEnvelope));
     617             :                 }
     618             :                 else
     619             :                 {
     620          60 :                     outDS->AddLayer(
     621             :                         *poSrcLayer,
     622          60 :                         std::make_unique<GDALVectorPipelinePassthroughLayer>(
     623             :                             *poSrcLayer));
     624             :                 }
     625             :             }
     626             :         }
     627             : 
     628          27 :         if (ret)
     629          27 :             m_outputDataset.Set(std::move(outDS));
     630             :     }
     631             : 
     632          28 :     return ret;
     633             : }
     634             : 
     635             : GDALVectorFilterAlgorithmStandalone::~GDALVectorFilterAlgorithmStandalone() =
     636             :     default;
     637             : 
     638             : //! @endcond

Generated by: LCOV version 1.14