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