Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: gdal "materialize" pipeline step
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_materialize.h"
14 : #include "gdal_utils.h"
15 : #include "gdal_priv.h"
16 : #include "ogrsf_frmts.h"
17 :
18 : //! @cond Doxygen_Suppress
19 :
20 : #ifndef _
21 : #define _(x) (x)
22 : #endif
23 :
24 : /************************************************************************/
25 : /* GDALMaterializeRasterAlgorithm() */
26 : /************************************************************************/
27 :
28 61 : GDALMaterializeRasterAlgorithm::GDALMaterializeRasterAlgorithm()
29 : : GDALMaterializeStepAlgorithm<GDALRasterPipelineStepAlgorithm,
30 61 : GDAL_OF_RASTER>(HELP_URL)
31 : {
32 61 : AddRasterHiddenInputDatasetArg();
33 :
34 : AddOutputDatasetArg(&m_outputDataset, GDAL_OF_RASTER,
35 : /* positionalAndRequired = */ false,
36 61 : _("Materialized dataset name"))
37 61 : .SetDatasetInputFlags(GADV_NAME);
38 :
39 61 : AddOutputFormatArg(&m_format)
40 : .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
41 : {GDAL_DCAP_RASTER, GDAL_DCAP_CREATECOPY,
42 366 : GDAL_DCAP_OPEN, GDAL_DMD_EXTENSIONS})
43 244 : .AddMetadataItem(GAAMDI_ALLOWED_FORMATS, {"MEM", "COG"})
44 122 : .AddMetadataItem(GAAMDI_EXCLUDED_FORMATS, {"VRT"});
45 :
46 61 : AddCreationOptionsArg(&m_creationOptions);
47 61 : AddOverwriteArg(&m_overwrite);
48 :
49 : AddArg(ARG_NAME_REOPEN_AND_DO_NOT_EARLY_DELETE, 0,
50 : _("Reopen after materialization and do not early deleted"),
51 122 : &m_reopenAndDoNotEarlyDelete)
52 61 : .SetHidden();
53 61 : }
54 :
55 : /************************************************************************/
56 : /* GDALMaterializeRasterAlgorithm::RunStep() */
57 : /************************************************************************/
58 :
59 19 : bool GDALMaterializeRasterAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
60 : {
61 19 : auto pfnProgress = ctxt.m_pfnProgress;
62 19 : auto pProgressData = ctxt.m_pProgressData;
63 :
64 19 : auto poSrcDS = m_inputDataset[0].GetDatasetRef();
65 19 : CPLAssert(poSrcDS);
66 19 : CPLAssert(!m_outputDataset.GetDatasetRef());
67 :
68 38 : std::string filename = m_outputDataset.GetName();
69 19 : if (m_format.empty())
70 : {
71 7 : if (filename.empty())
72 : {
73 3 : m_format = "GTiff";
74 : }
75 : else
76 : {
77 : const auto aosFormats =
78 : CPLStringList(GDALGetOutputDriversForDatasetName(
79 : filename.c_str(), GDAL_OF_RASTER,
80 : /* bSingleMatch = */ true,
81 4 : /* bWarn = */ true));
82 4 : if (aosFormats.size() != 1)
83 : {
84 1 : ReportError(CE_Failure, CPLE_AppDefined,
85 : "Cannot guess driver for %s", filename.c_str());
86 1 : return false;
87 : }
88 3 : m_format = aosFormats[0];
89 : }
90 : }
91 :
92 18 : auto poDrv = GetGDALDriverManager()->GetDriverByName(m_format.c_str());
93 18 : if (!poDrv)
94 : {
95 0 : ReportError(CE_Failure, CPLE_AppDefined, "Driver %s does not exist",
96 : m_format.c_str());
97 0 : return false;
98 : }
99 :
100 15 : const bool autoDeleteFile = !m_reopenAndDoNotEarlyDelete &&
101 33 : filename.empty() &&
102 5 : !EQUAL(m_format.c_str(), "MEM");
103 18 : if (filename.empty() && !EQUAL(m_format.c_str(), "MEM"))
104 : {
105 5 : filename = CPLGenerateTempFilenameSafe(nullptr);
106 :
107 5 : const char *pszExt = poDrv->GetMetadataItem(GDAL_DMD_EXTENSIONS);
108 5 : if (pszExt)
109 : {
110 5 : filename += '.';
111 5 : filename += CPLStringList(CSLTokenizeString(pszExt))[0];
112 : }
113 : }
114 :
115 36 : CPLStringList aosOptions(m_creationOptions);
116 18 : if (EQUAL(m_format.c_str(), "GTiff"))
117 : {
118 12 : if (aosOptions.FetchNameValue("TILED") == nullptr)
119 : {
120 12 : aosOptions.SetNameValue("TILED", "YES");
121 : }
122 12 : if (aosOptions.FetchNameValue("COPY_SRC_OVERVIEWS") == nullptr)
123 : {
124 5 : aosOptions.SetNameValue("COPY_SRC_OVERVIEWS", "YES");
125 : }
126 12 : if (aosOptions.FetchNameValue("COMPRESS") == nullptr)
127 : {
128 : const char *pszCOList =
129 12 : poDrv->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST);
130 : aosOptions.SetNameValue(
131 : "COMPRESS",
132 12 : pszCOList && strstr(pszCOList, "ZSTD") ? "ZSTD" : "DEFLATE");
133 : }
134 : }
135 :
136 18 : if (autoDeleteFile)
137 : {
138 4 : aosOptions.SetNameValue("@SUPPRESS_ASAP", "YES");
139 : }
140 :
141 : auto poOutDS = std::unique_ptr<GDALDataset>(
142 18 : poDrv->CreateCopy(filename.c_str(), poSrcDS, false, aosOptions.List(),
143 18 : pfnProgress, pProgressData));
144 18 : bool ok = poOutDS != nullptr && poOutDS->FlushCache() == CE_None;
145 18 : if (poOutDS)
146 : {
147 33 : if (m_reopenAndDoNotEarlyDelete ||
148 15 : poDrv->GetMetadataItem(GDAL_DCAP_REOPEN_AFTER_WRITE_REQUIRED))
149 : {
150 3 : ok = poOutDS->Close() == CE_None;
151 3 : poOutDS.reset();
152 3 : if (ok)
153 : {
154 6 : std::string reopenFormat = m_format;
155 3 : if (m_format == "COG")
156 1 : reopenFormat = "GTiff";
157 3 : const char *const apszAllowedDrivers[] = {reopenFormat.c_str(),
158 3 : nullptr};
159 3 : poOutDS.reset(GDALDataset::Open(
160 : filename.c_str(), GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
161 : apszAllowedDrivers));
162 3 : ok = poOutDS != nullptr;
163 : }
164 : }
165 18 : if (ok)
166 : {
167 18 : if (autoDeleteFile)
168 : {
169 : #if !defined(_WIN32)
170 4 : if (poDrv->GetMetadataItem(GDAL_DCAP_CAN_READ_AFTER_DELETE))
171 : {
172 4 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
173 4 : poDrv->Delete(poOutDS.get(),
174 8 : CPLStringList(poOutDS->GetFileList()).List());
175 : }
176 : #endif
177 4 : poOutDS->MarkSuppressOnClose();
178 : }
179 :
180 18 : m_outputDataset.Set(std::move(poOutDS));
181 : }
182 : }
183 18 : return ok;
184 : }
185 :
186 : /************************************************************************/
187 : /* GDALMaterializeVectorAlgorithm() */
188 : /************************************************************************/
189 :
190 56 : GDALMaterializeVectorAlgorithm::GDALMaterializeVectorAlgorithm()
191 : : GDALMaterializeStepAlgorithm<GDALVectorPipelineStepAlgorithm,
192 56 : GDAL_OF_VECTOR>(HELP_URL)
193 : {
194 56 : AddVectorHiddenInputDatasetArg();
195 :
196 : AddOutputDatasetArg(&m_outputDataset, GDAL_OF_VECTOR,
197 : /* positionalAndRequired = */ false,
198 56 : _("Materialized dataset name"))
199 56 : .SetDatasetInputFlags(GADV_NAME);
200 :
201 56 : AddOutputFormatArg(&m_format)
202 : .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
203 : {GDAL_DCAP_VECTOR, GDAL_DCAP_CREATE, GDAL_DCAP_OPEN,
204 336 : GDAL_DMD_EXTENSIONS})
205 168 : .AddMetadataItem(GAAMDI_ALLOWED_FORMATS, {"MEM"})
206 : .AddMetadataItem(GAAMDI_EXCLUDED_FORMATS,
207 280 : {"MBTiles", "MVT", "PMTiles", "JP2ECW"});
208 :
209 56 : AddCreationOptionsArg(&m_creationOptions);
210 56 : AddLayerCreationOptionsArg(&m_layerCreationOptions);
211 56 : AddOverwriteArg(&m_overwrite);
212 56 : }
213 :
214 : /************************************************************************/
215 : /* GDALMaterializeVectorAlgorithm::RunStep() */
216 : /************************************************************************/
217 :
218 18 : bool GDALMaterializeVectorAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
219 : {
220 18 : auto pfnProgress = ctxt.m_pfnProgress;
221 18 : auto pProgressData = ctxt.m_pProgressData;
222 :
223 18 : auto poSrcDS = m_inputDataset[0].GetDatasetRef();
224 18 : CPLAssert(poSrcDS);
225 18 : CPLAssert(!m_outputDataset.GetDatasetRef());
226 :
227 36 : std::string filename = m_outputDataset.GetName();
228 18 : if (m_format.empty())
229 : {
230 8 : if (filename.empty())
231 : {
232 4 : bool bSeveralGeomFields = false;
233 8 : for (const auto *poLayer : poSrcDS->GetLayers())
234 : {
235 4 : if (!bSeveralGeomFields)
236 4 : bSeveralGeomFields =
237 4 : poLayer->GetLayerDefn()->GetGeomFieldCount() > 1;
238 7 : if (!bSeveralGeomFields &&
239 3 : poLayer->GetLayerDefn()->GetGeomFieldCount() > 0)
240 : {
241 7 : for (const auto *poFieldDefn :
242 17 : poLayer->GetLayerDefn()->GetFields())
243 : {
244 7 : const auto eType = poFieldDefn->GetType();
245 7 : if (eType == OFTStringList || eType == OFTIntegerList ||
246 6 : eType == OFTRealList || eType == OFTInteger64List)
247 : {
248 1 : bSeveralGeomFields = true;
249 : }
250 : }
251 : }
252 : }
253 4 : m_format = bSeveralGeomFields ? "SQLite" : "GPKG";
254 : }
255 : else
256 : {
257 : const auto aosFormats =
258 : CPLStringList(GDALGetOutputDriversForDatasetName(
259 : filename.c_str(), GDAL_OF_VECTOR,
260 : /* bSingleMatch = */ true,
261 4 : /* bWarn = */ true));
262 4 : if (aosFormats.size() != 1)
263 : {
264 1 : ReportError(CE_Failure, CPLE_AppDefined,
265 : "Cannot guess driver for %s", filename.c_str());
266 1 : return false;
267 : }
268 3 : m_format = aosFormats[0];
269 : }
270 : }
271 :
272 17 : auto poDrv = GetGDALDriverManager()->GetDriverByName(m_format.c_str());
273 17 : if (!poDrv)
274 : {
275 0 : ReportError(CE_Failure, CPLE_AppDefined, "Driver %s does not exist",
276 : m_format.c_str());
277 0 : return false;
278 : }
279 :
280 17 : const bool autoDeleteFile = !m_reopenAndDoNotEarlyDelete &&
281 34 : filename.empty() &&
282 9 : !EQUAL(m_format.c_str(), "MEM");
283 17 : if (filename.empty() && !EQUAL(m_format.c_str(), "MEM"))
284 : {
285 8 : filename = CPLGenerateTempFilenameSafe(nullptr);
286 :
287 8 : const char *pszExt = poDrv->GetMetadataItem(GDAL_DMD_EXTENSIONS);
288 8 : if (pszExt)
289 : {
290 8 : filename += '.';
291 8 : filename += CPLStringList(CSLTokenizeString(pszExt))[0];
292 : }
293 : }
294 :
295 34 : CPLStringList aosOptions;
296 17 : aosOptions.AddString("--invoked-from-gdal-algorithm");
297 17 : if (!m_overwrite)
298 : {
299 17 : aosOptions.AddString("--no-overwrite");
300 : }
301 :
302 17 : aosOptions.AddString("-of");
303 17 : aosOptions.AddString(m_format.c_str());
304 18 : for (const auto &co : m_creationOptions)
305 : {
306 1 : aosOptions.AddString("-dsco");
307 1 : aosOptions.AddString(co.c_str());
308 : }
309 34 : CPLStringList aosReopenOpenOptions;
310 17 : if (EQUAL(m_format.c_str(), "SQLite"))
311 : {
312 : const char *pszCOList =
313 3 : poDrv->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST);
314 6 : if (pszCOList && strstr(pszCOList, "SPATIALITE") &&
315 6 : CPLStringList(m_creationOptions).FetchNameValue("SPATIALITE") ==
316 : nullptr)
317 : {
318 2 : aosOptions.AddString("-dsco");
319 2 : aosOptions.AddString("SPATIALITE=YES");
320 : }
321 3 : aosReopenOpenOptions.AddString("LIST_ALL_TABLES=YES");
322 : }
323 18 : for (const auto &co : m_layerCreationOptions)
324 : {
325 1 : aosOptions.AddString("-lco");
326 1 : aosOptions.AddString(co.c_str());
327 : }
328 17 : if (pfnProgress && pfnProgress != GDALDummyProgress)
329 : {
330 1 : aosOptions.AddString("-progress");
331 : }
332 :
333 17 : if (autoDeleteFile)
334 : {
335 8 : aosOptions.AddString("-dsco");
336 8 : aosOptions.AddString("@SUPPRESS_ASAP=YES");
337 : }
338 :
339 : GDALVectorTranslateOptions *psOptions =
340 17 : GDALVectorTranslateOptionsNew(aosOptions.List(), nullptr);
341 17 : GDALVectorTranslateOptionsSetProgress(psOptions, pfnProgress,
342 : pProgressData);
343 :
344 17 : GDALDatasetH hSrcDS = GDALDataset::ToHandle(poSrcDS);
345 : auto poOutDS = std::unique_ptr<GDALDataset>(
346 : GDALDataset::FromHandle(GDALVectorTranslate(
347 17 : filename.c_str(), nullptr, 1, &hSrcDS, psOptions, nullptr)));
348 17 : GDALVectorTranslateOptionsFree(psOptions);
349 :
350 17 : bool ok = poOutDS != nullptr && poOutDS->FlushCache() == CE_None;
351 17 : if (poOutDS)
352 : {
353 34 : if (m_reopenAndDoNotEarlyDelete ||
354 17 : poDrv->GetMetadataItem(GDAL_DCAP_REOPEN_AFTER_WRITE_REQUIRED))
355 : {
356 2 : ok = poOutDS->Close() == CE_None;
357 2 : poOutDS.reset();
358 2 : if (ok)
359 : {
360 2 : const char *const apszAllowedDrivers[] = {m_format.c_str(),
361 2 : nullptr};
362 2 : poOutDS.reset(GDALDataset::Open(
363 : filename.c_str(), GDAL_OF_VECTOR | GDAL_OF_VERBOSE_ERROR,
364 2 : apszAllowedDrivers, aosReopenOpenOptions.List()));
365 2 : ok = poOutDS != nullptr;
366 : }
367 : }
368 17 : if (ok)
369 : {
370 17 : if (autoDeleteFile)
371 : {
372 : #if !defined(_WIN32)
373 8 : if (poDrv->GetMetadataItem(GDAL_DCAP_CAN_READ_AFTER_DELETE))
374 : {
375 7 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
376 7 : poDrv->Delete(poOutDS.get(),
377 14 : CPLStringList(poOutDS->GetFileList()).List());
378 : }
379 : #endif
380 8 : poOutDS->MarkSuppressOnClose();
381 : }
382 :
383 17 : m_outputDataset.Set(std::move(poOutDS));
384 : }
385 : }
386 17 : return ok;
387 : }
388 :
389 : //! @endcond
|