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