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