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