Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: gdal "vector pipeline" subcommand
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_pipeline.h"
14 : #include "gdalalg_materialize.h"
15 : #include "gdalalg_vector_read.h"
16 : #include "gdalalg_vector_buffer.h"
17 : #include "gdalalg_vector_check_coverage.h"
18 : #include "gdalalg_vector_check_geometry.h"
19 : #include "gdalalg_vector_clean_coverage.h"
20 : #include "gdalalg_vector_clip.h"
21 : #include "gdalalg_vector_concat.h"
22 : #include "gdalalg_vector_edit.h"
23 : #include "gdalalg_vector_explode_collections.h"
24 : #include "gdalalg_vector_filter.h"
25 : #include "gdalalg_vector_geom.h"
26 : #include "gdalalg_vector_info.h"
27 : #include "gdalalg_vector_make_valid.h"
28 : #include "gdalalg_vector_partition.h"
29 : #include "gdalalg_vector_reproject.h"
30 : #include "gdalalg_vector_segmentize.h"
31 : #include "gdalalg_vector_select.h"
32 : #include "gdalalg_vector_set_geom_type.h"
33 : #include "gdalalg_vector_simplify.h"
34 : #include "gdalalg_vector_simplify_coverage.h"
35 : #include "gdalalg_vector_sql.h"
36 : #include "gdalalg_vector_swap_xy.h"
37 : #include "gdalalg_vector_write.h"
38 : #include "gdalalg_tee.h"
39 :
40 : #include "../frmts/mem/memdataset.h"
41 :
42 : #include "cpl_conv.h"
43 : #include "cpl_string.h"
44 :
45 : #include <algorithm>
46 : #include <cassert>
47 :
48 : //! @cond Doxygen_Suppress
49 :
50 : #ifndef _
51 : #define _(x) (x)
52 : #endif
53 :
54 : GDALVectorAlgorithmStepRegistry::~GDALVectorAlgorithmStepRegistry() = default;
55 :
56 : /************************************************************************/
57 : /* GDALVectorPipelineStepAlgorithm::GDALVectorPipelineStepAlgorithm() */
58 : /************************************************************************/
59 :
60 1767 : GDALVectorPipelineStepAlgorithm::GDALVectorPipelineStepAlgorithm(
61 : const std::string &name, const std::string &description,
62 1767 : const std::string &helpURL, bool standaloneStep)
63 : : GDALVectorPipelineStepAlgorithm(
64 : name, description, helpURL,
65 1767 : ConstructorOptions().SetStandaloneStep(standaloneStep))
66 : {
67 1767 : }
68 :
69 : /************************************************************************/
70 : /* GDALVectorPipelineStepAlgorithm::GDALVectorPipelineStepAlgorithm() */
71 : /************************************************************************/
72 :
73 2224 : GDALVectorPipelineStepAlgorithm::GDALVectorPipelineStepAlgorithm(
74 : const std::string &name, const std::string &description,
75 2224 : const std::string &helpURL, const ConstructorOptions &options)
76 2224 : : GDALPipelineStepAlgorithm(name, description, helpURL, options)
77 : {
78 2224 : if (m_standaloneStep)
79 : {
80 376 : m_supportsStreamedOutput = true;
81 :
82 376 : if (m_constructorOptions.addDefaultArguments)
83 : {
84 275 : AddVectorInputArgs(false);
85 275 : AddProgressArg();
86 275 : AddVectorOutputArgs(false, false);
87 : }
88 : }
89 2224 : }
90 :
91 : GDALVectorPipelineStepAlgorithm::~GDALVectorPipelineStepAlgorithm() = default;
92 :
93 : /************************************************************************/
94 : /* GDALVectorPipelineAlgorithm::GDALVectorPipelineAlgorithm() */
95 : /************************************************************************/
96 :
97 94 : GDALVectorPipelineAlgorithm::GDALVectorPipelineAlgorithm()
98 : : GDALAbstractPipelineAlgorithm(
99 : NAME, DESCRIPTION, HELP_URL,
100 94 : ConstructorOptions().SetInputDatasetMaxCount(INT_MAX))
101 : {
102 94 : m_supportsStreamedOutput = true;
103 :
104 94 : AddVectorInputArgs(/* hiddenForCLI = */ true);
105 94 : AddProgressArg();
106 188 : AddArg("pipeline", 0, _("Pipeline string"), &m_pipeline)
107 94 : .SetHiddenForCLI()
108 94 : .SetPositional();
109 94 : AddVectorOutputArgs(/* hiddenForCLI = */ true,
110 : /* shortNameOutputLayerAllowed=*/false);
111 :
112 94 : AddOutputStringArg(&m_output).SetHiddenForCLI();
113 94 : AddStdoutArg(&m_stdout);
114 :
115 94 : RegisterAlgorithms(m_stepRegistry, false);
116 94 : }
117 :
118 : /************************************************************************/
119 : /* GDALVectorPipelineAlgorithm::RegisterAlgorithms() */
120 : /************************************************************************/
121 :
122 : /* static */
123 274 : void GDALVectorPipelineAlgorithm::RegisterAlgorithms(
124 : GDALVectorAlgorithmStepRegistry ®istry, bool forMixedPipeline)
125 : {
126 274 : GDALAlgorithmRegistry::AlgInfo algInfo;
127 :
128 : const auto addSuffixIfNeeded =
129 2466 : [forMixedPipeline](const char *name) -> std::string
130 : {
131 4086 : return forMixedPipeline ? std::string(name).append(VECTOR_SUFFIX)
132 6552 : : std::string(name);
133 274 : };
134 :
135 274 : registry.Register<GDALVectorReadAlgorithm>(
136 548 : addSuffixIfNeeded(GDALVectorReadAlgorithm::NAME));
137 :
138 274 : registry.Register<GDALVectorWriteAlgorithm>(
139 548 : addSuffixIfNeeded(GDALVectorWriteAlgorithm::NAME));
140 :
141 274 : registry.Register<GDALVectorInfoAlgorithm>(
142 548 : addSuffixIfNeeded(GDALVectorInfoAlgorithm::NAME));
143 :
144 274 : registry.Register<GDALVectorBufferAlgorithm>();
145 274 : registry.Register<GDALVectorCheckCoverageAlgorithm>();
146 274 : registry.Register<GDALVectorCheckGeometryAlgorithm>();
147 274 : registry.Register<GDALVectorConcatAlgorithm>();
148 274 : registry.Register<GDALVectorCleanCoverageAlgorithm>();
149 :
150 274 : registry.Register<GDALVectorClipAlgorithm>(
151 548 : addSuffixIfNeeded(GDALVectorClipAlgorithm::NAME));
152 :
153 274 : registry.Register<GDALVectorEditAlgorithm>(
154 548 : addSuffixIfNeeded(GDALVectorEditAlgorithm::NAME));
155 :
156 274 : registry.Register<GDALVectorExplodeCollectionsAlgorithm>();
157 :
158 274 : registry.Register<GDALMaterializeVectorAlgorithm>(
159 548 : addSuffixIfNeeded(GDALMaterializeVectorAlgorithm::NAME));
160 :
161 274 : registry.Register<GDALVectorReprojectAlgorithm>(
162 548 : addSuffixIfNeeded(GDALVectorReprojectAlgorithm::NAME));
163 :
164 274 : registry.Register<GDALVectorFilterAlgorithm>();
165 274 : registry.Register<GDALVectorGeomAlgorithm>();
166 274 : registry.Register<GDALVectorMakeValidAlgorithm>();
167 274 : registry.Register<GDALVectorPartitionAlgorithm>();
168 274 : registry.Register<GDALVectorSegmentizeAlgorithm>();
169 :
170 274 : registry.Register<GDALVectorSelectAlgorithm>(
171 548 : addSuffixIfNeeded(GDALVectorSelectAlgorithm::NAME));
172 :
173 274 : registry.Register<GDALVectorSetGeomTypeAlgorithm>();
174 274 : registry.Register<GDALVectorSimplifyAlgorithm>();
175 274 : registry.Register<GDALVectorSimplifyCoverageAlgorithm>();
176 274 : registry.Register<GDALVectorSQLAlgorithm>();
177 274 : registry.Register<GDALVectorSwapXYAlgorithm>();
178 :
179 274 : registry.Register<GDALTeeVectorAlgorithm>(
180 548 : addSuffixIfNeeded(GDALTeeVectorAlgorithm::NAME));
181 274 : }
182 :
183 : /************************************************************************/
184 : /* GDALVectorPipelineAlgorithm::GetUsageForCLI() */
185 : /************************************************************************/
186 :
187 8 : std::string GDALVectorPipelineAlgorithm::GetUsageForCLI(
188 : bool shortUsage, const UsageOptions &usageOptions) const
189 : {
190 8 : UsageOptions stepUsageOptions;
191 8 : stepUsageOptions.isPipelineStep = true;
192 :
193 8 : if (!m_helpDocCategory.empty() && m_helpDocCategory != "main")
194 : {
195 4 : auto alg = GetStepAlg(m_helpDocCategory);
196 2 : if (alg)
197 : {
198 2 : alg->SetCallPath({m_helpDocCategory});
199 1 : alg->GetArg("help-doc")->Set(true);
200 1 : return alg->GetUsageForCLI(shortUsage, stepUsageOptions);
201 : }
202 : else
203 : {
204 1 : fprintf(stderr, "ERROR: unknown pipeline step '%s'\n",
205 : m_helpDocCategory.c_str());
206 : return CPLSPrintf("ERROR: unknown pipeline step '%s'\n",
207 1 : m_helpDocCategory.c_str());
208 : }
209 : }
210 :
211 6 : UsageOptions usageOptionsMain(usageOptions);
212 6 : usageOptionsMain.isPipelineMain = true;
213 : std::string ret =
214 12 : GDALAlgorithm::GetUsageForCLI(shortUsage, usageOptionsMain);
215 6 : if (shortUsage)
216 2 : return ret;
217 :
218 : ret += "\n<PIPELINE> is of the form: read|concat [READ-OPTIONS] "
219 4 : "( ! <STEP-NAME> [STEP-OPTIONS] )* ! write|info [WRITE-OPTIONS]\n";
220 :
221 4 : if (m_helpDocCategory == "main")
222 : {
223 1 : return ret;
224 : }
225 :
226 3 : ret += '\n';
227 3 : ret += "Example: 'gdal vector pipeline --progress ! read in.gpkg ! \\\n";
228 3 : ret += " reproject --dst-crs=EPSG:32632 ! ";
229 3 : ret += "write out.gpkg --overwrite'\n";
230 3 : ret += '\n';
231 3 : ret += "Potential steps are:\n";
232 :
233 78 : for (const std::string &name : m_stepRegistry.GetNames())
234 : {
235 150 : auto alg = GetStepAlg(name);
236 75 : assert(alg);
237 75 : auto [options, maxOptLen] = alg->GetArgNamesForCLI();
238 75 : stepUsageOptions.maxOptLen =
239 75 : std::max(stepUsageOptions.maxOptLen, maxOptLen);
240 : }
241 :
242 : {
243 3 : const auto name = GDALVectorReadAlgorithm::NAME;
244 3 : ret += '\n';
245 6 : auto alg = GetStepAlg(name);
246 6 : alg->SetCallPath({name});
247 3 : ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
248 : }
249 78 : for (const std::string &name : m_stepRegistry.GetNames())
250 : {
251 150 : auto alg = GetStepAlg(name);
252 75 : assert(alg);
253 81 : if (alg->CanBeFirstStep() && !alg->CanBeMiddleStep() &&
254 81 : !alg->IsHidden() && name != GDALVectorReadAlgorithm::NAME)
255 : {
256 3 : ret += '\n';
257 6 : alg->SetCallPath({name});
258 3 : ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
259 : }
260 : }
261 78 : for (const std::string &name : m_stepRegistry.GetNames())
262 : {
263 150 : auto alg = GetStepAlg(name);
264 75 : assert(alg);
265 75 : if (alg->CanBeMiddleStep() && !alg->IsHidden())
266 : {
267 57 : ret += '\n';
268 114 : alg->SetCallPath({name});
269 57 : ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
270 : }
271 : }
272 78 : for (const std::string &name : m_stepRegistry.GetNames())
273 : {
274 150 : auto alg = GetStepAlg(name);
275 75 : assert(alg);
276 87 : if (alg->CanBeLastStep() && !alg->CanBeMiddleStep() &&
277 87 : !alg->IsHidden() && name != GDALVectorWriteAlgorithm::NAME)
278 : {
279 6 : ret += '\n';
280 12 : alg->SetCallPath({name});
281 6 : ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
282 : }
283 : }
284 : {
285 3 : const auto name = GDALVectorWriteAlgorithm::NAME;
286 3 : ret += '\n';
287 6 : auto alg = GetStepAlg(name);
288 6 : alg->SetCallPath({name});
289 3 : ret += alg->GetUsageForCLI(shortUsage, stepUsageOptions);
290 : }
291 :
292 3 : ret += GetUsageForCLIEnd();
293 :
294 3 : return ret;
295 : }
296 :
297 : /************************************************************************/
298 : /* GDALVectorPipelineOutputLayer */
299 : /************************************************************************/
300 :
301 : /************************************************************************/
302 : /* GDALVectorPipelineOutputLayer() */
303 : /************************************************************************/
304 :
305 129 : GDALVectorPipelineOutputLayer::GDALVectorPipelineOutputLayer(OGRLayer &srcLayer)
306 129 : : m_srcLayer(srcLayer)
307 : {
308 129 : }
309 :
310 : /************************************************************************/
311 : /* ~GDALVectorPipelineOutputLayer() */
312 : /************************************************************************/
313 :
314 : GDALVectorPipelineOutputLayer::~GDALVectorPipelineOutputLayer() = default;
315 :
316 : /************************************************************************/
317 : /* GDALVectorPipelineOutputLayer::ResetReading() */
318 : /************************************************************************/
319 :
320 530 : void GDALVectorPipelineOutputLayer::ResetReading()
321 : {
322 530 : m_srcLayer.ResetReading();
323 530 : m_pendingFeatures.clear();
324 530 : m_idxInPendingFeatures = 0;
325 530 : }
326 :
327 : /************************************************************************/
328 : /* GDALVectorPipelineOutputLayer::GetNextRawFeature() */
329 : /************************************************************************/
330 :
331 1958 : OGRFeature *GDALVectorPipelineOutputLayer::GetNextRawFeature()
332 : {
333 1958 : if (m_idxInPendingFeatures < m_pendingFeatures.size())
334 : {
335 : OGRFeature *poFeature =
336 14 : m_pendingFeatures[m_idxInPendingFeatures].release();
337 14 : ++m_idxInPendingFeatures;
338 14 : return poFeature;
339 : }
340 1944 : m_pendingFeatures.clear();
341 1944 : m_idxInPendingFeatures = 0;
342 : while (true)
343 : {
344 : auto poSrcFeature =
345 1953 : std::unique_ptr<OGRFeature>(m_srcLayer.GetNextFeature());
346 1953 : if (!poSrcFeature)
347 222 : return nullptr;
348 1731 : TranslateFeature(std::move(poSrcFeature), m_pendingFeatures);
349 1731 : if (!m_pendingFeatures.empty())
350 1722 : break;
351 9 : }
352 1722 : OGRFeature *poFeature = m_pendingFeatures[0].release();
353 1722 : m_idxInPendingFeatures = 1;
354 1722 : return poFeature;
355 : }
356 :
357 : /************************************************************************/
358 : /* GDALVectorPipelineOutputDataset */
359 : /************************************************************************/
360 :
361 : /************************************************************************/
362 : /* GDALVectorPipelineOutputDataset() */
363 : /************************************************************************/
364 :
365 121 : GDALVectorPipelineOutputDataset::GDALVectorPipelineOutputDataset(
366 121 : GDALDataset &srcDS)
367 121 : : m_srcDS(srcDS)
368 : {
369 121 : SetDescription(m_srcDS.GetDescription());
370 121 : SetMetadata(m_srcDS.GetMetadata());
371 121 : }
372 :
373 : /************************************************************************/
374 : /* GDALVectorPipelineOutputDataset::AddLayer() */
375 : /************************************************************************/
376 :
377 131 : void GDALVectorPipelineOutputDataset::AddLayer(
378 : OGRLayer &oSrcLayer,
379 : std::unique_ptr<OGRLayerWithTranslateFeature> poNewLayer)
380 : {
381 131 : m_layersToDestroy.push_back(std::move(poNewLayer));
382 : OGRLayerWithTranslateFeature *poNewLayerRaw =
383 131 : m_layersToDestroy.back().get();
384 131 : m_layers.push_back(poNewLayerRaw);
385 131 : m_mapSrcLayerToNewLayer[&oSrcLayer] = poNewLayerRaw;
386 131 : }
387 :
388 : /************************************************************************/
389 : /* GDALVectorPipelineOutputDataset::GetLayerCount() */
390 : /************************************************************************/
391 :
392 425 : int GDALVectorPipelineOutputDataset::GetLayerCount() const
393 : {
394 425 : return static_cast<int>(m_layers.size());
395 : }
396 :
397 : /************************************************************************/
398 : /* GDALVectorPipelineOutputDataset::GetLayer() */
399 : /************************************************************************/
400 :
401 213 : OGRLayer *GDALVectorPipelineOutputDataset::GetLayer(int idx) const
402 : {
403 213 : return idx >= 0 && idx < GetLayerCount() ? m_layers[idx] : nullptr;
404 : }
405 :
406 : /************************************************************************/
407 : /* GDALVectorPipelineOutputDataset::TestCapability() */
408 : /************************************************************************/
409 :
410 78 : int GDALVectorPipelineOutputDataset::TestCapability(const char *pszCap) const
411 : {
412 78 : if (EQUAL(pszCap, ODsCRandomLayerRead) ||
413 31 : EQUAL(pszCap, ODsCMeasuredGeometries) || EQUAL(pszCap, ODsCZGeometries))
414 : {
415 62 : return m_srcDS.TestCapability(pszCap);
416 : }
417 16 : return false;
418 : }
419 :
420 : /************************************************************************/
421 : /* GDALVectorPipelineOutputDataset::ResetReading() */
422 : /************************************************************************/
423 :
424 3 : void GDALVectorPipelineOutputDataset::ResetReading()
425 : {
426 3 : m_srcDS.ResetReading();
427 3 : m_pendingFeatures.clear();
428 3 : m_idxInPendingFeatures = 0;
429 3 : }
430 :
431 : /************************************************************************/
432 : /* GDALVectorPipelineOutputDataset::GetNextFeature() */
433 : /************************************************************************/
434 :
435 23 : OGRFeature *GDALVectorPipelineOutputDataset::GetNextFeature(
436 : OGRLayer **ppoBelongingLayer, double *pdfProgressPct,
437 : GDALProgressFunc pfnProgress, void *pProgressData)
438 : {
439 23 : if (m_idxInPendingFeatures < m_pendingFeatures.size())
440 : {
441 : OGRFeature *poFeature =
442 3 : m_pendingFeatures[m_idxInPendingFeatures].release();
443 3 : if (ppoBelongingLayer)
444 3 : *ppoBelongingLayer = m_belongingLayer;
445 3 : ++m_idxInPendingFeatures;
446 3 : return poFeature;
447 : }
448 :
449 20 : m_pendingFeatures.clear();
450 20 : m_idxInPendingFeatures = 0;
451 :
452 : while (true)
453 : {
454 20 : OGRLayer *poSrcBelongingLayer = nullptr;
455 20 : auto poSrcFeature = std::unique_ptr<OGRFeature>(m_srcDS.GetNextFeature(
456 20 : &poSrcBelongingLayer, pdfProgressPct, pfnProgress, pProgressData));
457 20 : if (!poSrcFeature)
458 3 : return nullptr;
459 17 : auto iterToDstLayer = m_mapSrcLayerToNewLayer.find(poSrcBelongingLayer);
460 17 : if (iterToDstLayer != m_mapSrcLayerToNewLayer.end())
461 : {
462 17 : m_belongingLayer = iterToDstLayer->second;
463 17 : m_belongingLayer->TranslateFeature(std::move(poSrcFeature),
464 17 : m_pendingFeatures);
465 17 : if (!m_pendingFeatures.empty())
466 17 : break;
467 : }
468 0 : }
469 17 : OGRFeature *poFeature = m_pendingFeatures[0].release();
470 17 : if (ppoBelongingLayer)
471 17 : *ppoBelongingLayer = m_belongingLayer;
472 17 : m_idxInPendingFeatures = 1;
473 17 : return poFeature;
474 : }
475 :
476 : /************************************************************************/
477 : /* GDALVectorPipelinePassthroughLayer::GetLayerDefn() */
478 : /************************************************************************/
479 :
480 33 : const OGRFeatureDefn *GDALVectorPipelinePassthroughLayer::GetLayerDefn() const
481 : {
482 33 : return m_srcLayer.GetLayerDefn();
483 : }
484 :
485 : /************************************************************************/
486 : /* GDALVectorNonStreamingAlgorithmDataset() */
487 : /************************************************************************/
488 :
489 30 : GDALVectorNonStreamingAlgorithmDataset::GDALVectorNonStreamingAlgorithmDataset()
490 30 : : m_ds(MEMDataset::Create("", 0, 0, 0, GDT_Unknown, nullptr))
491 : {
492 30 : }
493 :
494 : /************************************************************************/
495 : /* ~GDALVectorNonStreamingAlgorithmDataset() */
496 : /************************************************************************/
497 :
498 : GDALVectorNonStreamingAlgorithmDataset::
499 : ~GDALVectorNonStreamingAlgorithmDataset() = default;
500 :
501 : /************************************************************************/
502 : /* GDALVectorNonStreamingAlgorithmDataset::AddProcessedLayer() */
503 : /************************************************************************/
504 :
505 26 : bool GDALVectorNonStreamingAlgorithmDataset::AddProcessedLayer(
506 : OGRLayer &srcLayer, OGRFeatureDefn &dstDefn)
507 : {
508 26 : CPLStringList aosOptions;
509 26 : if (srcLayer.TestCapability(OLCStringsAsUTF8))
510 : {
511 1 : aosOptions.AddNameValue("ADVERTIZE_UTF8", "TRUE");
512 : }
513 :
514 26 : OGRMemLayer *poDstLayer = m_ds->CreateLayer(dstDefn, aosOptions.List());
515 26 : m_layers.push_back(poDstLayer);
516 :
517 26 : const bool bRet = Process(srcLayer, *poDstLayer);
518 26 : poDstLayer->SetUpdatable(false);
519 52 : return bRet;
520 : }
521 :
522 20 : bool GDALVectorNonStreamingAlgorithmDataset::AddProcessedLayer(
523 : OGRLayer &srcLayer)
524 : {
525 20 : return AddProcessedLayer(srcLayer, *srcLayer.GetLayerDefn());
526 : }
527 :
528 : /************************************************************************/
529 : /* GDALVectorNonStreamingAlgorithmDataset::AddPassThroughLayer() */
530 : /************************************************************************/
531 :
532 6 : void GDALVectorNonStreamingAlgorithmDataset::AddPassThroughLayer(
533 : OGRLayer &oLayer)
534 : {
535 6 : m_passthrough_layers.push_back(
536 12 : std::make_unique<GDALVectorPipelinePassthroughLayer>(oLayer));
537 6 : m_layers.push_back(m_passthrough_layers.back().get());
538 6 : }
539 :
540 : /************************************************************************/
541 : /* GDALVectorNonStreamingAlgorithmDataset::GetLayerCount() */
542 : /************************************************************************/
543 :
544 74 : int GDALVectorNonStreamingAlgorithmDataset::GetLayerCount() const
545 : {
546 74 : return static_cast<int>(m_layers.size());
547 : }
548 :
549 : /************************************************************************/
550 : /* GDALVectorNonStreamingAlgorithmDataset::GetLayer() */
551 : /************************************************************************/
552 :
553 73 : OGRLayer *GDALVectorNonStreamingAlgorithmDataset::GetLayer(int idx) const
554 : {
555 73 : if (idx < 0 || idx >= static_cast<int>(m_layers.size()))
556 : {
557 4 : return nullptr;
558 : }
559 69 : return m_layers[idx];
560 : }
561 :
562 : /************************************************************************/
563 : /* GDALVectorNonStreamingAlgorithmDataset::TestCapability() */
564 : /************************************************************************/
565 :
566 18 : int GDALVectorNonStreamingAlgorithmDataset::TestCapability(
567 : const char *pszCap) const
568 : {
569 18 : if (EQUAL(pszCap, ODsCCreateLayer) || EQUAL(pszCap, ODsCDeleteLayer))
570 : {
571 4 : return false;
572 : }
573 :
574 14 : return m_ds->TestCapability(pszCap);
575 : }
576 :
577 : //! @endcond
|