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 9 : char **GetFileList(void) override 31 : { 32 18 : CPLStringList aosList; 33 9 : if (!m_filename.empty()) 34 8 : aosList.push_back(m_filename); 35 18 : return aosList.StealList(); 36 : } 37 : 38 : static int Identify(GDALOpenInfo *poOpenInfo); 39 : static GDALDataset *Open(GDALOpenInfo *poOpenInfo); 40 : 41 : protected: 42 31 : GDALDataset *RefUnderlyingDataset() const override 43 : { 44 31 : 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 32 : GDALDriver *GetDriver() override 57 : { 58 32 : return poDriver; 59 : } 60 : 61 37 : int GetLayerCount() override 62 : { 63 37 : return m_poUnderlyingDS->GetLayerCount(); 64 : } 65 : 66 20 : OGRLayer *GetLayer(int idx) override 67 : { 68 20 : return m_poUnderlyingDS->GetLayer(idx); 69 : } 70 : 71 7 : OGRLayer *GetLayerByName(const char *pszName) override 72 : { 73 7 : return m_poUnderlyingDS->GetLayerByName(pszName); 74 : } 75 : 76 37 : OGRLayer *ExecuteSQL(const char *pszStatement, OGRGeometry *poSpatialFilter, 77 : const char *pszDialect) override 78 : { 79 37 : return m_poUnderlyingDS->ExecuteSQL(pszStatement, poSpatialFilter, 80 37 : 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 48 : int TestCapability(const char *pszCap) override 98 : { 99 48 : 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 780 : RefUnderlyingRasterBand(bool /* bForceOpen */) const override 115 : { 116 780 : 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 33 : GDALGDataset::GDALGDataset(const std::string &filename, 132 : std::unique_ptr<GDALAlgorithm> poAlg, 133 33 : GDALDataset *poDS) 134 33 : : m_filename(filename), m_poAlg(std::move(poAlg)), m_poUnderlyingDS(poDS) 135 : { 136 33 : nRasterXSize = m_poUnderlyingDS->GetRasterXSize(); 137 33 : nRasterYSize = m_poUnderlyingDS->GetRasterYSize(); 138 56 : for (int i = 0; i < m_poUnderlyingDS->GetRasterCount(); ++i) 139 : { 140 23 : SetBand(i + 1, std::make_unique<GDALGRasterBand>( 141 46 : m_poUnderlyingDS->GetRasterBand(i + 1))); 142 : } 143 33 : } 144 : 145 : /************************************************************************/ 146 : /* GDALGDataset::UnrefUnderlyingDataset() */ 147 : /************************************************************************/ 148 : 149 31 : void GDALGDataset::UnrefUnderlyingDataset(GDALDataset *) const 150 : { 151 31 : } 152 : 153 : /************************************************************************/ 154 : /* GDALGRasterBand::GDALGRasterBand() */ 155 : /************************************************************************/ 156 : 157 23 : GDALGRasterBand::GDALGRasterBand(GDALRasterBand *poUnderlyingBand) 158 23 : : m_poUnderlyingBand(poUnderlyingBand) 159 : { 160 23 : nBand = poUnderlyingBand->GetBand(); 161 23 : eDataType = poUnderlyingBand->GetRasterDataType(); 162 23 : nRasterXSize = poUnderlyingBand->GetXSize(); 163 23 : nRasterYSize = poUnderlyingBand->GetYSize(); 164 23 : poUnderlyingBand->GetBlockSize(&nBlockXSize, &nBlockYSize); 165 23 : } 166 : 167 : /************************************************************************/ 168 : /* GDALGRasterBand::UnrefUnderlyingDataset() */ 169 : /************************************************************************/ 170 : 171 780 : void GDALGRasterBand::UnrefUnderlyingRasterBand(GDALRasterBand *) const 172 : { 173 780 : } 174 : 175 : /************************************************************************/ 176 : /* GDALGDataset::Identify() */ 177 : /************************************************************************/ 178 : 179 66737 : /* static */ int GDALGDataset::Identify(GDALOpenInfo *poOpenInfo) 180 : { 181 66737 : return poOpenInfo->IsSingleAllowedDriver("GDALG") || 182 66735 : (poOpenInfo->pabyHeader && 183 10796 : strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader), 184 133471 : "\"gdal_streamed_alg\"")) || 185 133434 : (strstr(poOpenInfo->pszFilename, "\"gdal_streamed_alg\"")); 186 : } 187 : 188 : /************************************************************************/ 189 : /* GDALGDataset::Open() */ 190 : /************************************************************************/ 191 : 192 52 : /* static */ GDALDataset *GDALGDataset::Open(GDALOpenInfo *poOpenInfo) 193 : { 194 104 : CPLJSONDocument oDoc; 195 52 : if (poOpenInfo->pabyHeader) 196 : { 197 34 : if (!oDoc.Load(poOpenInfo->pszFilename)) 198 : { 199 1 : return nullptr; 200 : } 201 : } 202 : else 203 : { 204 36 : if (!oDoc.LoadMemory( 205 18 : reinterpret_cast<const char *>(poOpenInfo->pszFilename))) 206 : { 207 1 : return nullptr; 208 : } 209 : } 210 50 : 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 49 : if (poOpenInfo->eAccess == GA_Update) 217 : { 218 1 : ReportUpdateNotSupportedByDriver("GDALG"); 219 1 : return nullptr; 220 : } 221 : 222 144 : const std::string osCommandLine = oDoc.GetRoot().GetString("command_line"); 223 48 : if (osCommandLine.empty()) 224 : { 225 1 : CPLError(CE_Failure, CPLE_AppDefined, "command_line missing"); 226 1 : return nullptr; 227 : } 228 : 229 11 : const auto CheckVersion = [&oDoc]() 230 : { 231 33 : const std::string osVersion = oDoc.GetRoot().GetString("gdal_version"); 232 15 : if (!osVersion.empty() && 233 4 : 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 11 : }; 242 : 243 94 : const CPLStringList aosArgs(CSLTokenizeString(osCommandLine.c_str())); 244 : 245 47 : auto alg = GDALGlobalAlgorithmRegistry::GetSingleton().Instantiate( 246 141 : GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME); 247 : 248 79 : if (poOpenInfo->pabyHeader && 249 79 : oDoc.GetRoot().GetBool("relative_paths_relative_to_this_file", true)) 250 : { 251 60 : alg->SetReferencePathForRelativePaths( 252 40 : CPLGetPathSafe(poOpenInfo->pszFilename).c_str()); 253 : } 254 : 255 47 : alg->SetExecutionForStreamedOutput(); 256 : 257 94 : alg->SetCallPath(std::vector<std::string>{aosArgs[0]}); 258 94 : std::vector<std::string> args; 259 339 : for (int i = 1; i < aosArgs.size(); ++i) 260 292 : args.push_back(aosArgs[i]); 261 47 : if (!alg->ParseCommandLineArguments(args)) 262 : { 263 3 : CheckVersion(); 264 3 : return nullptr; 265 : } 266 44 : 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 43 : if (!alg->Run(nullptr, nullptr)) 275 : { 276 8 : CheckVersion(); 277 8 : return nullptr; 278 : } 279 : 280 35 : std::unique_ptr<GDALDataset> ret; 281 35 : const auto outputArg = alg->GetActualAlgorithm().GetArg("output"); 282 35 : if (outputArg && outputArg->GetType() == GAAT_DATASET) 283 : { 284 35 : auto &val = outputArg->Get<GDALArgDatasetValue>(); 285 35 : auto poUnderlyingDS = val.GetDatasetRef(); 286 35 : if (poUnderlyingDS) 287 : { 288 35 : if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) && 289 21 : !(poOpenInfo->nOpenFlags & GDAL_OF_VECTOR)) 290 : { 291 : // Don't return if asked for a raster dataset but the 292 : // underlying one is not. 293 21 : if (poUnderlyingDS->GetRasterCount() == 0 && 294 1 : !poUnderlyingDS->GetMetadata("SUBDATASETS")) 295 : { 296 2 : return nullptr; 297 : } 298 : } 299 15 : else if (!(poOpenInfo->nOpenFlags & GDAL_OF_RASTER) && 300 14 : (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR)) 301 : { 302 : // Don't return if asked for a vector dataset but the 303 : // underlying one is not. 304 14 : if (poUnderlyingDS->GetLayerCount() == 0) 305 : { 306 1 : return nullptr; 307 : } 308 : } 309 33 : ret = std::make_unique<GDALGDataset>( 310 66 : poOpenInfo->pabyHeader ? poOpenInfo->pszFilename : "", 311 66 : std::move(alg), poUnderlyingDS); 312 : } 313 : } 314 : 315 33 : return ret.release(); 316 : } 317 : 318 : /************************************************************************/ 319 : /* GDALRegister_GDALG() */ 320 : /************************************************************************/ 321 : 322 1935 : void GDALRegister_GDALG() 323 : { 324 1935 : if (GDALGetDriverByName("GDALG") != nullptr) 325 282 : return; 326 : 327 3306 : auto poDriver = std::make_unique<GDALDriver>(); 328 : 329 1653 : poDriver->SetDescription("GDALG"); 330 1653 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES"); 331 1653 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES"); 332 1653 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, 333 1653 : "GDAL Streamed Algorithm driver"); 334 1653 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "gdalg.json"); 335 : 336 1653 : poDriver->SetMetadataItem(GDAL_DCAP_MEASURED_GEOMETRIES, "YES"); 337 1653 : poDriver->SetMetadataItem(GDAL_DCAP_CURVE_GEOMETRIES, "YES"); 338 1653 : poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES"); 339 : 340 1653 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES"); 341 : 342 1653 : poDriver->pfnIdentify = GDALGDataset::Identify; 343 1653 : poDriver->pfnOpen = GDALGDataset::Open; 344 : 345 1653 : GetGDALDriverManager()->RegisterDriver(poDriver.release()); 346 : }