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