Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: "explode" step of "vector pipeline"
5 : * Author: Daniel Baston
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2026, ISciences LLC
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "gdalalg_vector_explode.h"
14 :
15 : #include "cpl_conv.h"
16 : #include "cpl_string.h"
17 : #include "gdal_priv.h"
18 : #include "ogr_p.h"
19 : #include "ogrsf_frmts.h"
20 :
21 : #include <algorithm>
22 : #include <cinttypes>
23 : #include <list>
24 : #include <memory>
25 : #include <numeric>
26 : #include <vector>
27 :
28 : //! @cond Doxygen_Suppress
29 :
30 : #ifndef _
31 : #define _(x) (x)
32 : #endif
33 :
34 : /************************************************************************/
35 : /* GDALVectorExplodeAlgorithm::GDALVectorExplodeAlgorithm() */
36 : /************************************************************************/
37 :
38 105 : GDALVectorExplodeAlgorithm::GDALVectorExplodeAlgorithm(bool standaloneStep)
39 : : GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
40 105 : standaloneStep)
41 : {
42 105 : AddActiveLayerArg(&m_activeLayer);
43 :
44 : {
45 : auto &arg =
46 210 : AddArg("field", 0, _("Attribute fields(s) to explode"), &m_fields)
47 105 : .SetDuplicateValuesAllowed(false)
48 105 : .SetMetaVar("FIELD");
49 :
50 315 : SetAutoCompleteFunctionForFieldName(
51 105 : arg, nullptr, true, false, m_inputDataset, {"ALL"},
52 2 : [](const OGRFieldDefn *defn)
53 107 : { return OGR_GetFieldTypeIsList(defn->GetType()); });
54 : }
55 :
56 105 : AddArg("geometry", 0, _("Explode default geometry field"), &m_defaultGeom);
57 :
58 : {
59 : auto &arg = AddArg("geometry-field", 0,
60 210 : _("Geometry field(s) to explode"), &m_geomFields)
61 105 : .SetDuplicateValuesAllowed(false)
62 105 : .SetMetaVar("GEOMETRY-FIELD");
63 315 : SetAutoCompleteFunctionForFieldName(arg, nullptr, false, true,
64 210 : m_inputDataset, {"ALL"});
65 : }
66 :
67 : AddArg("index-field", 0, _("Name of the output index field"),
68 210 : &m_indexFieldName)
69 105 : .SetDefault(m_indexFieldName);
70 105 : }
71 :
72 : GDALVectorExplodeAlgorithmStandalone::~GDALVectorExplodeAlgorithmStandalone() =
73 : default;
74 :
75 : namespace
76 : {
77 :
78 : class GDALVectorExplodeLayer final : public GDALVectorPipelineOutputLayer
79 : {
80 : public:
81 22 : GDALVectorExplodeLayer(OGRLayer &srcLayer,
82 : const std::vector<std::string> &fieldsToExplode,
83 : const std::vector<std::string> &geomFieldsToExplode,
84 : const std::string &indexFieldName)
85 22 : : GDALVectorPipelineOutputLayer(srcLayer),
86 : m_fieldsToExplode(fieldsToExplode),
87 : m_geomFieldsToExplode(geomFieldsToExplode),
88 22 : m_indexFieldName(indexFieldName)
89 : {
90 22 : if (!PrepareFeatureDefn())
91 : {
92 1 : m_setupError = true;
93 : }
94 22 : }
95 :
96 22 : bool PrepareFeatureDefn()
97 : {
98 22 : m_poFeatureDefn.reset(
99 22 : OGRFeatureDefn::CreateFeatureDefn(m_srcLayer.GetName()));
100 :
101 : // Avoid creating geometry field with null SRS
102 : // We'll copy it in later from the source layer
103 22 : m_poFeatureDefn->DeleteGeomFieldDefn(0);
104 :
105 22 : const bool addIndexField = !m_indexFieldName.empty();
106 :
107 22 : if (addIndexField)
108 : {
109 : auto poIdxField = std::make_unique<OGRFieldDefn>(
110 2 : m_indexFieldName.c_str(), OFTInteger);
111 1 : m_poFeatureDefn->AddFieldDefn(std::move(poIdxField));
112 : }
113 :
114 22 : const OGRFeatureDefn *poSrcDefn = m_srcLayer.GetLayerDefn();
115 :
116 : // By default, all fields copied as-is.
117 22 : m_unnestedFieldSrcToDstMap.resize(poSrcDefn->GetFieldCount(), -1);
118 22 : m_passThroughFieldSrcToDstMap.resize(poSrcDefn->GetFieldCount());
119 22 : std::iota(m_passThroughFieldSrcToDstMap.begin(),
120 : m_passThroughFieldSrcToDstMap.end(), addIndexField ? 1 : 0);
121 :
122 22 : m_geomFieldExploded.resize(poSrcDefn->GetGeomFieldCount(), false);
123 :
124 42 : for (const auto &fieldName : m_fieldsToExplode)
125 : {
126 21 : const int iSrcField = poSrcDefn->GetFieldIndex(fieldName.c_str());
127 21 : if (iSrcField < 0)
128 : {
129 1 : CPLError(CE_Failure, CPLE_AppDefined,
130 : "Field '%s' not found in source layer.",
131 : fieldName.c_str());
132 1 : return false;
133 : }
134 :
135 : const OGRFieldDefn *poSrcFieldDefn =
136 20 : poSrcDefn->GetFieldDefn(iSrcField);
137 20 : const auto eSrcType = poSrcFieldDefn->GetType();
138 20 : if (OGR_GetFieldTypeIsList(eSrcType))
139 : {
140 17 : m_passThroughFieldSrcToDstMap[iSrcField] = -1;
141 17 : m_unnestedFieldSrcToDstMap[iSrcField] =
142 17 : iSrcField + addIndexField;
143 : }
144 : }
145 :
146 39 : for (const auto &fieldName : m_geomFieldsToExplode)
147 : {
148 : // Is it a geometry field?
149 18 : int iSrcGeomField = poSrcDefn->GetGeomFieldIndex(fieldName.c_str());
150 :
151 : // Interpret --geometry-field _OGR_GEOMETRY_ as the first geometry
152 : // field, regardless of what it is actually named
153 18 : if (iSrcGeomField < 0)
154 : {
155 24 : if (poSrcDefn->GetGeomFieldCount() > 0 &&
156 12 : EQUAL(fieldName.c_str(),
157 : OGR_GEOMETRY_DEFAULT_NON_EMPTY_NAME))
158 : {
159 10 : iSrcGeomField = 0;
160 : }
161 : }
162 :
163 : // Didn't find anything by name. Check by index.
164 20 : if (iSrcGeomField < 0 &&
165 2 : std::all_of(
166 2 : fieldName.begin(), fieldName.end(), [](char c)
167 2 : { return std::isdigit(static_cast<unsigned char>(c)); }))
168 : {
169 2 : const int iGeomField = std::atoi(fieldName.c_str());
170 :
171 2 : if (iGeomField < poSrcDefn->GetGeomFieldCount())
172 : {
173 2 : iSrcGeomField = iGeomField;
174 : }
175 : }
176 :
177 18 : if (iSrcGeomField < 0)
178 : {
179 0 : CPLError(
180 : CE_Failure, CPLE_AppDefined,
181 : "Could not find geometry field '%s' in source layer '%s'",
182 0 : fieldName.c_str(), m_srcLayer.GetName());
183 0 : return false;
184 : }
185 :
186 18 : m_geomFieldExploded[iSrcGeomField] = true;
187 : }
188 :
189 : // Create attribute fields
190 59 : for (int iSrcField = 0; iSrcField < poSrcDefn->GetFieldCount();
191 : iSrcField++)
192 : {
193 38 : const auto *poSrcFieldDefn = poSrcDefn->GetFieldDefn(iSrcField);
194 38 : std::unique_ptr<OGRFieldDefn> poDstFieldDefn;
195 :
196 38 : if (m_passThroughFieldSrcToDstMap[iSrcField] != -1)
197 : {
198 : poDstFieldDefn =
199 21 : std::make_unique<OGRFieldDefn>(*poSrcFieldDefn);
200 : }
201 : else
202 : {
203 : const auto eScalarType =
204 17 : OGR_GetFieldTypeAsScalar(poSrcFieldDefn->GetType());
205 17 : poDstFieldDefn = std::make_unique<OGRFieldDefn>(
206 34 : poSrcFieldDefn->GetNameRef(), eScalarType);
207 : }
208 :
209 38 : m_poFeatureDefn->AddFieldDefn(std::move(poDstFieldDefn));
210 : }
211 :
212 : // Create geometry fields
213 44 : for (int iSrcGeomField = 0;
214 44 : iSrcGeomField < poSrcDefn->GetGeomFieldCount(); iSrcGeomField++)
215 : {
216 : const OGRGeomFieldDefn *poSrcGeomFieldDefn =
217 23 : poSrcDefn->GetGeomFieldDefn(iSrcGeomField);
218 23 : std::unique_ptr<OGRGeomFieldDefn> poDstGeomFieldDefn;
219 :
220 23 : if (m_geomFieldExploded[iSrcGeomField])
221 : {
222 : const auto eDstType =
223 18 : OGR_GT_GetSingle(poSrcGeomFieldDefn->GetType());
224 18 : poDstGeomFieldDefn = std::make_unique<OGRGeomFieldDefn>(
225 18 : poSrcGeomFieldDefn->GetNameRef(), eDstType);
226 36 : poDstGeomFieldDefn->SetSpatialRef(
227 18 : poSrcGeomFieldDefn->GetSpatialRef());
228 : }
229 : else
230 : {
231 : poDstGeomFieldDefn =
232 5 : std::make_unique<OGRGeomFieldDefn>(*poSrcGeomFieldDefn);
233 : }
234 :
235 23 : m_poFeatureDefn->AddGeomFieldDefn(std::move(poDstGeomFieldDefn));
236 : }
237 :
238 21 : return true;
239 : }
240 :
241 3 : const char *GetDescription() const override
242 : {
243 3 : return m_poFeatureDefn->GetName();
244 : }
245 :
246 368 : const OGRFeatureDefn *GetLayerDefn() const override
247 : {
248 368 : return m_poFeatureDefn.get();
249 : }
250 :
251 133 : void ResetReading() override
252 : {
253 133 : m_nextFID = 1;
254 133 : GDALVectorPipelineOutputLayer::ResetReading();
255 133 : }
256 :
257 54 : int TestCapability(const char *pszCap) const override
258 : {
259 54 : if (EQUAL(pszCap, OLCFastGetExtent) ||
260 52 : EQUAL(pszCap, OLCFastGetExtent3D) ||
261 52 : EQUAL(pszCap, OLCStringsAsUTF8) ||
262 38 : EQUAL(pszCap, OLCCurveGeometries) ||
263 34 : EQUAL(pszCap, OLCMeasuredGeometries) ||
264 30 : EQUAL(pszCap, OLCZGeometries))
265 : {
266 27 : return m_srcLayer.TestCapability(pszCap);
267 : }
268 :
269 27 : return false;
270 : }
271 :
272 162 : bool TranslateFeature(
273 : std::unique_ptr<OGRFeature> poSrcFeature,
274 : std::vector<std::unique_ptr<OGRFeature>> &apoOutFeatures) override
275 : {
276 162 : if (m_setupError)
277 : {
278 1 : CPLError(CE_Failure, CPLE_AppDefined,
279 : "Failed to prepare output layer.");
280 1 : return false;
281 : }
282 :
283 161 : int nDstFeatures = 1;
284 :
285 627 : for (int iDstFeature = 0; iDstFeature < nDstFeatures; iDstFeature++)
286 : {
287 : auto poDstFeature =
288 472 : std::make_unique<OGRFeature>(m_poFeatureDefn.get());
289 472 : if (!m_indexFieldName.empty())
290 : {
291 9 : poDstFeature->SetField(0, iDstFeature);
292 : }
293 :
294 944 : if (poDstFeature->SetFieldsFrom(
295 472 : poSrcFeature.get(), m_passThroughFieldSrcToDstMap.data(),
296 472 : true) != OGRERR_NONE)
297 : {
298 0 : CPLError(CE_Failure, CPLE_AppDefined,
299 : "Failed to set fields of output feature");
300 0 : return false;
301 : }
302 :
303 2576 : for (int iSrcArrayField = 0;
304 2576 : iSrcArrayField <
305 2576 : static_cast<int>(m_unnestedFieldSrcToDstMap.size());
306 : iSrcArrayField++)
307 : {
308 : const int iDstField =
309 2108 : m_unnestedFieldSrcToDstMap[iSrcArrayField];
310 2108 : if (iDstField < 0)
311 : {
312 1622 : continue;
313 : }
314 :
315 : const auto poSrcFieldDefn =
316 486 : poSrcFeature->GetFieldDefnRef(iSrcArrayField);
317 486 : const auto eSrcType = poSrcFieldDefn->GetType();
318 486 : int nArrayLength = -1;
319 486 : if (eSrcType == OFTIntegerList)
320 : {
321 423 : const int *pnArray = poSrcFeature->GetFieldAsIntegerList(
322 : iSrcArrayField, &nArrayLength);
323 423 : if (iDstFeature >= nArrayLength)
324 : {
325 1 : CPLError(CE_Failure, CPLE_AppDefined,
326 : "Field '%s' of source feature %" PRId64
327 : " does not have enough elements.",
328 : poSrcFieldDefn->GetNameRef(),
329 1 : static_cast<int64_t>(poSrcFeature->GetFID()));
330 4 : return false;
331 : }
332 422 : poDstFeature->SetField(iDstField, pnArray[iDstFeature]);
333 : }
334 63 : else if (eSrcType == OFTInteger64List)
335 : {
336 : const GIntBig *pnArray =
337 21 : poSrcFeature->GetFieldAsInteger64List(iSrcArrayField,
338 : &nArrayLength);
339 21 : if (iDstFeature >= nArrayLength)
340 : {
341 1 : CPLError(CE_Failure, CPLE_AppDefined,
342 : "Field '%s' of source feature %" PRId64
343 : " does not have enough elements.",
344 : poSrcFieldDefn->GetNameRef(),
345 1 : static_cast<int64_t>(poSrcFeature->GetFID()));
346 1 : return false;
347 : }
348 20 : poDstFeature->SetField(iDstField, pnArray[iDstFeature]);
349 : }
350 42 : else if (eSrcType == OFTRealList)
351 : {
352 : const double *padfArray =
353 21 : poSrcFeature->GetFieldAsDoubleList(iSrcArrayField,
354 : &nArrayLength);
355 21 : if (iDstFeature >= nArrayLength)
356 : {
357 1 : CPLError(CE_Failure, CPLE_AppDefined,
358 : "Field '%s' of source feature %" PRId64
359 : " does not have enough elements.",
360 : poSrcFieldDefn->GetNameRef(),
361 1 : static_cast<int64_t>(poSrcFeature->GetFID()));
362 1 : return false;
363 : }
364 20 : poDstFeature->SetField(iDstField, padfArray[iDstFeature]);
365 : }
366 21 : else if (eSrcType == OFTStringList)
367 : {
368 : CSLConstList papszArray =
369 21 : poSrcFeature->GetFieldAsStringList(iSrcArrayField);
370 21 : nArrayLength = CSLCount(papszArray);
371 21 : if (iDstFeature >= nArrayLength)
372 : {
373 1 : CPLError(CE_Failure, CPLE_AppDefined,
374 : "Field '%s' of source feature %" PRId64
375 : " does not have enough elements.",
376 : poSrcFieldDefn->GetNameRef(),
377 1 : static_cast<int64_t>(poSrcFeature->GetFID()));
378 1 : return false;
379 : }
380 20 : poDstFeature->SetField(iDstField, papszArray[iDstFeature]);
381 : }
382 482 : nDstFeatures = std::max(nDstFeatures, nArrayLength);
383 : }
384 :
385 935 : for (int iGeomField = 0;
386 935 : iGeomField < poSrcFeature->GetGeomFieldCount(); iGeomField++)
387 : {
388 469 : if (m_geomFieldExploded[iGeomField])
389 : {
390 0 : std::unique_ptr<OGRGeometry> poDstGeom;
391 :
392 : OGRGeometry *poSrcGeom(
393 55 : poSrcFeature->GetGeomFieldRef(iGeomField));
394 :
395 : const bool bSrcIsCollection =
396 103 : poSrcGeom != nullptr &&
397 48 : OGR_GT_IsSubClassOf(
398 48 : wkbFlatten(poSrcGeom->getGeometryType()),
399 55 : wkbGeometryCollection);
400 :
401 55 : if (bSrcIsCollection)
402 : {
403 : OGRGeometryCollection *poColl =
404 44 : poSrcGeom->toGeometryCollection();
405 :
406 44 : auto nGeoms = poColl->getNumGeometries();
407 44 : nDstFeatures = std::max(nDstFeatures, nGeoms);
408 :
409 44 : if (nGeoms == 0)
410 : {
411 1 : CPLError(
412 : CE_Failure, CPLE_AppDefined,
413 : "Geometry field '%s' of source feature %" PRId64
414 : " has %d elements (expected %d)",
415 1 : poSrcFeature->GetDefnRef()
416 1 : ->GetGeomFieldDefn(iGeomField)
417 : ->GetNameRef(),
418 1 : static_cast<int64_t>(poSrcFeature->GetFID()),
419 : nGeoms + iDstFeature, nDstFeatures);
420 1 : return false;
421 : }
422 :
423 43 : poDstGeom = poColl->stealGeometry(0);
424 : }
425 : else
426 : {
427 13 : if (iDstFeature > 1 &&
428 2 : apoOutFeatures.front()->GetGeomFieldRef(
429 : iGeomField) != nullptr)
430 : {
431 1 : CPLError(
432 : CE_Failure, CPLE_AppDefined,
433 : "Geometry field '%s' of source feature %" PRId64
434 : " is not a collection.",
435 1 : poSrcFeature->GetDefnRef()
436 1 : ->GetGeomFieldDefn(iGeomField)
437 : ->GetNameRef(),
438 1 : static_cast<int64_t>(poSrcFeature->GetFID()));
439 1 : return false;
440 : }
441 :
442 10 : poDstGeom.reset(
443 : poSrcFeature->StealGeometry(iGeomField));
444 : }
445 :
446 106 : poDstFeature->SetGeomField(iGeomField,
447 53 : std::move(poDstGeom));
448 : }
449 : else
450 : {
451 0 : std::unique_ptr<OGRGeometry> poSrcGeom;
452 :
453 414 : if (apoOutFeatures.empty())
454 : {
455 213 : poSrcGeom.reset(
456 : poSrcFeature->StealGeometry(iGeomField));
457 : }
458 : else
459 : {
460 402 : poSrcGeom.reset(apoOutFeatures.front()
461 201 : ->GetGeomFieldRef(iGeomField)
462 201 : ->clone());
463 : }
464 :
465 828 : poDstFeature->SetGeomField(iGeomField,
466 414 : std::move(poSrcGeom));
467 : }
468 : }
469 :
470 466 : poDstFeature->SetFID(m_nextFID++);
471 466 : if (PassesFilters(poDstFeature.get()))
472 354 : apoOutFeatures.push_back(std::move(poDstFeature));
473 : }
474 :
475 155 : return true;
476 : }
477 :
478 : protected:
479 4 : OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
480 : bool bForce) override
481 : {
482 4 : return m_srcLayer.GetExtent(iGeomField, psExtent, bForce);
483 : }
484 :
485 0 : OGRErr IGetExtent3D(int iGeomField, OGREnvelope3D *psExtent3D,
486 : bool bForce) override
487 : {
488 0 : return m_srcLayer.GetExtent3D(iGeomField, psExtent3D, bForce);
489 : }
490 :
491 : private:
492 : std::vector<int> m_passThroughFieldSrcToDstMap{};
493 : std::vector<int> m_unnestedFieldSrcToDstMap{};
494 : std::vector<bool> m_geomFieldExploded{};
495 : std::vector<std::string> m_fieldsToExplode{};
496 : std::vector<std::string> m_geomFieldsToExplode{};
497 : std::string m_indexFieldName{};
498 : bool m_setupError{false};
499 : OGRFeatureDefnRefCountedPtr m_poFeatureDefn{nullptr};
500 : GIntBig m_nextFID{1};
501 :
502 : CPL_DISALLOW_COPY_ASSIGN(GDALVectorExplodeLayer)
503 : };
504 :
505 : } // namespace
506 :
507 : /************************************************************************/
508 : /* GDALVectorExplodeAlgorithm::RunStep() */
509 : /************************************************************************/
510 :
511 21 : bool GDALVectorExplodeAlgorithm::RunStep(GDALPipelineStepRunContext &)
512 : {
513 21 : auto poSrcDS = m_inputDataset[0].GetDatasetRef();
514 21 : CPLAssert(poSrcDS);
515 :
516 42 : auto poOutDS = std::make_unique<GDALVectorPipelineOutputDataset>(*poSrcDS);
517 :
518 21 : if (m_defaultGeom)
519 : {
520 4 : m_geomFields.emplace_back(OGR_GEOMETRY_DEFAULT_NON_EMPTY_NAME);
521 : }
522 :
523 21 : if (m_fields.empty() && m_geomFields.empty())
524 : {
525 0 : ReportError(CE_Failure, CPLE_IllegalArg,
526 : "At least one field or geometry field must be specified");
527 0 : return false;
528 : }
529 :
530 43 : for (OGRLayer *poSrcLayer : poSrcDS->GetLayers())
531 : {
532 22 : if (!poSrcLayer)
533 0 : continue;
534 :
535 24 : if (!m_activeLayer.empty() &&
536 2 : poSrcLayer->GetDescription() != m_activeLayer)
537 : {
538 2 : poOutDS->AddLayer(
539 : *poSrcLayer,
540 2 : std::make_unique<GDALVectorPipelinePassthroughLayer>(
541 : *poSrcLayer));
542 : }
543 :
544 22 : const auto *poLayerDefn = poSrcLayer->GetLayerDefn();
545 :
546 44 : auto fieldsForLayer = m_fields;
547 44 : auto geomFieldsForLayer = m_geomFields;
548 :
549 22 : if (geomFieldsForLayer.size() == 1 && geomFieldsForLayer[0] == "ALL")
550 : {
551 2 : geomFieldsForLayer.clear();
552 2 : for (int iGeomField = 0;
553 4 : iGeomField < poLayerDefn->GetGeomFieldCount(); iGeomField++)
554 : {
555 : geomFieldsForLayer.emplace_back(
556 2 : poLayerDefn->GetGeomFieldDefn(iGeomField)->GetNameRef());
557 : }
558 : }
559 :
560 22 : if (fieldsForLayer.size() == 1 && fieldsForLayer[0] == "ALL")
561 : {
562 2 : fieldsForLayer.clear();
563 4 : for (int iField = 0; iField < poLayerDefn->GetFieldCount();
564 : iField++)
565 : {
566 : fieldsForLayer.emplace_back(
567 2 : poLayerDefn->GetFieldDefn(iField)->GetNameRef());
568 : }
569 : }
570 :
571 : auto poOutLayer = std::make_unique<GDALVectorExplodeLayer>(
572 22 : *poSrcLayer, fieldsForLayer, geomFieldsForLayer, m_indexFieldName);
573 22 : poOutDS->AddLayer(*poSrcLayer, std::move(poOutLayer));
574 : }
575 :
576 21 : m_outputDataset.Set(std::move(poOutDS));
577 21 : return true;
578 : }
579 :
580 : //! @endcond
|