Coverage Report

Created: 2026-08-11 08:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/gdalg/gdalgdriver.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  GDAL Algorithm driver
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 "cpl_json.h"
14
#include "cpl_string.h"
15
16
#include "gdalalgorithm.h"
17
#include "gdal_frmts.h"
18
#include "gdal_proxy.h"
19
#include "gdal_priv.h"
20
21
/************************************************************************/
22
/*                             GDALGDataset                             */
23
/************************************************************************/
24
25
class GDALGDataset final : public GDALProxyDataset
26
{
27
  public:
28
    GDALGDataset(const std::string &filename,
29
                 std::unique_ptr<GDALAlgorithm> poAlg, GDALDataset *poDS);
30
31
    char **GetFileList(void) override
32
0
    {
33
0
        CPLStringList aosList;
34
0
        if (!m_filename.empty())
35
0
            aosList.push_back(m_filename);
36
0
        return aosList.StealList();
37
0
    }
38
39
    static int Identify(GDALOpenInfo *poOpenInfo);
40
    static GDALDataset *Open(GDALOpenInfo *poOpenInfo);
41
42
  protected:
43
    GDALDataset *RefUnderlyingDataset() const override
44
0
    {
45
0
        return m_poUnderlyingDS;
46
0
    }
47
48
    void UnrefUnderlyingDataset(GDALDataset *) const override;
49
50
  private:
51
    const std::string m_filename;
52
    std::unique_ptr<GDALAlgorithm> m_poAlg{};
53
    GDALDataset *m_poUnderlyingDS = nullptr;
54
55
    CPL_DISALLOW_COPY_ASSIGN(GDALGDataset)
56
57
    GDALDriver *GetDriver() const override
58
0
    {
59
0
        return poDriver;
60
0
    }
61
62
    int GetLayerCount() const override
63
0
    {
64
0
        return m_poUnderlyingDS->GetLayerCount();
65
0
    }
66
67
    const OGRLayer *GetLayer(int idx) const override
68
0
    {
69
0
        return m_poUnderlyingDS->GetLayer(idx);
70
0
    }
71
72
    OGRLayer *GetLayerByName(const char *pszName) override
73
0
    {
74
0
        return m_poUnderlyingDS->GetLayerByName(pszName);
75
0
    }
76
77
    OGRLayer *ExecuteSQL(const char *pszStatement, OGRGeometry *poSpatialFilter,
78
                         const char *pszDialect) override
79
0
    {
80
0
        return m_poUnderlyingDS->ExecuteSQL(pszStatement, poSpatialFilter,
81
0
                                            pszDialect);
82
0
    }
83
84
    void ResetReading() override
85
0
    {
86
0
        m_poUnderlyingDS->ResetReading();
87
0
    }
88
89
    OGRFeature *GetNextFeature(OGRLayer **ppoBelongingLayer,
90
                               double *pdfProgressPct,
91
                               GDALProgressFunc pfnProgress,
92
                               void *pProgressData) override
93
0
    {
94
0
        return m_poUnderlyingDS->GetNextFeature(
95
0
            ppoBelongingLayer, pdfProgressPct, pfnProgress, pProgressData);
96
0
    }
97
98
    int TestCapability(const char *pszCap) const override
99
0
    {
100
0
        return m_poUnderlyingDS->TestCapability(pszCap);
101
0
    }
102
103
    std::shared_ptr<GDALGroup> GetRootGroup() const override
104
0
    {
105
0
        return m_poUnderlyingDS->GetRootGroup();
106
0
    }
107
};
108
109
/************************************************************************/
110
/*                           GDALGRasterBand                            */
111
/************************************************************************/
112
113
class GDALGRasterBand final : public GDALProxyRasterBand
114
{
115
  public:
116
    explicit GDALGRasterBand(GDALRasterBand *poUnderlyingBand);
117
118
  protected:
119
    GDALRasterBand *
120
    RefUnderlyingRasterBand(bool /* bForceOpen */) const override
121
0
    {
122
0
        return m_poUnderlyingBand;
123
0
    }
124
125
    void UnrefUnderlyingRasterBand(GDALRasterBand *) const override;
126
127
  private:
128
    GDALRasterBand *m_poUnderlyingBand = nullptr;
129
130
    CPL_DISALLOW_COPY_ASSIGN(GDALGRasterBand)
131
};
132
133
/************************************************************************/
134
/*                     GDALGDataset::GDALGDataset()                     */
135
/************************************************************************/
136
137
GDALGDataset::GDALGDataset(const std::string &filename,
138
                           std::unique_ptr<GDALAlgorithm> poAlg,
139
                           GDALDataset *poDS)
140
0
    : m_filename(filename), m_poAlg(std::move(poAlg)), m_poUnderlyingDS(poDS)
141
0
{
142
0
    nRasterXSize = m_poUnderlyingDS->GetRasterXSize();
143
0
    nRasterYSize = m_poUnderlyingDS->GetRasterYSize();
144
0
    for (int i = 0; i < m_poUnderlyingDS->GetRasterCount(); ++i)
145
0
    {
146
0
        SetBand(i + 1, std::make_unique<GDALGRasterBand>(
147
0
                           m_poUnderlyingDS->GetRasterBand(i + 1)));
148
0
    }
149
0
}
150
151
/************************************************************************/
152
/*                GDALGDataset::UnrefUnderlyingDataset()                */
153
/************************************************************************/
154
155
void GDALGDataset::UnrefUnderlyingDataset(GDALDataset *) const
156
0
{
157
0
}
158
159
/************************************************************************/
160
/*                  GDALGRasterBand::GDALGRasterBand()                  */
161
/************************************************************************/
162
163
GDALGRasterBand::GDALGRasterBand(GDALRasterBand *poUnderlyingBand)
164
0
    : m_poUnderlyingBand(poUnderlyingBand)
165
0
{
166
0
    nBand = poUnderlyingBand->GetBand();
167
0
    eDataType = poUnderlyingBand->GetRasterDataType();
168
0
    nRasterXSize = poUnderlyingBand->GetXSize();
169
0
    nRasterYSize = poUnderlyingBand->GetYSize();
170
0
    poUnderlyingBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
171
0
}
172
173
/************************************************************************/
174
/*              GDALGRasterBand::UnrefUnderlyingDataset()               */
175
/************************************************************************/
176
177
void GDALGRasterBand::UnrefUnderlyingRasterBand(GDALRasterBand *) const
178
0
{
179
0
}
180
181
/************************************************************************/
182
/*                       GDALGDataset::Identify()                       */
183
/************************************************************************/
184
185
/* static */ int GDALGDataset::Identify(GDALOpenInfo *poOpenInfo)
186
712k
{
187
712k
    return poOpenInfo->IsSingleAllowedDriver("GDALG") ||
188
712k
           (poOpenInfo->pabyHeader &&
189
233k
            strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
190
233k
                   "\"gdal_streamed_alg\"")) ||
191
711k
           (strstr(poOpenInfo->pszFilename, "\"gdal_streamed_alg\""));
192
712k
}
193
194
/************************************************************************/
195
/*                         GDALGDataset::Open()                         */
196
/************************************************************************/
197
198
/* static */ GDALDataset *GDALGDataset::Open(GDALOpenInfo *poOpenInfo)
199
1.43k
{
200
1.43k
    CPLJSONDocument oDoc;
201
1.43k
    if (poOpenInfo->pabyHeader)
202
1.12k
    {
203
1.12k
        if (!oDoc.Load(poOpenInfo->pszFilename))
204
989
        {
205
989
            return nullptr;
206
989
        }
207
1.12k
    }
208
312
    else
209
312
    {
210
312
        if (!oDoc.LoadMemory(
211
312
                reinterpret_cast<const char *>(poOpenInfo->pszFilename)))
212
273
        {
213
273
            return nullptr;
214
273
        }
215
312
    }
216
171
    if (oDoc.GetRoot().GetString("type") != "gdal_streamed_alg")
217
171
    {
218
171
        CPLDebug("GDALG", "\"type\" = \"gdal_streamed_alg\" missing");
219
171
        return nullptr;
220
171
    }
221
222
0
    if (poOpenInfo->eAccess == GA_Update)
223
0
    {
224
0
        ReportUpdateNotSupportedByDriver("GDALG");
225
0
        return nullptr;
226
0
    }
227
228
0
    const std::string osCommandLine = oDoc.GetRoot().GetString("command_line");
229
0
    if (osCommandLine.empty())
230
0
    {
231
0
        CPLError(CE_Failure, CPLE_AppDefined, "command_line missing");
232
0
        return nullptr;
233
0
    }
234
235
0
    const auto CheckVersion = [&oDoc]()
236
0
    {
237
0
        const std::string osVersion = oDoc.GetRoot().GetString("gdal_version");
238
0
        if (!osVersion.empty() &&
239
0
            atoi(GDALVersionInfo("VERSION_NUM")) < atoi(osVersion.c_str()))
240
0
        {
241
0
            CPLError(CE_Failure, CPLE_AppDefined,
242
0
                     "The failure might be due to the .gdalg.json file having "
243
0
                     "been created with GDAL VERSION_NUM=%s which is newer "
244
0
                     "than current GDAL VERSION_NUM=%s",
245
0
                     osVersion.c_str(), GDALVersionInfo("VERSION_NUM"));
246
0
        }
247
0
    };
248
249
0
    const CPLStringList aosArgs(CSLTokenizeString(osCommandLine.c_str()));
250
251
0
    auto alg = GDALGlobalAlgorithmRegistry::GetSingleton().Instantiate(
252
0
        GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME);
253
254
0
    if (poOpenInfo->pabyHeader &&
255
0
        oDoc.GetRoot().GetBool("relative_paths_relative_to_this_file", true))
256
0
    {
257
0
        alg->SetReferencePathForRelativePaths(
258
0
            CPLGetPathSafe(poOpenInfo->pszFilename).c_str());
259
0
    }
260
261
0
    alg->SetExecutionForStreamedOutput();
262
263
0
    alg->SetCallPath(std::vector<std::string>{aosArgs[0]});
264
0
    std::vector<std::string> args;
265
0
    for (int i = 1; i < aosArgs.size(); ++i)
266
0
        args.push_back(aosArgs[i]);
267
0
    if (!alg->ParseCommandLineArguments(args))
268
0
    {
269
0
        CheckVersion();
270
0
        return nullptr;
271
0
    }
272
0
    if (!alg->GetActualAlgorithm().SupportsStreamedOutput())
273
0
    {
274
0
        CPLError(CE_Failure, CPLE_AppDefined,
275
0
                 "Algorithm %s does not support a streamed output",
276
0
                 alg->GetActualAlgorithm().GetName().c_str());
277
0
        return nullptr;
278
0
    }
279
280
0
    if (!alg->Run(nullptr, nullptr))
281
0
    {
282
0
        CheckVersion();
283
0
        return nullptr;
284
0
    }
285
286
0
    std::unique_ptr<GDALDataset> ret;
287
0
    const auto outputArg = alg->GetActualAlgorithm().GetArg("output");
288
0
    if (outputArg && outputArg->GetType() == GAAT_DATASET)
289
0
    {
290
0
        auto &val = outputArg->Get<GDALArgDatasetValue>();
291
0
        auto poUnderlyingDS = val.GetDatasetRef();
292
0
        if (poUnderlyingDS)
293
0
        {
294
0
            if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) &&
295
0
                !(poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) &&
296
0
                !(poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER))
297
0
            {
298
                // Don't return if asked for a raster dataset but the
299
                // underlying one is not.
300
0
                if (poUnderlyingDS->GetRasterCount() == 0 &&
301
0
                    !poUnderlyingDS->GetMetadata(GDAL_MDD_SUBDATASETS))
302
0
                {
303
0
                    return nullptr;
304
0
                }
305
0
            }
306
0
            else if ((poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) &&
307
0
                     !(poOpenInfo->nOpenFlags & GDAL_OF_RASTER) &&
308
0
                     !(poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER))
309
0
            {
310
                // Don't return if asked for a vector dataset but the
311
                // underlying one is not.
312
0
                if (poUnderlyingDS->GetLayerCount() == 0)
313
0
                {
314
0
                    return nullptr;
315
0
                }
316
0
            }
317
0
            else if ((poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER) &&
318
0
                     !(poOpenInfo->nOpenFlags & GDAL_OF_RASTER) &&
319
0
                     !(poOpenInfo->nOpenFlags & GDAL_OF_VECTOR))
320
0
            {
321
                // Don't return if asked for a multidim dataset but the
322
                // underlying one is not.
323
0
                if (!poUnderlyingDS->GetRootGroup())
324
0
                {
325
0
                    return nullptr;
326
0
                }
327
0
            }
328
0
            ret = std::make_unique<GDALGDataset>(
329
0
                poOpenInfo->pabyHeader ? poOpenInfo->pszFilename : "",
330
0
                std::move(alg), poUnderlyingDS);
331
0
        }
332
0
    }
333
334
0
    return ret.release();
335
0
}
336
337
/************************************************************************/
338
/*                         GDALRegister_GDALG()                         */
339
/************************************************************************/
340
341
void GDALRegister_GDALG()
342
22
{
343
22
    if (GDALGetDriverByName("GDALG") != nullptr)
344
0
        return;
345
346
22
    auto poDriver = std::make_unique<GDALDriver>();
347
348
22
    poDriver->SetDescription("GDALG");
349
22
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
350
22
    poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
351
22
    poDriver->SetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER, "YES");
352
22
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
353
22
                              "GDAL Streamed Algorithm driver");
354
22
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "gdalg.json");
355
356
22
    poDriver->SetMetadataItem(GDAL_DCAP_MEASURED_GEOMETRIES, "YES");
357
22
    poDriver->SetMetadataItem(GDAL_DCAP_CURVE_GEOMETRIES, "YES");
358
22
    poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES");
359
360
22
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
361
362
22
    poDriver->pfnIdentify = GDALGDataset::Identify;
363
22
    poDriver->pfnOpen = GDALGDataset::Open;
364
365
22
    GetGDALDriverManager()->RegisterDriver(poDriver.release());
366
22
}