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