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 74946 : /* static */ int GDALGDataset::Identify(GDALOpenInfo *poOpenInfo) 186 : { 187 74946 : return poOpenInfo->IsSingleAllowedDriver("GDALG") || 188 74944 : (poOpenInfo->pabyHeader && 189 13508 : strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader), 190 149890 : "\"gdal_streamed_alg\"")) || 191 149826 : (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 97 : if (osCommandLine.empty()) 230 : { 231 1 : CPLError(CE_Failure, CPLE_AppDefined, "command_line missing"); 232 1 : return nullptr; 233 : } 234 : 235 14 : const auto CheckVersion = [&oDoc]() 236 : { 237 42 : const std::string osVersion = oDoc.GetRoot().GetString("gdal_version"); 238 19 : if (!osVersion.empty() && 239 5 : atoi(GDALVersionInfo("VERSION_NUM")) < atoi(osVersion.c_str())) 240 : { 241 1 : CPLError(CE_Failure, CPLE_AppDefined, 242 : "The failure might be due to the .gdalg.json file having " 243 : "been created with GDAL VERSION_NUM=%s which is newer " 244 : "than current GDAL VERSION_NUM=%s", 245 : osVersion.c_str(), GDALVersionInfo("VERSION_NUM")); 246 : } 247 14 : }; 248 : 249 192 : const CPLStringList aosArgs(CSLTokenizeString(osCommandLine.c_str())); 250 : 251 96 : auto alg = GDALGlobalAlgorithmRegistry::GetSingleton().Instantiate( 252 288 : GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME); 253 : 254 156 : if (poOpenInfo->pabyHeader && 255 156 : oDoc.GetRoot().GetBool("relative_paths_relative_to_this_file", true)) 256 : { 257 72 : alg->SetReferencePathForRelativePaths( 258 48 : CPLGetPathSafe(poOpenInfo->pszFilename).c_str()); 259 : } 260 : 261 96 : alg->SetExecutionForStreamedOutput(); 262 : 263 192 : alg->SetCallPath(std::vector<std::string>{aosArgs[0]}); 264 192 : std::vector<std::string> args; 265 720 : for (int i = 1; i < aosArgs.size(); ++i) 266 624 : args.push_back(aosArgs[i]); 267 96 : if (!alg->ParseCommandLineArguments(args)) 268 : { 269 3 : CheckVersion(); 270 3 : return nullptr; 271 : } 272 93 : if (!alg->GetActualAlgorithm().SupportsStreamedOutput()) 273 : { 274 1 : CPLError(CE_Failure, CPLE_AppDefined, 275 : "Algorithm %s does not support a streamed output", 276 1 : alg->GetActualAlgorithm().GetName().c_str()); 277 1 : return nullptr; 278 : } 279 : 280 92 : if (!alg->Run(nullptr, nullptr)) 281 : { 282 11 : CheckVersion(); 283 11 : return nullptr; 284 : } 285 : 286 81 : std::unique_ptr<GDALDataset> ret; 287 81 : const auto outputArg = alg->GetActualAlgorithm().GetArg("output"); 288 81 : if (outputArg && outputArg->GetType() == GAAT_DATASET) 289 : { 290 81 : auto &val = outputArg->Get<GDALArgDatasetValue>(); 291 81 : auto poUnderlyingDS = val.GetDatasetRef(); 292 81 : if (poUnderlyingDS) 293 : { 294 81 : if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) && 295 39 : !(poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) && 296 38 : !(poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER)) 297 : { 298 : // Don't return if asked for a raster dataset but the 299 : // underlying one is not. 300 39 : if (poUnderlyingDS->GetRasterCount() == 0 && 301 1 : !poUnderlyingDS->GetMetadata(GDAL_MDD_SUBDATASETS)) 302 : { 303 2 : return nullptr; 304 : } 305 : } 306 43 : else if ((poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) && 307 42 : !(poOpenInfo->nOpenFlags & GDAL_OF_RASTER) && 308 41 : !(poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER)) 309 : { 310 : // Don't return if asked for a vector dataset but the 311 : // underlying one is not. 312 41 : if (poUnderlyingDS->GetLayerCount() == 0) 313 : { 314 1 : return nullptr; 315 : } 316 : } 317 2 : else if ((poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER) && 318 1 : !(poOpenInfo->nOpenFlags & GDAL_OF_RASTER) && 319 1 : !(poOpenInfo->nOpenFlags & GDAL_OF_VECTOR)) 320 : { 321 : // Don't return if asked for a multidim dataset but the 322 : // underlying one is not. 323 1 : if (!poUnderlyingDS->GetRootGroup()) 324 : { 325 0 : return nullptr; 326 : } 327 : } 328 79 : ret = std::make_unique<GDALGDataset>( 329 158 : poOpenInfo->pabyHeader ? poOpenInfo->pszFilename : "", 330 158 : std::move(alg), poUnderlyingDS); 331 : } 332 : } 333 : 334 79 : return ret.release(); 335 : } 336 : 337 : /************************************************************************/ 338 : /* GDALRegister_GDALG() */ 339 : /************************************************************************/ 340 : 341 2135 : void GDALRegister_GDALG() 342 : { 343 2135 : if (GDALGetDriverByName("GDALG") != nullptr) 344 263 : return; 345 : 346 3744 : auto poDriver = std::make_unique<GDALDriver>(); 347 : 348 1872 : poDriver->SetDescription("GDALG"); 349 1872 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES"); 350 1872 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES"); 351 1872 : poDriver->SetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER, "YES"); 352 1872 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, 353 1872 : "GDAL Streamed Algorithm driver"); 354 1872 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "gdalg.json"); 355 : 356 1872 : poDriver->SetMetadataItem(GDAL_DCAP_MEASURED_GEOMETRIES, "YES"); 357 1872 : poDriver->SetMetadataItem(GDAL_DCAP_CURVE_GEOMETRIES, "YES"); 358 1872 : poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES"); 359 : 360 1872 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES"); 361 : 362 1872 : poDriver->pfnIdentify = GDALGDataset::Identify; 363 1872 : poDriver->pfnOpen = GDALGDataset::Open; 364 : 365 1872 : GetGDALDriverManager()->RegisterDriver(poDriver.release()); 366 : }