/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 | } |