Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: "gdal vector explode-collections"
5 : * Author: Even Rouault <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "gdalalg_vector_explode_collections.h"
14 :
15 : #include "gdal_priv.h"
16 : #include "ogrsf_frmts.h"
17 :
18 : #include <list>
19 : #include <utility>
20 :
21 : //! @cond Doxygen_Suppress
22 :
23 : #ifndef _
24 : #define _(x) (x)
25 : #endif
26 :
27 : /************************************************************************/
28 : /* GDALVectorExplodeCollectionsAlgorithm() */
29 : /************************************************************************/
30 :
31 57 : GDALVectorExplodeCollectionsAlgorithm::GDALVectorExplodeCollectionsAlgorithm(
32 57 : bool standaloneStep)
33 : : GDALVectorGeomAbstractAlgorithm(NAME, DESCRIPTION, HELP_URL,
34 57 : standaloneStep, m_opts)
35 : {
36 114 : AddArg("geometry-type", 0, _("Geometry type"), &m_opts.m_type)
37 : .SetAutoCompleteFunction(
38 2 : [](const std::string ¤tValue)
39 : {
40 2 : std::vector<std::string> oRet;
41 18 : for (const char *type :
42 : {"GEOMETRY", "POINT", "LINESTRING", "POLYGON",
43 : "CIRCULARSTRING", "COMPOUNDCURVE", "CURVEPOLYGON",
44 20 : "POLYHEDRALSURFACE", "TIN"})
45 : {
46 27 : if (currentValue.empty() ||
47 9 : STARTS_WITH(type, currentValue.c_str()))
48 : {
49 10 : oRet.push_back(type);
50 10 : oRet.push_back(std::string(type).append("Z"));
51 10 : oRet.push_back(std::string(type).append("M"));
52 10 : oRet.push_back(std::string(type).append("ZM"));
53 : }
54 : }
55 2 : return oRet;
56 57 : });
57 :
58 : AddArg("skip-on-type-mismatch", 0,
59 : _("Skip feature when change of feature geometry type failed"),
60 57 : &m_opts.m_skip);
61 57 : }
62 :
63 : namespace
64 : {
65 :
66 : /************************************************************************/
67 : /* GDALVectorExplodeCollectionsAlgorithmLayer */
68 : /************************************************************************/
69 :
70 : class GDALVectorExplodeCollectionsAlgorithmLayer final
71 : : public GDALVectorPipelineOutputLayer
72 : {
73 : private:
74 : const GDALVectorExplodeCollectionsAlgorithm::Options m_opts;
75 : int m_iGeomIdx = -1;
76 : OGRFeatureDefn *const m_poFeatureDefn = nullptr;
77 : GIntBig m_nextFID = 1;
78 :
79 : CPL_DISALLOW_COPY_ASSIGN(GDALVectorExplodeCollectionsAlgorithmLayer)
80 :
81 : void TranslateFeature(
82 : std::unique_ptr<OGRFeature> poSrcFeature,
83 : std::vector<std::unique_ptr<OGRFeature>> &apoOutFeatures) override;
84 :
85 31 : bool IsSelectedGeomField(int idx) const
86 : {
87 31 : return m_iGeomIdx < 0 || idx == m_iGeomIdx;
88 : }
89 :
90 : public:
91 : GDALVectorExplodeCollectionsAlgorithmLayer(
92 : OGRLayer &oSrcLayer,
93 : const GDALVectorExplodeCollectionsAlgorithm::Options &opts);
94 :
95 16 : ~GDALVectorExplodeCollectionsAlgorithmLayer() override
96 8 : {
97 8 : m_poFeatureDefn->Release();
98 16 : }
99 :
100 221 : const OGRFeatureDefn *GetLayerDefn() const override
101 : {
102 221 : return m_poFeatureDefn;
103 : }
104 :
105 121 : void ResetReading() override
106 : {
107 121 : m_nextFID = 1;
108 121 : GDALVectorPipelineOutputLayer::ResetReading();
109 121 : }
110 :
111 5 : OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
112 : bool bForce) override
113 : {
114 5 : return m_srcLayer.GetExtent(iGeomField, psExtent, bForce);
115 : }
116 :
117 39 : int TestCapability(const char *pszCap) const override
118 : {
119 39 : if (EQUAL(pszCap, OLCCurveGeometries) ||
120 35 : EQUAL(pszCap, OLCMeasuredGeometries) ||
121 31 : EQUAL(pszCap, OLCZGeometries) || EQUAL(pszCap, OLCFastGetExtent) ||
122 26 : EQUAL(pszCap, OLCStringsAsUTF8))
123 : {
124 27 : return m_srcLayer.TestCapability(pszCap);
125 : }
126 12 : return false;
127 : }
128 : };
129 :
130 : /************************************************************************/
131 : /* GDALVectorExplodeCollectionsAlgorithmLayer() */
132 : /************************************************************************/
133 :
134 8 : GDALVectorExplodeCollectionsAlgorithmLayer::
135 : GDALVectorExplodeCollectionsAlgorithmLayer(
136 : OGRLayer &oSrcLayer,
137 8 : const GDALVectorExplodeCollectionsAlgorithm::Options &opts)
138 : : GDALVectorPipelineOutputLayer(oSrcLayer), m_opts(opts),
139 8 : m_poFeatureDefn(oSrcLayer.GetLayerDefn()->Clone())
140 : {
141 8 : SetDescription(oSrcLayer.GetDescription());
142 8 : SetMetadata(oSrcLayer.GetMetadata());
143 8 : m_poFeatureDefn->Reference();
144 :
145 8 : if (!m_opts.m_geomField.empty())
146 : {
147 4 : const int nIdx = oSrcLayer.GetLayerDefn()->GetGeomFieldIndex(
148 2 : m_opts.m_geomField.c_str());
149 2 : if (nIdx >= 0)
150 2 : m_iGeomIdx = nIdx;
151 : else
152 0 : m_iGeomIdx = INT_MAX;
153 : }
154 :
155 19 : for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
156 : {
157 11 : if (IsSelectedGeomField(i))
158 : {
159 9 : const auto poGeomFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(i);
160 9 : poGeomFieldDefn->SetType(
161 9 : !m_opts.m_type.empty()
162 : ? m_opts.m_eType
163 7 : : OGR_GT_GetSingle(poGeomFieldDefn->GetType()));
164 : }
165 : }
166 8 : }
167 :
168 : /************************************************************************/
169 : /* TranslateFeature() */
170 : /************************************************************************/
171 :
172 413 : void GDALVectorExplodeCollectionsAlgorithmLayer::TranslateFeature(
173 : std::unique_ptr<OGRFeature> poSrcFeature,
174 : std::vector<std::unique_ptr<OGRFeature>> &apoOutFeatures)
175 : {
176 826 : std::list<std::pair<std::unique_ptr<OGRFeature>, int>> apoTmpFeatures;
177 413 : apoTmpFeatures.emplace_back(std::move(poSrcFeature), 0);
178 413 : const int nGeomFieldCount = m_poFeatureDefn->GetGeomFieldCount();
179 832 : while (!apoTmpFeatures.empty())
180 : {
181 838 : auto [poCurFeature, nextGeomIndex] = std::move(apoTmpFeatures.front());
182 419 : auto insertionPoint = apoTmpFeatures.erase(apoTmpFeatures.begin());
183 419 : bool bInsertionDone = false;
184 839 : for (int i = nextGeomIndex; i < nGeomFieldCount; ++i)
185 : {
186 423 : auto poGeom = poCurFeature->GetGeomFieldRef(i);
187 422 : if (poGeom && !poGeom->IsEmpty() &&
188 422 : OGR_GT_IsSubClassOf(poGeom->getGeometryType(),
189 845 : wkbGeometryCollection) &&
190 20 : IsSelectedGeomField(i))
191 : {
192 : const auto poGeomFieldDefn =
193 16 : m_poFeatureDefn->GetGeomFieldDefn(i);
194 16 : bInsertionDone = true;
195 : const auto eTargetType =
196 16 : !m_opts.m_type.empty()
197 16 : ? m_opts.m_eType
198 14 : : OGR_GT_GetSingle(poGeomFieldDefn->GetType());
199 : auto poColl = std::unique_ptr<OGRGeometryCollection>(
200 16 : poCurFeature->StealGeometry(i)->toGeometryCollection());
201 16 : bool bTmpFeaturesInserted = false;
202 49 : for (const auto *poSubGeomRef : poColl.get())
203 : {
204 : auto poNewFeature =
205 66 : std::unique_ptr<OGRFeature>(poCurFeature->Clone());
206 : auto poNewGeom =
207 66 : std::unique_ptr<OGRGeometry>(poSubGeomRef->clone());
208 33 : if (poNewGeom->getGeometryType() != eTargetType)
209 3 : poNewGeom.reset(OGRGeometryFactory::forceTo(
210 : poNewGeom.release(), eTargetType));
211 36 : if (m_opts.m_skip && !m_opts.m_type.empty() &&
212 3 : (!poNewGeom ||
213 3 : (wkbFlatten(eTargetType) != wkbUnknown &&
214 3 : poNewGeom->getGeometryType() != eTargetType)))
215 : {
216 : // skip
217 : }
218 : else
219 : {
220 64 : poNewGeom->assignSpatialReference(
221 32 : poGeomFieldDefn->GetSpatialRef());
222 32 : poNewFeature->SetGeomFieldDirectly(i,
223 : poNewGeom.release());
224 :
225 56 : if (!m_opts.m_geomField.empty() ||
226 24 : i == nGeomFieldCount - 1)
227 : {
228 26 : poNewFeature->SetFDefnUnsafe(m_poFeatureDefn);
229 26 : poNewFeature->SetFID(m_nextFID);
230 26 : ++m_nextFID;
231 26 : apoOutFeatures.push_back(std::move(poNewFeature));
232 : }
233 : else
234 : {
235 6 : bTmpFeaturesInserted = true;
236 : apoTmpFeatures.insert(
237 : insertionPoint,
238 6 : std::pair<std::unique_ptr<OGRFeature>, int>(
239 6 : std::move(poNewFeature),
240 12 : nextGeomIndex + 1));
241 : }
242 : }
243 : }
244 :
245 16 : if (bTmpFeaturesInserted)
246 3 : break;
247 : }
248 407 : else if (poGeom)
249 : {
250 : const auto poGeomFieldDefn =
251 406 : m_poFeatureDefn->GetGeomFieldDefn(i);
252 406 : poGeom->assignSpatialReference(
253 406 : poGeomFieldDefn->GetSpatialRef());
254 : }
255 : }
256 419 : if (!bInsertionDone)
257 : {
258 403 : poCurFeature->SetFDefnUnsafe(m_poFeatureDefn);
259 403 : poCurFeature->SetFID(m_nextFID);
260 403 : ++m_nextFID;
261 403 : apoOutFeatures.push_back(std::move(poCurFeature));
262 : }
263 : }
264 413 : }
265 :
266 : } // namespace
267 :
268 : /************************************************************************/
269 : /* GDALVectorExplodeCollectionsAlgorithm::CreateAlgLayer() */
270 : /************************************************************************/
271 :
272 : std::unique_ptr<OGRLayerWithTranslateFeature>
273 8 : GDALVectorExplodeCollectionsAlgorithm::CreateAlgLayer(OGRLayer &srcLayer)
274 : {
275 8 : return std::make_unique<GDALVectorExplodeCollectionsAlgorithmLayer>(
276 8 : srcLayer, m_opts);
277 : }
278 :
279 : /************************************************************************/
280 : /* GDALVectorExplodeCollectionsAlgorithm::RunStep() */
281 : /************************************************************************/
282 :
283 9 : bool GDALVectorExplodeCollectionsAlgorithm::RunStep(
284 : GDALPipelineStepRunContext &ctxt)
285 : {
286 9 : if (!m_opts.m_type.empty())
287 : {
288 3 : m_opts.m_eType = OGRFromOGCGeomType(m_opts.m_type.c_str());
289 4 : if (wkbFlatten(m_opts.m_eType) == wkbUnknown &&
290 1 : !STARTS_WITH_CI(m_opts.m_type.c_str(), "GEOMETRY"))
291 : {
292 1 : ReportError(CE_Failure, CPLE_AppDefined,
293 : "Invalid geometry type '%s'", m_opts.m_type.c_str());
294 1 : return false;
295 : }
296 : }
297 :
298 8 : return GDALVectorGeomAbstractAlgorithm::RunStep(ctxt);
299 : }
300 :
301 : GDALVectorExplodeCollectionsAlgorithmStandalone::
302 : ~GDALVectorExplodeCollectionsAlgorithmStandalone() = default;
303 :
304 : //! @endcond
|