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