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 : void 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 : }
120 :
121 : public:
122 20 : explicit GDALVectorRenameLayerAlgorithmLayer(
123 : OGRLayer &oSrcLayer, const std::string &osOutputLayerName)
124 20 : : GDALVectorPipelineOutputLayer(oSrcLayer),
125 20 : m_poFeatureDefn(oSrcLayer.GetLayerDefn()->Clone())
126 : {
127 20 : m_poFeatureDefn->SetName(osOutputLayerName.c_str());
128 20 : const int nGeomFieldCount = m_poFeatureDefn->GetGeomFieldCount();
129 20 : const auto poSrcLayerDefn = oSrcLayer.GetLayerDefn();
130 39 : for (int i = 0; i < nGeomFieldCount; ++i)
131 : {
132 38 : m_poFeatureDefn->GetGeomFieldDefn(i)->SetSpatialRef(
133 19 : poSrcLayerDefn->GetGeomFieldDefn(i)->GetSpatialRef());
134 : }
135 20 : SetDescription(m_poFeatureDefn->GetName());
136 20 : SetMetadata(oSrcLayer.GetMetadata());
137 20 : }
138 :
139 311 : const OGRFeatureDefn *GetLayerDefn() const override
140 : {
141 311 : return m_poFeatureDefn.get();
142 : }
143 :
144 13 : GIntBig GetFeatureCount(int bForce) override
145 : {
146 13 : return m_srcLayer.GetFeatureCount(bForce);
147 : }
148 :
149 4 : OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
150 : bool bForce) override
151 : {
152 4 : return m_srcLayer.GetExtent(iGeomField, psExtent, bForce);
153 : }
154 :
155 14 : OGRErr SetIgnoredFields(CSLConstList papszFields) override
156 : {
157 14 : return m_srcLayer.SetIgnoredFields(papszFields);
158 : }
159 :
160 34 : OGRErr SetAttributeFilter(const char *pszAttributeFilter) override
161 : {
162 34 : OGRLayer::SetAttributeFilter(pszAttributeFilter);
163 34 : return m_srcLayer.SetAttributeFilter(pszAttributeFilter);
164 : }
165 :
166 1 : OGRGeometry *GetSpatialFilter() override
167 : {
168 1 : return m_srcLayer.GetSpatialFilter();
169 : }
170 :
171 26 : OGRErr ISetSpatialFilter(int iGeomField, const OGRGeometry *poGeom) override
172 : {
173 26 : return m_srcLayer.SetSpatialFilter(iGeomField, poGeom);
174 : }
175 :
176 8 : OGRFeature *GetFeature(GIntBig nFID) override
177 : {
178 : auto poSrcFeature =
179 16 : std::unique_ptr<OGRFeature>(m_srcLayer.GetFeature(nFID));
180 8 : if (!poSrcFeature)
181 3 : return nullptr;
182 5 : poSrcFeature->SetFDefnUnsafe(m_poFeatureDefn.get());
183 5 : return poSrcFeature.release();
184 : }
185 :
186 48 : int TestCapability(const char *pszCap) const override
187 : {
188 48 : return m_srcLayer.TestCapability(pszCap);
189 : }
190 : };
191 :
192 : /************************************************************************/
193 : /* GDALVectorRenameLayerAlgorithmDataset */
194 : /************************************************************************/
195 :
196 : class GDALVectorRenameLayerAlgorithmDataset final
197 : : public GDALVectorPipelineOutputDataset
198 : {
199 : public:
200 17 : GDALVectorRenameLayerAlgorithmDataset(
201 : GDALDataset &oSrcDS, const std::vector<std::string> &aosNewLayerNames)
202 17 : : GDALVectorPipelineOutputDataset(oSrcDS)
203 : {
204 17 : const int nLayerCount = oSrcDS.GetLayerCount();
205 17 : CPLAssert(aosNewLayerNames.size() == static_cast<size_t>(nLayerCount));
206 52 : for (int i = 0; i < nLayerCount; ++i)
207 : {
208 70 : m_mapOldLayerNameToNew[oSrcDS.GetLayer(i)->GetName()] =
209 70 : aosNewLayerNames[i];
210 : }
211 17 : }
212 :
213 : const GDALRelationship *
214 : GetRelationship(const std::string &name) const override;
215 :
216 : private:
217 : std::map<std::string, std::string> m_mapOldLayerNameToNew{};
218 : mutable std::map<std::string, std::unique_ptr<GDALRelationship>>
219 : m_relationships{};
220 : };
221 :
222 : /************************************************************************/
223 : /* GetRelationship() */
224 : /************************************************************************/
225 :
226 6 : const GDALRelationship *GDALVectorRenameLayerAlgorithmDataset::GetRelationship(
227 : const std::string &name) const
228 : {
229 6 : const auto oIterRelationships = m_relationships.find(name);
230 6 : if (oIterRelationships != m_relationships.end())
231 1 : return oIterRelationships->second.get();
232 :
233 5 : const GDALRelationship *poSrcRelationShip = m_srcDS.GetRelationship(name);
234 5 : if (!poSrcRelationShip)
235 1 : return nullptr;
236 : const auto oIterLeftTableName =
237 4 : m_mapOldLayerNameToNew.find(poSrcRelationShip->GetLeftTableName());
238 : const auto oIterRightTableName =
239 4 : m_mapOldLayerNameToNew.find(poSrcRelationShip->GetRightTableName());
240 : const auto oIterMappingTableName =
241 4 : m_mapOldLayerNameToNew.find(poSrcRelationShip->GetMappingTableName());
242 4 : if (oIterLeftTableName == m_mapOldLayerNameToNew.end() &&
243 4 : oIterRightTableName == m_mapOldLayerNameToNew.end() &&
244 4 : oIterMappingTableName == m_mapOldLayerNameToNew.end())
245 : {
246 0 : return poSrcRelationShip;
247 : }
248 :
249 : auto poNewRelationship =
250 4 : std::make_unique<GDALRelationship>(*poSrcRelationShip);
251 4 : if (oIterLeftTableName != m_mapOldLayerNameToNew.end())
252 4 : poNewRelationship->SetLeftTableName(oIterLeftTableName->second);
253 4 : if (oIterRightTableName != m_mapOldLayerNameToNew.end())
254 4 : poNewRelationship->SetRightTableName(oIterRightTableName->second);
255 4 : if (oIterMappingTableName != m_mapOldLayerNameToNew.end())
256 4 : poNewRelationship->SetMappingTableName(oIterMappingTableName->second);
257 :
258 8 : return m_relationships.insert({name, std::move(poNewRelationship)})
259 4 : .first->second.get();
260 : }
261 :
262 : } // namespace
263 :
264 : /************************************************************************/
265 : /* TruncateUTF8ToMaxChar() */
266 : /************************************************************************/
267 :
268 7 : static void TruncateUTF8ToMaxChar(std::string &osStr, size_t maxCharCount)
269 : {
270 7 : size_t nCharacterCount = 0;
271 28 : for (size_t i = 0; i < osStr.size(); ++i)
272 : {
273 : // Is it first byte of a UTF-8 character?
274 28 : if ((osStr[i] & 0xc0) != 0x80)
275 : {
276 23 : ++nCharacterCount;
277 23 : if (nCharacterCount == maxCharCount)
278 : {
279 7 : osStr.resize(i + 1);
280 7 : break;
281 : }
282 : }
283 : }
284 7 : }
285 :
286 : /************************************************************************/
287 : /* GDALVectorRenameLayerAlgorithm::RunStep() */
288 : /************************************************************************/
289 :
290 17 : bool GDALVectorRenameLayerAlgorithm::RunStep(GDALPipelineStepRunContext &)
291 : {
292 17 : auto poSrcDS = m_inputDataset[0].GetDatasetRef();
293 17 : CPLAssert(poSrcDS);
294 :
295 17 : CPLAssert(m_outputDataset.GetName().empty());
296 17 : CPLAssert(!m_outputDataset.GetDatasetRef());
297 :
298 : // First pass over layer names to create new layer names matching specified
299 : // constraints
300 34 : std::vector<std::string> aosNames;
301 34 : std::map<std::string, int> oMapCountNames;
302 17 : bool bNonUniqueNames = false;
303 17 : const int nLayerCount = poSrcDS->GetLayerCount();
304 52 : for (int i = 0; i < nLayerCount; ++i)
305 : {
306 35 : const OGRLayer *poSrcLayer = poSrcDS->GetLayer(i);
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 52 : for (int i = 0; i < nLayerCount; ++i)
413 : {
414 35 : OGRLayer *poSrcLayer = poSrcDS->GetLayer(i);
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 : }
430 :
431 17 : m_outputDataset.Set(std::move(outDS));
432 :
433 34 : return true;
434 : }
435 :
436 : GDALVectorRenameLayerAlgorithmStandalone::
437 : ~GDALVectorRenameLayerAlgorithmStandalone() = default;
438 :
439 : //! @endcond
|