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 31 : GDALVectorExplodeCollectionsAlgorithm::GDALVectorExplodeCollectionsAlgorithm(
32 31 : bool standaloneStep)
33 : : GDALVectorGeomAbstractAlgorithm(NAME, DESCRIPTION, HELP_URL,
34 31 : standaloneStep, m_opts)
35 : {
36 62 : 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 31 : });
57 :
58 : AddArg("skip-on-type-mismatch", 0,
59 : _("Skip feature when change of feature geometry type failed"),
60 31 : &m_opts.m_skip);
61 31 : }
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 224 : OGRFeatureDefn *GetLayerDefn() override
101 : {
102 224 : return m_poFeatureDefn;
103 : }
104 :
105 119 : void ResetReading() override
106 : {
107 119 : m_nextFID = 1;
108 119 : GDALVectorPipelineOutputLayer::ResetReading();
109 119 : }
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 38 : int TestCapability(const char *pszCap) override
118 : {
119 38 : if (EQUAL(pszCap, OLCCurveGeometries) ||
120 34 : EQUAL(pszCap, OLCMeasuredGeometries) ||
121 30 : EQUAL(pszCap, OLCZGeometries) || EQUAL(pszCap, OLCFastGetExtent) ||
122 25 : EQUAL(pszCap, OLCStringsAsUTF8))
123 : {
124 27 : return m_srcLayer.TestCapability(pszCap);
125 : }
126 11 : 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 402 : void GDALVectorExplodeCollectionsAlgorithmLayer::TranslateFeature(
173 : std::unique_ptr<OGRFeature> poSrcFeature,
174 : std::vector<std::unique_ptr<OGRFeature>> &apoOutFeatures)
175 : {
176 804 : std::list<std::pair<std::unique_ptr<OGRFeature>, int>> apoTmpFeatures;
177 402 : apoTmpFeatures.emplace_back(std::move(poSrcFeature), 0);
178 402 : const int nGeomFieldCount = m_poFeatureDefn->GetGeomFieldCount();
179 810 : while (!apoTmpFeatures.empty())
180 : {
181 816 : auto [poCurFeature, nextGeomIndex] = std::move(apoTmpFeatures.front());
182 408 : auto insertionPoint = apoTmpFeatures.erase(apoTmpFeatures.begin());
183 408 : bool bInsertionDone = false;
184 817 : for (int i = nextGeomIndex; i < nGeomFieldCount; ++i)
185 : {
186 412 : auto poGeom = poCurFeature->GetGeomFieldRef(i);
187 411 : if (poGeom && !poGeom->IsEmpty() &&
188 411 : OGR_GT_IsSubClassOf(poGeom->getGeometryType(),
189 823 : 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 396 : else if (poGeom)
249 : {
250 : const auto poGeomFieldDefn =
251 395 : m_poFeatureDefn->GetGeomFieldDefn(i);
252 395 : poGeom->assignSpatialReference(
253 395 : poGeomFieldDefn->GetSpatialRef());
254 : }
255 : }
256 408 : if (!bInsertionDone)
257 : {
258 392 : poCurFeature->SetFDefnUnsafe(m_poFeatureDefn);
259 392 : poCurFeature->SetFID(m_nextFID);
260 392 : ++m_nextFID;
261 392 : apoOutFeatures.push_back(std::move(poCurFeature));
262 : }
263 : }
264 402 : }
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
|