Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: "gdal vector geom 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_geom_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 : /* GDALVectorGeomExplodeCollectionsAlgorithm() */
29 : /************************************************************************/
30 :
31 15 : GDALVectorGeomExplodeCollectionsAlgorithm::
32 15 : GDALVectorGeomExplodeCollectionsAlgorithm(bool standaloneStep)
33 : : GDALVectorGeomAbstractAlgorithm(NAME, DESCRIPTION, HELP_URL,
34 15 : standaloneStep, m_opts)
35 : {
36 30 : 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 15 : });
57 :
58 : AddArg("skip-on-type-mismatch", 0,
59 : _("Skip feature when change of feature geometry type failed"),
60 15 : &m_opts.m_skip);
61 15 : }
62 :
63 : namespace
64 : {
65 :
66 : /************************************************************************/
67 : /* GDALVectorGeomExplodeCollectionsAlgorithmLayer */
68 : /************************************************************************/
69 :
70 : class GDALVectorGeomExplodeCollectionsAlgorithmLayer final
71 : : public GDALVectorPipelineOutputLayer
72 : {
73 : private:
74 : const GDALVectorGeomExplodeCollectionsAlgorithm::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(GDALVectorGeomExplodeCollectionsAlgorithmLayer)
80 :
81 : void TranslateFeature(
82 : std::unique_ptr<OGRFeature> poSrcFeature,
83 : std::vector<std::unique_ptr<OGRFeature>> &apoOutFeatures) override;
84 :
85 30 : bool IsSelectedGeomField(int idx) const
86 : {
87 30 : return m_iGeomIdx < 0 || idx == m_iGeomIdx;
88 : }
89 :
90 : public:
91 : GDALVectorGeomExplodeCollectionsAlgorithmLayer(
92 : OGRLayer &oSrcLayer,
93 : const GDALVectorGeomExplodeCollectionsAlgorithm::Options &opts);
94 :
95 14 : ~GDALVectorGeomExplodeCollectionsAlgorithmLayer() override
96 7 : {
97 7 : m_poFeatureDefn->Release();
98 14 : }
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 : /* GDALVectorGeomExplodeCollectionsAlgorithmLayer() */
132 : /************************************************************************/
133 :
134 7 : GDALVectorGeomExplodeCollectionsAlgorithmLayer::
135 : GDALVectorGeomExplodeCollectionsAlgorithmLayer(
136 : OGRLayer &oSrcLayer,
137 7 : const GDALVectorGeomExplodeCollectionsAlgorithm::Options &opts)
138 : : GDALVectorPipelineOutputLayer(oSrcLayer), m_opts(opts),
139 7 : m_poFeatureDefn(oSrcLayer.GetLayerDefn()->Clone())
140 : {
141 7 : SetDescription(oSrcLayer.GetDescription());
142 7 : SetMetadata(oSrcLayer.GetMetadata());
143 7 : m_poFeatureDefn->Reference();
144 :
145 7 : 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 17 : for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
156 : {
157 10 : if (IsSelectedGeomField(i))
158 : {
159 8 : const auto poGeomFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(i);
160 8 : poGeomFieldDefn->SetType(
161 8 : !m_opts.m_type.empty()
162 : ? m_opts.m_eType
163 6 : : OGR_GT_GetSingle(poGeomFieldDefn->GetType()));
164 : }
165 : }
166 7 : }
167 :
168 : /************************************************************************/
169 : /* TranslateFeature() */
170 : /************************************************************************/
171 :
172 402 : void GDALVectorGeomExplodeCollectionsAlgorithmLayer::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 : /* GDALVectorGeomExplodeCollectionsAlgorithm::CreateAlgLayer() */
270 : /************************************************************************/
271 :
272 : std::unique_ptr<OGRLayerWithTranslateFeature>
273 7 : GDALVectorGeomExplodeCollectionsAlgorithm::CreateAlgLayer(OGRLayer &srcLayer)
274 : {
275 7 : return std::make_unique<GDALVectorGeomExplodeCollectionsAlgorithmLayer>(
276 7 : srcLayer, m_opts);
277 : }
278 :
279 : /************************************************************************/
280 : /* GDALVectorGeomExplodeCollectionsAlgorithm::RunStep() */
281 : /************************************************************************/
282 :
283 8 : bool GDALVectorGeomExplodeCollectionsAlgorithm::RunStep(GDALProgressFunc,
284 : void *)
285 : {
286 8 : 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 7 : return GDALVectorGeomAbstractAlgorithm::RunStep(nullptr, nullptr);
299 : }
300 :
301 : //! @endcond
|