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(std::unique_ptr<GDALAlgorithm> poAlg, GDALDataset *poDS); 28 : 29 : static int Identify(GDALOpenInfo *poOpenInfo); 30 : static GDALDataset *Open(GDALOpenInfo *poOpenInfo); 31 : 32 : protected: 33 0 : GDALDataset *RefUnderlyingDataset() const override 34 : { 35 0 : return m_poUnderlyingDS; 36 : } 37 : 38 0 : void UnrefUnderlyingDataset(GDALDataset *) const override 39 : { 40 0 : } 41 : 42 : private: 43 : std::unique_ptr<GDALAlgorithm> m_poAlg{}; 44 : GDALDataset *m_poUnderlyingDS = nullptr; 45 : 46 : CPL_DISALLOW_COPY_ASSIGN(GDALGDataset) 47 : 48 1 : GDALDriver *GetDriver() override 49 : { 50 1 : return poDriver; 51 : } 52 : 53 1 : int GetLayerCount() override 54 : { 55 1 : return m_poUnderlyingDS->GetLayerCount(); 56 : } 57 : 58 1 : OGRLayer *GetLayer(int idx) override 59 : { 60 1 : return m_poUnderlyingDS->GetLayer(idx); 61 : } 62 : 63 1 : OGRLayer *GetLayerByName(const char *pszName) override 64 : { 65 1 : return m_poUnderlyingDS->GetLayerByName(pszName); 66 : } 67 : 68 1 : OGRLayer *ExecuteSQL(const char *pszStatement, OGRGeometry *poSpatialFilter, 69 : const char *pszDialect) override 70 : { 71 1 : return m_poUnderlyingDS->ExecuteSQL(pszStatement, poSpatialFilter, 72 1 : pszDialect); 73 : } 74 : 75 1 : void ResetReading() override 76 : { 77 1 : m_poUnderlyingDS->ResetReading(); 78 1 : } 79 : 80 11 : OGRFeature *GetNextFeature(OGRLayer **ppoBelongingLayer, 81 : double *pdfProgressPct, 82 : GDALProgressFunc pfnProgress, 83 : void *pProgressData) override 84 : { 85 11 : return m_poUnderlyingDS->GetNextFeature( 86 11 : ppoBelongingLayer, pdfProgressPct, pfnProgress, pProgressData); 87 : } 88 : 89 1 : int TestCapability(const char *pszCap) override 90 : { 91 1 : return m_poUnderlyingDS->TestCapability(pszCap); 92 : } 93 : }; 94 : 95 : /************************************************************************/ 96 : /* GDALGRasterBand */ 97 : /************************************************************************/ 98 : 99 : class GDALGRasterBand final : public GDALProxyRasterBand 100 : { 101 : public: 102 : explicit GDALGRasterBand(GDALRasterBand *poUnderlyingBand); 103 : 104 : protected: 105 : GDALRasterBand * 106 6 : RefUnderlyingRasterBand(bool /* bForceOpen */) const override 107 : { 108 6 : return m_poUnderlyingBand; 109 : } 110 : 111 6 : void UnrefUnderlyingRasterBand(GDALRasterBand *) const override 112 : { 113 6 : } 114 : 115 : private: 116 : GDALRasterBand *m_poUnderlyingBand = nullptr; 117 : 118 : CPL_DISALLOW_COPY_ASSIGN(GDALGRasterBand) 119 : }; 120 : 121 : /************************************************************************/ 122 : /* GDALGDataset::GDALGDataset() */ 123 : /************************************************************************/ 124 : 125 7 : GDALGDataset::GDALGDataset(std::unique_ptr<GDALAlgorithm> poAlg, 126 7 : GDALDataset *poDS) 127 7 : : m_poAlg(std::move(poAlg)), m_poUnderlyingDS(poDS) 128 : { 129 7 : nRasterXSize = m_poUnderlyingDS->GetRasterXSize(); 130 7 : nRasterYSize = m_poUnderlyingDS->GetRasterYSize(); 131 13 : for (int i = 0; i < m_poUnderlyingDS->GetRasterCount(); ++i) 132 : { 133 6 : SetBand(i + 1, std::make_unique<GDALGRasterBand>( 134 12 : m_poUnderlyingDS->GetRasterBand(i + 1))); 135 : } 136 7 : } 137 : 138 : /************************************************************************/ 139 : /* GDALGRasterBand::GDALGRasterBand() */ 140 : /************************************************************************/ 141 : 142 6 : GDALGRasterBand::GDALGRasterBand(GDALRasterBand *poUnderlyingBand) 143 6 : : m_poUnderlyingBand(poUnderlyingBand) 144 : { 145 6 : nBand = poUnderlyingBand->GetBand(); 146 6 : eDataType = poUnderlyingBand->GetRasterDataType(); 147 6 : nRasterXSize = poUnderlyingBand->GetXSize(); 148 6 : nRasterYSize = poUnderlyingBand->GetYSize(); 149 6 : poUnderlyingBand->GetBlockSize(&nBlockXSize, &nBlockYSize); 150 6 : } 151 : 152 : /************************************************************************/ 153 : /* GDALGDataset::Identify() */ 154 : /************************************************************************/ 155 : 156 58435 : /* static */ int GDALGDataset::Identify(GDALOpenInfo *poOpenInfo) 157 : { 158 68732 : return (poOpenInfo->pabyHeader && 159 10297 : strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader), 160 116870 : "\"gdal_streamed_alg\"")) || 161 116862 : (strstr(poOpenInfo->pszFilename, "\"gdal_streamed_alg\"")); 162 : } 163 : 164 : /************************************************************************/ 165 : /* GDALGDataset::Open() */ 166 : /************************************************************************/ 167 : 168 16 : /* static */ GDALDataset *GDALGDataset::Open(GDALOpenInfo *poOpenInfo) 169 : { 170 16 : if (!Identify(poOpenInfo)) 171 0 : return nullptr; 172 32 : CPLJSONDocument oDoc; 173 16 : if (poOpenInfo->pabyHeader) 174 : { 175 4 : if (!oDoc.Load(poOpenInfo->pszFilename)) 176 : { 177 0 : return nullptr; 178 : } 179 : } 180 : else 181 : { 182 24 : if (!oDoc.LoadMemory( 183 12 : reinterpret_cast<const char *>(poOpenInfo->pszFilename))) 184 : { 185 0 : return nullptr; 186 : } 187 : } 188 16 : if (oDoc.GetRoot().GetString("type") != "gdal_streamed_alg") 189 0 : return nullptr; 190 48 : const std::string osCommandLine = oDoc.GetRoot().GetString("command_line"); 191 16 : if (osCommandLine.empty()) 192 : { 193 0 : CPLError(CE_Failure, CPLE_AppDefined, "command_line missing"); 194 0 : return nullptr; 195 : } 196 : 197 32 : const CPLStringList aosArgs(CSLTokenizeString(osCommandLine.c_str())); 198 : 199 16 : auto alg = GDALGlobalAlgorithmRegistry::GetSingleton().Instantiate( 200 48 : GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME); 201 : 202 20 : if (poOpenInfo->pabyHeader && 203 20 : oDoc.GetRoot().GetBool("relative_paths_relative_to_this_file", true)) 204 : { 205 12 : alg->SetReferencePathForRelativePaths( 206 8 : CPLGetPathSafe(poOpenInfo->pszFilename).c_str()); 207 : } 208 : 209 16 : alg->SetExecutionForStreamedOutput(); 210 : 211 32 : alg->SetCallPath(std::vector<std::string>{aosArgs[0]}); 212 32 : std::vector<std::string> args; 213 105 : for (int i = 1; i < aosArgs.size(); ++i) 214 89 : args.push_back(aosArgs[i]); 215 16 : if (!alg->ParseCommandLineArguments(args)) 216 : { 217 1 : return nullptr; 218 : } 219 15 : if (!alg->GetActualAlgorithm().SupportsStreamedOutput()) 220 : { 221 0 : CPLError(CE_Failure, CPLE_AppDefined, 222 : "Algorithm %s does not support a streamed output", 223 0 : alg->GetActualAlgorithm().GetName().c_str()); 224 0 : return nullptr; 225 : } 226 : 227 15 : if (!alg->Run(nullptr, nullptr)) 228 : { 229 6 : return nullptr; 230 : } 231 : 232 9 : const auto outputArg = alg->GetActualAlgorithm().GetArg("output"); 233 9 : if (outputArg && outputArg->GetType() == GAAT_DATASET) 234 : { 235 9 : auto &val = outputArg->Get<GDALArgDatasetValue>(); 236 9 : auto poUnderlyingDS = val.GetDatasetRef(); 237 9 : if (poUnderlyingDS) 238 : { 239 9 : if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) && 240 7 : !(poOpenInfo->nOpenFlags & GDAL_OF_VECTOR)) 241 : { 242 : // Don't return if asked for a raster dataset but the 243 : // underlying one is not. 244 8 : if (poUnderlyingDS->GetRasterCount() == 0 && 245 1 : !poUnderlyingDS->GetMetadata("SUBDATASETS")) 246 : { 247 9 : return nullptr; 248 : } 249 : } 250 2 : else if (!(poOpenInfo->nOpenFlags & GDAL_OF_RASTER) && 251 2 : (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR)) 252 : { 253 : // Don't return if asked for a vector dataset but the 254 : // underlying one is not. 255 2 : if (poUnderlyingDS->GetLayerCount() == 0) 256 : { 257 1 : return nullptr; 258 : } 259 : } 260 14 : return std::make_unique<GDALGDataset>(std::move(alg), 261 : poUnderlyingDS) 262 7 : .release(); 263 : } 264 : } 265 : 266 0 : return nullptr; 267 : } 268 : 269 : /************************************************************************/ 270 : /* GDALRegister_GDALG() */ 271 : /************************************************************************/ 272 : 273 1667 : void GDALRegister_GDALG() 274 : { 275 1667 : if (GDALGetDriverByName("GDALG") != nullptr) 276 282 : return; 277 : 278 2770 : auto poDriver = std::make_unique<GDALDriver>(); 279 : 280 1385 : poDriver->SetDescription("GDALG"); 281 1385 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES"); 282 1385 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES"); 283 1385 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, 284 1385 : "GDAL Streamed Algorithm driver"); 285 1385 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "gdalg.json"); 286 : 287 1385 : poDriver->SetMetadataItem(GDAL_DCAP_MEASURED_GEOMETRIES, "YES"); 288 1385 : poDriver->SetMetadataItem(GDAL_DCAP_CURVE_GEOMETRIES, "YES"); 289 1385 : poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES"); 290 : 291 1385 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES"); 292 : 293 1385 : poDriver->pfnIdentify = GDALGDataset::Identify; 294 1385 : poDriver->pfnOpen = GDALGDataset::Open; 295 : 296 1385 : GetGDALDriverManager()->RegisterDriver(poDriver.release()); 297 : }