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