Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: "rename-layer" step of "vector pipeline"
5 : * Author: Even Rouault <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2026, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "gdalalg_vector_rename_layer.h"
14 :
15 : //! @cond Doxygen_Suppress
16 :
17 : #include <map>
18 :
19 : #include "cpl_string.h"
20 :
21 : #ifndef _
22 : #define _(x) (x)
23 : #endif
24 :
25 : /************************************************************************/
26 : /* GDALVectorRenameLayerAlgorithm::GDALVectorRenameLayerAlgorithm() */
27 : /************************************************************************/
28 :
29 97 : GDALVectorRenameLayerAlgorithm::GDALVectorRenameLayerAlgorithm(
30 97 : bool standaloneStep)
31 : : GDALVectorPipelineStepAlgorithm(
32 : NAME, DESCRIPTION, HELP_URL,
33 0 : ConstructorOptions()
34 97 : .SetStandaloneStep(standaloneStep)
35 97 : .SetAddInputLayerNameArgument(false)
36 194 : .SetOutputLayerNameAvailableInPipelineStep(true))
37 : {
38 97 : AddLayerNameArg(&m_inputLayerName);
39 97 : if (!standaloneStep)
40 : {
41 37 : AddOutputLayerNameArg(/* hiddenForCLI = */ false,
42 : /* shortNameOutputLayerAllowed = */ false);
43 : }
44 97 : AddArg("ascii", 0, _("Force names to ASCII character"), &m_ascii);
45 : AddArg("lower-case", 0,
46 : _("Force names to lower case (only on ASCII characters)"),
47 97 : &m_lowerCase);
48 : AddArg("filename-compatible", 0, _("Force names to be usable as filenames"),
49 97 : &m_filenameCompatible);
50 : AddArg("reserved-characters", 0, _("Reserved character(s) to be removed"),
51 97 : &m_reservedChars);
52 : AddArg("replacement-character", 0,
53 : _("Replacement character when ASCII conversion not possible"),
54 194 : &m_replacementChar)
55 97 : .SetMaxCharCount(1);
56 194 : AddArg("max-length", 0, _("Maximum length of layer names"), &m_maxLength)
57 97 : .SetMinValueIncluded(1);
58 :
59 97 : AddValidationAction(
60 156 : [this]()
61 : {
62 24 : if (!m_inputLayerName.empty() && m_outputLayerName.empty())
63 : {
64 1 : ReportError(CE_Failure, CPLE_AppDefined,
65 : "Argument output-layer must be specified when "
66 : "input-layer is specified");
67 1 : return false;
68 : }
69 :
70 23 : if (!m_inputDataset.empty() && m_inputDataset[0].GetDatasetRef())
71 : {
72 21 : auto poSrcDS = m_inputDataset[0].GetDatasetRef();
73 26 : if (!m_inputLayerName.empty() &&
74 5 : poSrcDS->GetLayerByName(m_inputLayerName.c_str()) ==
75 : nullptr)
76 : {
77 1 : ReportError(CE_Failure, CPLE_AppDefined,
78 : "Input layer '%s' does not exist",
79 : m_inputLayerName.c_str());
80 1 : return false;
81 : }
82 :
83 27 : if (!m_outputLayerName.empty() && m_inputLayerName.empty() &&
84 7 : poSrcDS->GetLayerCount() >= 2)
85 : {
86 1 : ReportError(CE_Failure, CPLE_AppDefined,
87 : "Argument input-layer must be specified when "
88 : "output-layer is specified and there is more "
89 : "than one layer");
90 1 : return false;
91 : }
92 : }
93 :
94 21 : return true;
95 : });
96 97 : }
97 :
98 : namespace
99 : {
100 :
101 : /************************************************************************/
102 : /* GDALVectorRenameLayerAlgorithmLayer */
103 : /************************************************************************/
104 :
105 : class GDALVectorRenameLayerAlgorithmLayer final
106 : : public GDALVectorPipelineOutputLayer
107 : {
108 : private:
109 : const OGRFeatureDefnRefCountedPtr m_poFeatureDefn;
110 :
111 : CPL_DISALLOW_COPY_ASSIGN(GDALVectorRenameLayerAlgorithmLayer)
112 :
113 195 : bool TranslateFeature(
114 : std::unique_ptr<OGRFeature> poSrcFeature,
115 : std::vector<std::unique_ptr<OGRFeature>> &apoOutFeatures) override
116 : {
117 195 : poSrcFeature->SetFDefnUnsafe(m_poFeatureDefn.get());
118 195 : apoOutFeatures.push_back(std::move(poSrcFeature));
119 195 : return true;
120 : }
121 :
122 : public:
123 20 : explicit GDALVectorRenameLayerAlgorithmLayer(
124 : OGRLayer &oSrcLayer, const std::string &osOutputLayerName)
125 20 : : GDALVectorPipelineOutputLayer(oSrcLayer),
126 20 : m_poFeatureDefn(oSrcLayer.GetLayerDefn()->Clone())
127 : {
128 20 : m_poFeatureDefn->SetName(osOutputLayerName.c_str());
129 20 : const int nGeomFieldCount = m_poFeatureDefn->GetGeomFieldCount();
130 20 : const auto poSrcLayerDefn = oSrcLayer.GetLayerDefn();
131 39 : for (int i = 0; i < nGeomFieldCount; ++i)
132 : {
133 38 : m_poFeatureDefn->GetGeomFieldDefn(i)->SetSpatialRef(
134 19 : poSrcLayerDefn->GetGeomFieldDefn(i)->GetSpatialRef());
135 : }
136 20 : SetDescription(m_poFeatureDefn->GetName());
137 20 : SetMetadata(oSrcLayer.GetMetadata());
138 20 : }
139 :
140 311 : const OGRFeatureDefn *GetLayerDefn() const override
141 : {
142 311 : return m_poFeatureDefn.get();
143 : }
144 :
145 13 : GIntBig GetFeatureCount(int bForce) override
146 : {
147 13 : return m_srcLayer.GetFeatureCount(bForce);
148 : }
149 :
150 4 : OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
151 : bool bForce) override
152 : {
153 4 : return m_srcLayer.GetExtent(iGeomField, psExtent, bForce);
154 : }
155 :
156 14 : OGRErr SetIgnoredFields(CSLConstList papszFields) override
157 : {
158 14 : return m_srcLayer.SetIgnoredFields(papszFields);
159 : }
160 :
161 34 : OGRErr SetAttributeFilter(const char *pszAttributeFilter) override
162 : {
163 34 : OGRLayer::SetAttributeFilter(pszAttributeFilter);
164 34 : return m_srcLayer.SetAttributeFilter(pszAttributeFilter);
165 : }
166 :
167 1 : OGRGeometry *GetSpatialFilter() override
168 : {
169 1 : return m_srcLayer.GetSpatialFilter();
170 : }
171 :
172 26 : OGRErr ISetSpatialFilter(int iGeomField, const OGRGeometry *poGeom) override
173 : {
174 26 : return m_srcLayer.SetSpatialFilter(iGeomField, poGeom);
175 : }
176 :
177 8 : OGRFeature *GetFeature(GIntBig nFID) override
178 : {
179 : auto poSrcFeature =
180 16 : std::unique_ptr<OGRFeature>(m_srcLayer.GetFeature(nFID));
181 8 : if (!poSrcFeature)
182 3 : return nullptr;
183 5 : poSrcFeature->SetFDefnUnsafe(m_poFeatureDefn.get());
184 5 : return poSrcFeature.release();
185 : }
186 :
187 48 : int TestCapability(const char *pszCap) const override
188 : {
189 48 : return m_srcLayer.TestCapability(pszCap);
190 : }
191 : };
192 :
193 : /************************************************************************/
194 : /* GDALVectorRenameLayerAlgorithmDataset */
195 : /************************************************************************/
196 :
197 : class GDALVectorRenameLayerAlgorithmDataset final
198 : : public GDALVectorPipelineOutputDataset
199 : {
200 : public:
201 17 : GDALVectorRenameLayerAlgorithmDataset(
202 : GDALDataset &oSrcDS, const std::vector<std::string> &aosNewLayerNames)
203 17 : : GDALVectorPipelineOutputDataset(oSrcDS)
204 : {
205 17 : const int nLayerCount = oSrcDS.GetLayerCount();
206 17 : CPLAssert(aosNewLayerNames.size() == static_cast<size_t>(nLayerCount));
207 52 : for (int i = 0; i < nLayerCount; ++i)
208 : {
209 70 : m_mapOldLayerNameToNew[oSrcDS.GetLayer(i)->GetName()] =
210 70 : aosNewLayerNames[i];
211 : }
212 17 : }
213 :
214 : const GDALRelationship *
215 : GetRelationship(const std::string &name) const override;
216 :
217 : private:
218 : std::map<std::string, std::string> m_mapOldLayerNameToNew{};
219 : mutable std::map<std::string, std::unique_ptr<GDALRelationship>>
220 : m_relationships{};
221 : };
222 :
223 : /************************************************************************/
224 : /* GetRelationship() */
225 : /************************************************************************/
226 :
227 6 : const GDALRelationship *GDALVectorRenameLayerAlgorithmDataset::GetRelationship(
228 : const std::string &name) const
229 : {
230 6 : const auto oIterRelationships = m_relationships.find(name);
231 6 : if (oIterRelationships != m_relationships.end())
232 1 : return oIterRelationships->second.get();
233 :
234 5 : const GDALRelationship *poSrcRelationShip = m_srcDS.GetRelationship(name);
235 5 : if (!poSrcRelationShip)
236 1 : return nullptr;
237 : const auto oIterLeftTableName =
238 4 : m_mapOldLayerNameToNew.find(poSrcRelationShip->GetLeftTableName());
239 : const auto oIterRightTableName =
240 4 : m_mapOldLayerNameToNew.find(poSrcRelationShip->GetRightTableName());
241 : const auto oIterMappingTableName =
242 4 : m_mapOldLayerNameToNew.find(poSrcRelationShip->GetMappingTableName());
243 4 : if (oIterLeftTableName == m_mapOldLayerNameToNew.end() &&
244 4 : oIterRightTableName == m_mapOldLayerNameToNew.end() &&
245 4 : oIterMappingTableName == m_mapOldLayerNameToNew.end())
246 : {
247 0 : return poSrcRelationShip;
248 : }
249 :
250 : auto poNewRelationship =
251 4 : std::make_unique<GDALRelationship>(*poSrcRelationShip);
252 4 : if (oIterLeftTableName != m_mapOldLayerNameToNew.end())
253 4 : poNewRelationship->SetLeftTableName(oIterLeftTableName->second);
254 4 : if (oIterRightTableName != m_mapOldLayerNameToNew.end())
255 4 : poNewRelationship->SetRightTableName(oIterRightTableName->second);
256 4 : if (oIterMappingTableName != m_mapOldLayerNameToNew.end())
257 4 : poNewRelationship->SetMappingTableName(oIterMappingTableName->second);
258 :
259 8 : return m_relationships.insert({name, std::move(poNewRelationship)})
260 4 : .first->second.get();
261 : }
262 :
263 : } // namespace
264 :
265 : /************************************************************************/
266 : /* TruncateUTF8ToMaxChar() */
267 : /************************************************************************/
268 :
269 7 : static void TruncateUTF8ToMaxChar(std::string &osStr, size_t maxCharCount)
270 : {
271 7 : size_t nCharacterCount = 0;
272 28 : for (size_t i = 0; i < osStr.size(); ++i)
273 : {
274 : // Is it first byte of a UTF-8 character?
275 28 : if ((osStr[i] & 0xc0) != 0x80)
276 : {
277 23 : ++nCharacterCount;
278 23 : if (nCharacterCount == maxCharCount)
279 : {
280 7 : osStr.resize(i + 1);
281 7 : break;
282 : }
283 : }
284 : }
285 7 : }
286 :
287 : /************************************************************************/
288 : /* GDALVectorRenameLayerAlgorithm::RunStep() */
289 : /************************************************************************/
290 :
291 17 : bool GDALVectorRenameLayerAlgorithm::RunStep(GDALPipelineStepRunContext &)
292 : {
293 17 : auto poSrcDS = m_inputDataset[0].GetDatasetRef();
294 17 : CPLAssert(poSrcDS);
295 :
296 17 : CPLAssert(m_outputDataset.GetName().empty());
297 17 : CPLAssert(!m_outputDataset.GetDatasetRef());
298 :
299 : // First pass over layer names to create new layer names matching specified
300 : // constraints
301 34 : std::vector<std::string> aosNames;
302 34 : std::map<std::string, int> oMapCountNames;
303 17 : bool bNonUniqueNames = false;
304 17 : const int nLayerCount = poSrcDS->GetLayerCount();
305 52 : for (const OGRLayer *poSrcLayer : poSrcDS->GetLayers())
306 : {
307 66 : if ((m_inputLayerName == poSrcLayer->GetDescription() ||
308 70 : nLayerCount == 1) &&
309 14 : !m_outputLayerName.empty())
310 : {
311 8 : aosNames.push_back(m_outputLayerName);
312 : }
313 : else
314 : {
315 54 : std::string osName(poSrcLayer->GetDescription());
316 27 : if (!m_reservedChars.empty())
317 : {
318 2 : std::string osNewName;
319 11 : for (char c : osName)
320 : {
321 10 : if (m_reservedChars.find(c) != std::string::npos)
322 : {
323 1 : if (!m_replacementChar.empty())
324 1 : osNewName += m_replacementChar;
325 : }
326 : else
327 : {
328 9 : osNewName += c;
329 : }
330 : }
331 1 : osName = std::move(osNewName);
332 : }
333 27 : if (m_filenameCompatible)
334 : {
335 1 : osName = CPLLaunderForFilenameSafe(
336 1 : osName, m_replacementChar.c_str()[0]);
337 : }
338 27 : if (m_ascii)
339 : {
340 2 : char *pszStr = CPLUTF8ForceToASCII(
341 2 : osName.c_str(), m_replacementChar.c_str()[0]);
342 2 : osName = pszStr;
343 2 : CPLFree(pszStr);
344 : }
345 27 : if (m_lowerCase)
346 : {
347 10 : for (char &c : osName)
348 : {
349 9 : if (c >= 'A' && c <= 'Z')
350 9 : c = c - 'A' + 'a';
351 : }
352 : }
353 27 : if (m_maxLength > 0)
354 : {
355 5 : TruncateUTF8ToMaxChar(osName, m_maxLength);
356 : }
357 27 : if (++oMapCountNames[osName] > 1)
358 3 : bNonUniqueNames = true;
359 27 : aosNames.push_back(std::move(osName));
360 : }
361 : }
362 :
363 : // Extra optional pass if some names are not unique
364 17 : if (bNonUniqueNames)
365 : {
366 6 : std::map<std::string, int> oMapCurCounter;
367 3 : bool bUniquenessPossible = true;
368 9 : for (auto &osName : aosNames)
369 : {
370 6 : const int nCountForName = oMapCountNames[osName];
371 6 : if (nCountForName > 1)
372 : {
373 6 : const int nCounter = ++oMapCurCounter[osName];
374 12 : std::string osSuffix("_");
375 6 : if (nCountForName <= 9)
376 6 : osSuffix += CPLSPrintf("%d", nCounter);
377 0 : else if (nCountForName <= 99)
378 0 : osSuffix += CPLSPrintf("%02d", nCounter);
379 : else
380 0 : osSuffix += CPLSPrintf("%03d", nCounter);
381 6 : const size_t nNameLen = CPLStrlenUTF8Ex(osName.c_str());
382 10 : if (m_maxLength > 0 && nNameLen + osSuffix.size() >
383 4 : static_cast<size_t>(m_maxLength))
384 : {
385 4 : if (nNameLen > osSuffix.size())
386 : {
387 2 : TruncateUTF8ToMaxChar(osName,
388 2 : nNameLen - osSuffix.size());
389 2 : osName += osSuffix;
390 : }
391 2 : else if (bUniquenessPossible)
392 : {
393 1 : ReportError(CE_Warning, CPLE_AppDefined,
394 : "Cannot create unique name for '%s' while "
395 : "respecting %d maximum length",
396 : osName.c_str(), m_maxLength);
397 1 : bUniquenessPossible = false;
398 : }
399 : }
400 : else
401 : {
402 2 : osName += osSuffix;
403 : }
404 : }
405 : }
406 : }
407 :
408 : auto outDS = std::make_unique<GDALVectorRenameLayerAlgorithmDataset>(
409 17 : *poSrcDS, aosNames);
410 :
411 : // Final pass to create output layers
412 17 : size_t i = 0;
413 52 : for (OGRLayer *poSrcLayer : poSrcDS->GetLayers())
414 : {
415 35 : if (poSrcLayer->GetDescription() != aosNames[i])
416 : {
417 : auto poLayer =
418 : std::make_unique<GDALVectorRenameLayerAlgorithmLayer>(
419 20 : *poSrcLayer, aosNames[i]);
420 20 : outDS->AddLayer(*poSrcLayer, std::move(poLayer));
421 : }
422 : else
423 : {
424 30 : outDS->AddLayer(
425 : *poSrcLayer,
426 30 : std::make_unique<GDALVectorPipelinePassthroughLayer>(
427 : *poSrcLayer));
428 : }
429 35 : ++i;
430 : }
431 :
432 17 : m_outputDataset.Set(std::move(outDS));
433 :
434 34 : return true;
435 : }
436 :
437 : GDALVectorRenameLayerAlgorithmStandalone::
438 : ~GDALVectorRenameLayerAlgorithmStandalone() = default;
439 :
440 : //! @endcond
|