Coverage Report

Created: 2026-02-14 06:52

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