LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_pixel_info.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 515 554 93.0 %
Date: 2026-07-09 08:35:43 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  gdal "raster pixelinfo" subcommand
       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 "gdalalg_raster_pixel_info.h"
      14             : 
      15             : #include "cpl_conv.h"
      16             : #include "cpl_json.h"
      17             : #include "cpl_minixml.h"
      18             : #include "gdal_priv.h"
      19             : #include "ogrsf_frmts.h"
      20             : #include "ogr_spatialref.h"
      21             : 
      22             : #include <algorithm>
      23             : #include <cmath>
      24             : #include <limits>
      25             : #include <set>
      26             : #include <vector>
      27             : 
      28             : //! @cond Doxygen_Suppress
      29             : 
      30             : #ifndef _
      31             : #define _(x) (x)
      32             : #endif
      33             : 
      34             : /************************************************************************/
      35             : /*     GDALRasterPixelInfoAlgorithm::GDALRasterPixelInfoAlgorithm()     */
      36             : /************************************************************************/
      37             : 
      38         122 : GDALRasterPixelInfoAlgorithm::GDALRasterPixelInfoAlgorithm(bool standaloneStep)
      39             :     : GDALPipelineStepAlgorithm(
      40             :           NAME, DESCRIPTION, HELP_URL,
      41           0 :           ConstructorOptions()
      42         122 :               .SetStandaloneStep(standaloneStep)
      43         122 :               .SetAddAppendLayerArgument(false)
      44         122 :               .SetAddOverwriteLayerArgument(false)
      45         122 :               .SetAddUpdateArgument(false)
      46         122 :               .SetAddUpsertArgument(false)
      47         122 :               .SetAddSkipErrorsArgument(false)
      48         366 :               .SetOutputFormatCreateCapability(GDAL_DCAP_CREATE))
      49             : {
      50         122 :     if (standaloneStep)
      51             :     {
      52          92 :         AddProgressArg(/* hidden = */ true);
      53             :         AddOutputFormatArg(&m_format, false, false,
      54             :                            _("Output format (default is 'GeoJSON' if "
      55          92 :                              "'position-dataset' not specified)"))
      56             :             .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES,
      57         368 :                              {GDAL_DCAP_VECTOR, GDAL_DCAP_CREATE})
      58          92 :             .SetAvailableInPipelineStep(false);
      59          92 :         AddOpenOptionsArg(&m_openOptions).SetAvailableInPipelineStep(false);
      60          92 :         AddInputFormatsArg(&m_inputFormats)
      61         276 :             .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_RASTER})
      62          92 :             .SetAvailableInPipelineStep(false);
      63             :     }
      64             : 
      65         122 :     AddInputDatasetArg(&m_inputDataset, GDAL_OF_RASTER)
      66         244 :         .AddAlias("dataset")
      67         122 :         .SetMinCount(1)
      68         122 :         .SetMaxCount(1);
      69             : 
      70             :     {
      71             :         auto &coordinateDatasetArg =
      72             :             AddArg("position-dataset", '\0',
      73             :                    _("Vector dataset with coordinates"), &m_vectorDataset,
      74         244 :                    GDAL_OF_VECTOR)
      75         122 :                 .SetMutualExclusionGroup("position-dataset-pos");
      76         122 :         if (!standaloneStep)
      77          30 :             coordinateDatasetArg.SetPositional();
      78             : 
      79         122 :         SetAutoCompleteFunctionForFilename(coordinateDatasetArg,
      80             :                                            GDAL_OF_VECTOR);
      81             : 
      82             :         auto &layerArg = AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
      83         244 :                                 _("Input layer name"), &m_inputLayerNames)
      84         122 :                              .SetMaxCount(1)
      85         122 :                              .AddAlias("layer");
      86         122 :         SetAutoCompleteFunctionForLayerName(layerArg, coordinateDatasetArg);
      87             :     }
      88             : 
      89             :     AddOutputDatasetArg(&m_outputDataset, GDAL_OF_VECTOR,
      90         122 :                         /* positionalAndRequired = */ false)
      91         122 :         .SetHiddenForCLI(!standaloneStep);
      92         122 :     if (standaloneStep)
      93             :     {
      94          92 :         AddCreationOptionsArg(&m_creationOptions)
      95          92 :             .SetAvailableInPipelineStep(false);
      96          92 :         AddLayerCreationOptionsArg(&m_layerCreationOptions)
      97          92 :             .SetAvailableInPipelineStep(false);
      98          92 :         AddOverwriteArg(&m_overwrite).SetAvailableInPipelineStep(false);
      99          92 :         AddOutputStringArg(&m_output)
     100          92 :             .SetHiddenForCLI()
     101          92 :             .SetAvailableInPipelineStep(false);
     102             :     }
     103             : 
     104         122 :     AddBandArg(&m_band);
     105             :     AddArg("overview", 0, _("Which overview level of source file must be used"),
     106         244 :            &m_overview)
     107         122 :         .SetMinValueIncluded(0);
     108             : 
     109             :     auto &positionArg =
     110         244 :         AddArg("position", 'p', _("Pixel position"), &m_pos)
     111         244 :             .AddAlias("pos")
     112         244 :             .SetMetaVar("<column,line> or <X,Y>")
     113         244 :             .SetMutualExclusionGroup("position-dataset-pos")
     114             :             .AddValidationAction(
     115          51 :                 [this]
     116             :                 {
     117          50 :                     if ((m_pos.size() % 2) != 0)
     118             :                     {
     119           1 :                         ReportError(
     120             :                             CE_Failure, CPLE_IllegalArg,
     121             :                             "An even number of values must be specified "
     122             :                             "for 'position' argument");
     123           1 :                         return false;
     124             :                     }
     125          49 :                     return true;
     126         122 :                 });
     127         122 :     if (standaloneStep)
     128          92 :         positionArg.SetPositional();
     129             : 
     130             :     AddArg("position-crs", 0,
     131             :            _("CRS of position (default is 'pixel' if 'position-dataset' not "
     132             :              "specified)"),
     133         244 :            &m_posCrs)
     134         488 :         .SetIsCRSArg(false, {"pixel", "dataset"})
     135         122 :         .AddHiddenAlias("l_srs");
     136             : 
     137             :     AddArg("resampling", 'r', _("Resampling algorithm for interpolation"),
     138         244 :            &m_resampling)
     139         122 :         .SetDefault(m_resampling)
     140         122 :         .SetChoices("nearest", "bilinear", "cubic", "cubicspline")
     141         122 :         .SetHiddenChoices("near");
     142             : 
     143             :     AddArg(
     144             :         "promote-pixel-value-to-z", 0,
     145             :         _("Whether to set the pixel value as Z component of GeoJSON geometry"),
     146         122 :         &m_promotePixelValueToZ);
     147             : 
     148             :     AddArg("include-field", 0,
     149             :            _("Fields from coordinate dataset to include in output (special "
     150             :              "values: ALL and NONE)"),
     151         244 :            &m_includeFields)
     152         122 :         .SetDefault("ALL");
     153             : 
     154         122 :     AddValidationAction(
     155         184 :         [this]
     156             :         {
     157          63 :             if (m_inputDataset.size() == 1)
     158             :             {
     159          59 :                 if (auto poSrcDS = m_inputDataset[0].GetDatasetRef())
     160             :                 {
     161          57 :                     if (poSrcDS->GetRasterCount() > 0)
     162             :                     {
     163             :                         const int nOvrCount =
     164          56 :                             poSrcDS->GetRasterBand(1)->GetOverviewCount();
     165          59 :                         if (m_overview >= 0 && poSrcDS->GetRasterCount() > 0 &&
     166           3 :                             m_overview >= nOvrCount)
     167             :                         {
     168           2 :                             if (nOvrCount == 0)
     169             :                             {
     170           1 :                                 ReportError(CE_Failure, CPLE_IllegalArg,
     171             :                                             "Source dataset has no overviews. "
     172             :                                             "Argument 'overview' must not be "
     173             :                                             "specified.");
     174             :                             }
     175             :                             else
     176             :                             {
     177           1 :                                 ReportError(
     178             :                                     CE_Failure, CPLE_IllegalArg,
     179             :                                     "Source dataset has only %d overview "
     180             :                                     "level%s. "
     181             :                                     "'overview' "
     182             :                                     "value must be strictly lower than this "
     183             :                                     "number.",
     184             :                                     nOvrCount, nOvrCount > 1 ? "s" : "");
     185             :                             }
     186           2 :                             return false;
     187             :                         }
     188             :                     }
     189             :                     else
     190             :                     {
     191           1 :                         ReportError(CE_Failure, CPLE_IllegalArg,
     192             :                                     "Source dataset has no raster band.");
     193           1 :                         return false;
     194             :                     }
     195             :                 }
     196             :             }
     197          60 :             return true;
     198             :         });
     199         122 : }
     200             : 
     201             : /************************************************************************/
     202             : /*    GDALRasterPixelInfoAlgorithm::~GDALRasterPixelInfoAlgorithm()     */
     203             : /************************************************************************/
     204             : 
     205         152 : GDALRasterPixelInfoAlgorithm::~GDALRasterPixelInfoAlgorithm()
     206             : {
     207         122 :     if (!m_osTmpFilename.empty())
     208             :     {
     209           6 :         VSIRmdir(CPLGetPathSafe(m_osTmpFilename.c_str()).c_str());
     210           6 :         VSIUnlink(m_osTmpFilename.c_str());
     211             :     }
     212         152 : }
     213             : 
     214             : /************************************************************************/
     215             : /*               GDALRasterPixelInfoAlgorithm::RunImpl()                */
     216             : /************************************************************************/
     217             : 
     218          43 : bool GDALRasterPixelInfoAlgorithm::RunImpl(GDALProgressFunc pfnProgress,
     219             :                                            void *pProgressData)
     220             : {
     221          43 :     GDALPipelineStepRunContext stepCtxt;
     222          43 :     stepCtxt.m_pfnProgress = pfnProgress;
     223          43 :     stepCtxt.m_pProgressData = pProgressData;
     224          43 :     return RunPreStepPipelineValidations() && RunStep(stepCtxt);
     225             : }
     226             : 
     227             : /************************************************************************/
     228             : /*               GDALRasterPixelInfoAlgorithm::RunStep()                */
     229             : /************************************************************************/
     230             : 
     231          49 : bool GDALRasterPixelInfoAlgorithm::RunStep(GDALPipelineStepRunContext &)
     232             : {
     233          49 :     auto poSrcDS = m_inputDataset[0].GetDatasetRef();
     234          49 :     CPLAssert(poSrcDS);
     235             : 
     236          49 :     auto poVectorSrcDS = m_vectorDataset.GetDatasetRef();
     237             : 
     238          49 :     if (m_pos.empty() && !poVectorSrcDS && !IsCalledFromCommandLine())
     239             :     {
     240           2 :         ReportError(
     241             :             CE_Failure, CPLE_AppDefined,
     242             :             "Argument 'position' or 'position-dataset' must be specified.");
     243           2 :         return false;
     244             :     }
     245             : 
     246          47 :     if (!m_standaloneStep)
     247             :     {
     248           5 :         m_format = "MEM";
     249             :     }
     250          42 :     else if (m_outputDataset.GetName().empty() && m_format != "MEM")
     251             :     {
     252          25 :         if (m_format.empty())
     253             :         {
     254          16 :             m_format = "GeoJSON";
     255             :         }
     256           9 :         else if (!EQUAL(m_format.c_str(), "CSV") &&
     257           0 :                  !EQUAL(m_format.c_str(), "GeoJSON"))
     258             :         {
     259           0 :             ReportError(CE_Failure, CPLE_AppDefined,
     260             :                         "Only CSV or GeoJSON output format is allowed when "
     261             :                         "'output' dataset is not specified.");
     262           0 :             return false;
     263             :         }
     264             :     }
     265          17 :     else if (m_format.empty())
     266             :     {
     267             :         const std::string osExt =
     268          26 :             CPLGetExtensionSafe(m_outputDataset.GetName().c_str());
     269          13 :         if (EQUAL(osExt.c_str(), "csv"))
     270           1 :             m_format = "CSV";
     271          12 :         else if (EQUAL(osExt.c_str(), "json"))
     272           1 :             m_format = "GeoJSON";
     273             :     }
     274             : 
     275          47 :     const bool bIsCSV = EQUAL(m_format.c_str(), "CSV");
     276          47 :     const bool bIsGeoJSON = EQUAL(m_format.c_str(), "GeoJSON");
     277             : 
     278          47 :     OGRLayer *poSrcLayer = nullptr;
     279          94 :     std::vector<int> anSrcFieldIndicesToInclude;
     280          94 :     std::vector<int> anMapSrcToDstFields;
     281             : 
     282          47 :     if (poVectorSrcDS)
     283             :     {
     284          20 :         if (m_inputLayerNames.empty())
     285             :         {
     286          18 :             const int nLayerCount = poVectorSrcDS->GetLayerCount();
     287          18 :             if (nLayerCount == 0)
     288             :             {
     289           2 :                 ReportError(CE_Failure, CPLE_AppDefined,
     290             :                             "Dataset '%s' has no vector layer",
     291           2 :                             poVectorSrcDS->GetDescription());
     292           2 :                 return false;
     293             :             }
     294          16 :             else if (nLayerCount > 1)
     295             :             {
     296           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     297             :                             "Dataset '%s' has more than one vector layer. "
     298             :                             "Please specify the 'input-layer' argument",
     299           1 :                             poVectorSrcDS->GetDescription());
     300           1 :                 return false;
     301             :             }
     302          15 :             poSrcLayer = poVectorSrcDS->GetLayer(0);
     303             :         }
     304             :         else
     305             :         {
     306             :             poSrcLayer =
     307           2 :                 poVectorSrcDS->GetLayerByName(m_inputLayerNames[0].c_str());
     308             :         }
     309          17 :         if (!poSrcLayer)
     310             :         {
     311           2 :             ReportError(
     312             :                 CE_Failure, CPLE_AppDefined, "Cannot find layer '%s' in '%s'",
     313           2 :                 m_inputLayerNames.empty() ? "" : m_inputLayerNames[0].c_str(),
     314           1 :                 poVectorSrcDS->GetDescription());
     315           1 :             return false;
     316             :         }
     317          16 :         if (poSrcLayer->GetGeomType() == wkbNone)
     318             :         {
     319           2 :             ReportError(CE_Failure, CPLE_AppDefined,
     320             :                         "Layer '%s' of '%s' has no geometry column",
     321           2 :                         poSrcLayer->GetName(), poVectorSrcDS->GetDescription());
     322           2 :             return false;
     323             :         }
     324             : 
     325          14 :         if (!GetFieldIndices(m_includeFields, OGRLayer::ToHandle(poSrcLayer),
     326             :                              anSrcFieldIndicesToInclude))
     327             :         {
     328           1 :             return false;
     329             :         }
     330             : 
     331          13 :         if (m_posCrs.empty())
     332             :         {
     333          10 :             const auto poVectorLayerSRS = poSrcLayer->GetSpatialRef();
     334          10 :             if (poVectorLayerSRS)
     335             :             {
     336           4 :                 const char *const apszOptions[] = {"FORMAT=WKT2", nullptr};
     337           4 :                 m_posCrs = poVectorLayerSRS->exportToWkt(apszOptions);
     338             :             }
     339             :         }
     340             :     }
     341             : 
     342          40 :     if (m_posCrs.empty())
     343          28 :         m_posCrs = "pixel";
     344             : 
     345             :     const GDALRIOResampleAlg eInterpolation =
     346          40 :         GDALRasterIOGetResampleAlg(m_resampling.c_str());
     347             : 
     348          40 :     const auto poSrcCRS = poSrcDS->GetSpatialRef();
     349          40 :     GDALGeoTransform gt;
     350          40 :     const bool bHasGT = poSrcDS->GetGeoTransform(gt) == CE_None;
     351          40 :     GDALGeoTransform invGT;
     352             : 
     353          40 :     if (m_posCrs != "pixel")
     354             :     {
     355          10 :         if (!poSrcCRS)
     356             :         {
     357           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     358             :                         "Dataset has no CRS. Only 'position-crs' = 'pixel' is "
     359             :                         "supported.");
     360           1 :             return false;
     361             :         }
     362             : 
     363           9 :         if (!bHasGT)
     364             :         {
     365           1 :             ReportError(CE_Failure, CPLE_AppDefined, "Cannot get geotransform");
     366           1 :             return false;
     367             :         }
     368             : 
     369           8 :         if (!gt.GetInverse(invGT))
     370             :         {
     371           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     372             :                         "Cannot invert geotransform");
     373           1 :             return false;
     374             :         }
     375             :     }
     376             : 
     377          37 :     std::unique_ptr<OGRCoordinateTransformation> poCT;
     378          74 :     OGRSpatialReference oUserCRS;
     379          37 :     if (m_posCrs != "pixel" && m_posCrs != "dataset")
     380             :     {
     381           6 :         oUserCRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     382             :         // Already validated due SetIsCRSArg()
     383           6 :         CPL_IGNORE_RET_VAL(oUserCRS.SetFromUserInput(m_posCrs.c_str()));
     384           6 :         poCT.reset(OGRCreateCoordinateTransformation(&oUserCRS, poSrcCRS));
     385           6 :         if (!poCT)
     386           0 :             return false;
     387             :     }
     388             : 
     389          37 :     if (m_band.empty())
     390             :     {
     391          74 :         for (int i = 1; i <= poSrcDS->GetRasterCount(); ++i)
     392          37 :             m_band.push_back(i);
     393             :     }
     394             : 
     395          37 :     std::unique_ptr<GDALDataset> poOutDS;
     396          37 :     OGRLayer *poOutLayer = nullptr;
     397          37 :     std::unique_ptr<OGRCoordinateTransformation> poCTSrcCRSToOutCRS;
     398             : 
     399             :     const bool isInteractive =
     400          61 :         m_pos.empty() && m_outputDataset.GetName().empty() &&
     401          61 :         IsCalledFromCommandLine() && CPLIsInteractive(stdin);
     402             : 
     403          74 :     std::string osOutFilename = m_outputDataset.GetName();
     404          37 :     if (osOutFilename.empty() && bIsCSV)
     405             :     {
     406           6 :         if (isInteractive)
     407             :         {
     408           0 :             osOutFilename = "/vsistdout/";
     409             :         }
     410             :         else
     411             :         {
     412           6 :             osOutFilename = VSIMemGenerateHiddenFilename("subdir");
     413           6 :             osOutFilename += "/out.csv";
     414           6 :             VSIMkdir(CPLGetPathSafe(osOutFilename.c_str()).c_str(), 0755);
     415           6 :             m_osTmpFilename = osOutFilename;
     416             :         }
     417             :     }
     418             : 
     419          37 :     if (!osOutFilename.empty() || m_format == "MEM" || !m_standaloneStep)
     420             :     {
     421          25 :         if (bIsGeoJSON)
     422             :         {
     423           1 :             m_outputFile.reset(VSIFOpenL(osOutFilename.c_str(), "wb"));
     424           1 :             if (!m_outputFile)
     425             :             {
     426           0 :                 ReportError(CE_Failure, CPLE_FileIO, "Cannot create '%s'",
     427             :                             osOutFilename.c_str());
     428           0 :                 return false;
     429             :             }
     430             :         }
     431             :         else
     432             :         {
     433          24 :             if (m_format.empty())
     434             :             {
     435             :                 const auto aosFormats =
     436             :                     CPLStringList(GDALGetOutputDriversForDatasetName(
     437             :                         osOutFilename.c_str(), GDAL_OF_VECTOR,
     438             :                         /* bSingleMatch = */ true,
     439          10 :                         /* bWarn = */ true));
     440          10 :                 if (aosFormats.size() != 1)
     441             :                 {
     442           1 :                     ReportError(CE_Failure, CPLE_AppDefined,
     443             :                                 "Cannot guess driver for %s",
     444             :                                 osOutFilename.c_str());
     445           1 :                     return false;
     446             :                 }
     447           9 :                 m_format = aosFormats[0];
     448             :             }
     449             : 
     450             :             auto poOutDrv =
     451          23 :                 GetGDALDriverManager()->GetDriverByName(m_format.c_str());
     452          23 :             if (!poOutDrv)
     453             :             {
     454           0 :                 ReportError(CE_Failure, CPLE_AppDefined,
     455             :                             "Cannot find driver %s", m_format.c_str());
     456           0 :                 return false;
     457             :             }
     458          23 :             poOutDS.reset(
     459             :                 poOutDrv->Create(osOutFilename.c_str(), 0, 0, 0, GDT_Unknown,
     460          46 :                                  CPLStringList(m_creationOptions).List()));
     461          23 :             if (!poOutDS)
     462           1 :                 return false;
     463             : 
     464          22 :             std::string osOutLayerName;
     465          22 :             if (EQUAL(m_format.c_str(), "ESRI Shapefile") || bIsCSV ||
     466             :                 !poSrcLayer)
     467             :             {
     468          10 :                 osOutLayerName = CPLGetBasenameSafe(osOutFilename.c_str());
     469             :             }
     470             :             else
     471          12 :                 osOutLayerName = poSrcLayer->GetName();
     472             : 
     473          22 :             const OGRSpatialReference *poOutCRS = nullptr;
     474          22 :             if (!oUserCRS.IsEmpty())
     475           5 :                 poOutCRS = &oUserCRS;
     476          17 :             else if (poSrcLayer)
     477             :             {
     478           7 :                 poOutCRS = poSrcLayer->GetSpatialRef();
     479           7 :                 if (!poOutCRS)
     480           7 :                     poOutCRS = poSrcCRS;
     481             :             }
     482          44 :             poOutLayer = poOutDS->CreateLayer(
     483             :                 osOutLayerName.c_str(), poOutCRS, wkbPoint,
     484          44 :                 CPLStringList(m_layerCreationOptions).List());
     485          22 :             if (!poOutLayer)
     486             :             {
     487           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
     488             :                             "Cannot create layer '%s' in '%s'",
     489             :                             osOutLayerName.c_str(), osOutFilename.c_str());
     490           1 :                 return false;
     491             :             }
     492          21 :             if (poSrcCRS && poOutCRS)
     493             :             {
     494          12 :                 poCTSrcCRSToOutCRS.reset(
     495             :                     OGRCreateCoordinateTransformation(poSrcCRS, poOutCRS));
     496          12 :                 if (!poCTSrcCRSToOutCRS)
     497           0 :                     return false;
     498             :             }
     499             :         }
     500             :     }
     501             : 
     502          34 :     if (poOutLayer)
     503             :     {
     504          21 :         bool bOK = true;
     505             : 
     506          21 :         if (bIsCSV)
     507             :         {
     508          16 :             OGRFieldDefn oFieldLine("geom_x", OFTReal);
     509           8 :             bOK &= poOutLayer->CreateField(&oFieldLine) == OGRERR_NONE;
     510             : 
     511           8 :             OGRFieldDefn oFieldColumn("geom_y", OFTReal);
     512           8 :             bOK &= poOutLayer->CreateField(&oFieldColumn) == OGRERR_NONE;
     513             :         }
     514             : 
     515          21 :         if (poSrcLayer)
     516             :         {
     517          24 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     518          12 :             const auto poSrcLayerDefn = poSrcLayer->GetLayerDefn();
     519          12 :             auto poOutLayerDefn = poOutLayer->GetLayerDefn();
     520             : 
     521          12 :             anMapSrcToDstFields.resize(poSrcLayerDefn->GetFieldCount(), -1);
     522             : 
     523          19 :             for (int nSrcIdx : anSrcFieldIndicesToInclude)
     524             :             {
     525             :                 const auto poSrcFieldDefn =
     526           7 :                     poSrcLayerDefn->GetFieldDefn(nSrcIdx);
     527           7 :                 if (poOutLayer->CreateField(poSrcFieldDefn) == OGRERR_NONE)
     528             :                 {
     529           7 :                     const int nDstIdx = poOutLayerDefn->GetFieldIndex(
     530           7 :                         poSrcFieldDefn->GetNameRef());
     531           7 :                     if (nDstIdx >= 0)
     532           7 :                         anMapSrcToDstFields[nSrcIdx] = nDstIdx;
     533             :                 }
     534             :             }
     535             :         }
     536             :         else
     537             :         {
     538           9 :             OGRFieldDefn oFieldExtraInput("extra_content", OFTString);
     539           9 :             bOK &= poOutLayer->CreateField(&oFieldExtraInput) == OGRERR_NONE;
     540             :         }
     541             : 
     542          21 :         OGRFieldDefn oFieldColumn("column", OFTReal);
     543          21 :         bOK &= poOutLayer->CreateField(&oFieldColumn) == OGRERR_NONE;
     544             : 
     545          21 :         OGRFieldDefn oFieldLine("line", OFTReal);
     546          21 :         bOK &= poOutLayer->CreateField(&oFieldLine) == OGRERR_NONE;
     547             : 
     548          42 :         for (int nBand : m_band)
     549             :         {
     550             :             auto hBand =
     551          21 :                 GDALRasterBand::ToHandle(poSrcDS->GetRasterBand(nBand));
     552          21 :             const bool bIsComplex = CPL_TO_BOOL(
     553             :                 GDALDataTypeIsComplex(GDALGetRasterDataType(hBand)));
     554          21 :             if (bIsComplex)
     555             :             {
     556             :                 OGRFieldDefn oFieldReal(CPLSPrintf("band_%d_real_value", nBand),
     557           6 :                                         OFTReal);
     558           3 :                 bOK &= poOutLayer->CreateField(&oFieldReal) == OGRERR_NONE;
     559             : 
     560             :                 OGRFieldDefn oFieldImag(
     561           3 :                     CPLSPrintf("band_%d_imaginary_value", nBand), OFTReal);
     562           3 :                 bOK &= poOutLayer->CreateField(&oFieldImag) == OGRERR_NONE;
     563             :             }
     564             :             else
     565             :             {
     566             :                 OGRFieldDefn oFieldRaw(CPLSPrintf("band_%d_raw_value", nBand),
     567          36 :                                        OFTReal);
     568          18 :                 bOK &= poOutLayer->CreateField(&oFieldRaw) == OGRERR_NONE;
     569             : 
     570             :                 OGRFieldDefn oFieldUnscaled(
     571          18 :                     CPLSPrintf("band_%d_unscaled_value", nBand), OFTReal);
     572          18 :                 bOK &= poOutLayer->CreateField(&oFieldUnscaled) == OGRERR_NONE;
     573             :             }
     574             :         }
     575             : 
     576          21 :         if (!bOK)
     577             :         {
     578           0 :             ReportError(CE_Failure, CPLE_AppDefined,
     579             :                         "Cannot create fields in output layer");
     580           0 :             return false;
     581             :         }
     582             :     }
     583             : 
     584          68 :     CPLJSONObject oCollection;
     585          34 :     oCollection.Add("type", "FeatureCollection");
     586          34 :     std::unique_ptr<OGRCoordinateTransformation> poCTToWGS84;
     587          34 :     bool canOutputGeoJSONGeom = false;
     588          34 :     if (poSrcCRS && bHasGT)
     589             :     {
     590          32 :         const char *pszAuthName = poSrcCRS->GetAuthorityName();
     591          32 :         const char *pszAuthCode = poSrcCRS->GetAuthorityCode();
     592          32 :         if (pszAuthName && pszAuthCode && EQUAL(pszAuthName, "EPSG"))
     593             :         {
     594          31 :             canOutputGeoJSONGeom = true;
     595          62 :             CPLJSONObject jCRS;
     596          31 :             CPLJSONObject oProperties;
     597          31 :             if (EQUAL(pszAuthCode, "4326"))
     598           2 :                 oProperties.Add("name", "urn:ogc:def:crs:OGC:1.3:CRS84");
     599             :             else
     600          29 :                 oProperties.Add(
     601             :                     "name",
     602          58 :                     std::string("urn:ogc:def:crs:EPSG::").append(pszAuthCode));
     603          31 :             jCRS.Add("type", "name");
     604          31 :             jCRS.Add("properties", oProperties);
     605          62 :             oCollection.Add("crs", jCRS);
     606             :         }
     607             :         else
     608             :         {
     609           2 :             OGRSpatialReference oCRS;
     610           1 :             oCRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     611           1 :             oCRS.importFromEPSG(4326);
     612           1 :             poCTToWGS84.reset(
     613             :                 OGRCreateCoordinateTransformation(poSrcCRS, &oCRS));
     614           1 :             if (poCTToWGS84)
     615             :             {
     616           1 :                 canOutputGeoJSONGeom = true;
     617           2 :                 CPLJSONObject jCRS;
     618           1 :                 CPLJSONObject oProperties;
     619           1 :                 oProperties.Add("name", "urn:ogc:def:crs:OGC:1.3:CRS84");
     620           1 :                 jCRS.Add("type", "name");
     621           1 :                 jCRS.Add("properties", oProperties);
     622           1 :                 oCollection.Add("crs", jCRS);
     623             :             }
     624             :         }
     625             :     }
     626          68 :     CPLJSONArray oFeatures;
     627          34 :     oCollection.Add("features", oFeatures);
     628             : 
     629             :     char szLine[1024];
     630          34 :     int nLine = 0;
     631          34 :     size_t iVal = 0;
     632          16 :     do
     633             :     {
     634          50 :         double x = 0, y = 0;
     635          50 :         std::string osExtraContent;
     636           0 :         std::unique_ptr<OGRFeature> poSrcFeature;
     637          50 :         if (poSrcLayer)
     638             :         {
     639          26 :             poSrcFeature.reset(poSrcLayer->GetNextFeature());
     640          26 :             if (!poSrcFeature)
     641          13 :                 break;
     642          13 :             const auto poGeom = poSrcFeature->GetGeometryRef();
     643          13 :             if (!poGeom || poGeom->IsEmpty())
     644           0 :                 continue;
     645          13 :             if (wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
     646             :             {
     647          13 :                 x = poGeom->toPoint()->getX();
     648          13 :                 y = poGeom->toPoint()->getY();
     649             :             }
     650             :             else
     651             :             {
     652           0 :                 OGRPoint oPoint;
     653           0 :                 if (poGeom->Centroid(&oPoint) != OGRERR_NONE)
     654           0 :                     return false;
     655           0 :                 x = oPoint.getX();
     656           0 :                 y = oPoint.getY();
     657             :             }
     658             :         }
     659          24 :         else if (iVal + 1 < m_pos.size())
     660             :         {
     661          18 :             x = m_pos[iVal++];
     662          18 :             y = m_pos[iVal++];
     663             :         }
     664             :         else
     665             :         {
     666           6 :             if (CPLIsInteractive(stdin))
     667             :             {
     668           0 :                 if (m_posCrs != "pixel")
     669             :                 {
     670           0 :                     fprintf(stderr, "Enter X Y values separated by space, and "
     671             :                                     "press Return.\n");
     672             :                 }
     673             :                 else
     674             :                 {
     675           0 :                     fprintf(stderr,
     676             :                             "Enter pixel line values separated by space, "
     677             :                             "and press Return.\n");
     678             :                 }
     679             :             }
     680             : 
     681           6 :             if (fgets(szLine, sizeof(szLine) - 1, stdin) && szLine[0] != '\n')
     682             :             {
     683           3 :                 const CPLStringList aosTokens(CSLTokenizeString(szLine));
     684           3 :                 const int nCount = aosTokens.size();
     685             : 
     686           3 :                 ++nLine;
     687           3 :                 if (nCount < 2)
     688             :                 {
     689           0 :                     fprintf(stderr, "Not enough values at line %d\n", nLine);
     690           0 :                     return false;
     691             :                 }
     692             :                 else
     693             :                 {
     694           3 :                     x = CPLAtof(aosTokens[0]);
     695           3 :                     y = CPLAtof(aosTokens[1]);
     696             : 
     697           9 :                     for (int i = 2; i < nCount; ++i)
     698             :                     {
     699           6 :                         if (!osExtraContent.empty())
     700           3 :                             osExtraContent += ' ';
     701           6 :                         osExtraContent += aosTokens[i];
     702             :                     }
     703           6 :                     while (!osExtraContent.empty() &&
     704           3 :                            isspace(static_cast<int>(osExtraContent.back())))
     705             :                     {
     706           0 :                         osExtraContent.pop_back();
     707             :                     }
     708             :                 }
     709             :             }
     710             :             else
     711             :             {
     712           3 :                 break;
     713             :             }
     714             :         }
     715             : 
     716          34 :         const double xOri = x;
     717          34 :         const double yOri = y;
     718          34 :         double dfPixel{0}, dfLine{0};
     719             : 
     720          34 :         if (poCT)
     721             :         {
     722           6 :             if (!poCT->Transform(1, &x, &y, nullptr))
     723           0 :                 return false;
     724             :         }
     725             : 
     726          34 :         if (m_posCrs != "pixel")
     727             :         {
     728           7 :             invGT.Apply(x, y, &dfPixel, &dfLine);
     729             :         }
     730             :         else
     731             :         {
     732          27 :             dfPixel = x;
     733          27 :             dfLine = y;
     734             :         }
     735             :         const int iPixel = static_cast<int>(
     736          68 :             std::clamp(std::floor(dfPixel), static_cast<double>(INT_MIN),
     737          34 :                        static_cast<double>(INT_MAX)));
     738             :         const int iLine = static_cast<int>(
     739          68 :             std::clamp(std::floor(dfLine), static_cast<double>(INT_MIN),
     740          34 :                        static_cast<double>(INT_MAX)));
     741             : 
     742          34 :         CPLJSONObject oFeature;
     743          34 :         CPLJSONObject oProperties;
     744           0 :         std::unique_ptr<OGRFeature> poFeature;
     745          34 :         if (bIsGeoJSON)
     746             :         {
     747          13 :             oFeature.Add("type", "Feature");
     748          13 :             oFeature.Add("properties", oProperties);
     749             :             {
     750          13 :                 CPLJSONArray oArray;
     751          13 :                 oArray.Add(xOri);
     752          13 :                 oArray.Add(yOri);
     753          13 :                 oProperties.Add("input_coordinate", oArray);
     754             :             }
     755          13 :             if (!osExtraContent.empty())
     756           1 :                 oProperties.Add("extra_content", osExtraContent);
     757          13 :             oProperties.Add("column", dfPixel);
     758          13 :             oProperties.Add("line", dfLine);
     759             : 
     760          13 :             if (poSrcFeature)
     761             :             {
     762           8 :                 for (int i : anSrcFieldIndicesToInclude)
     763             :                 {
     764           7 :                     const auto *poFieldDefn = poSrcFeature->GetFieldDefnRef(i);
     765           7 :                     const char *pszName = poFieldDefn->GetNameRef();
     766           7 :                     const auto eType = poFieldDefn->GetType();
     767           7 :                     switch (eType)
     768             :                     {
     769           3 :                         case OFTInteger:
     770             :                         case OFTInteger64:
     771             :                         {
     772           3 :                             if (poFieldDefn->GetSubType() == OFSTBoolean)
     773             :                             {
     774           1 :                                 oProperties.Add(
     775             :                                     pszName,
     776           1 :                                     poSrcFeature->GetFieldAsInteger(i) != 0);
     777             :                             }
     778             :                             else
     779             :                             {
     780           2 :                                 oProperties.Add(
     781             :                                     pszName,
     782             :                                     poSrcFeature->GetFieldAsInteger64(i));
     783             :                             }
     784           3 :                             break;
     785             :                         }
     786             : 
     787           1 :                         case OFTReal:
     788             :                         {
     789           1 :                             oProperties.Add(pszName,
     790             :                                             poSrcFeature->GetFieldAsDouble(i));
     791           1 :                             break;
     792             :                         }
     793             : 
     794           2 :                         case OFTString:
     795             :                         {
     796           2 :                             if (poFieldDefn->GetSubType() != OFSTJSON)
     797             :                             {
     798           1 :                                 oProperties.Add(
     799             :                                     pszName, poSrcFeature->GetFieldAsString(i));
     800           1 :                                 break;
     801             :                             }
     802             :                             else
     803             :                             {
     804             :                                 [[fallthrough]];
     805             :                             }
     806             :                         }
     807             : 
     808             :                         default:
     809             :                         {
     810             :                             char *pszJSON =
     811           2 :                                 poSrcFeature->GetFieldAsSerializedJSon(i);
     812           4 :                             CPLJSONDocument oDoc;
     813           2 :                             if (oDoc.LoadMemory(pszJSON))
     814           2 :                                 oProperties.Add(pszName, oDoc.GetRoot());
     815           2 :                             CPLFree(pszJSON);
     816           2 :                             break;
     817             :                         }
     818             :                     }
     819             :                 }
     820             :             }
     821             :         }
     822             :         else
     823             :         {
     824          21 :             CPLAssert(poOutLayer);
     825             :             poFeature =
     826          21 :                 std::make_unique<OGRFeature>(poOutLayer->GetLayerDefn());
     827             : 
     828          21 :             if (poSrcFeature)
     829             :             {
     830          24 :                 CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     831          24 :                 poFeature->SetFrom(poSrcFeature.get(),
     832          12 :                                    anMapSrcToDstFields.data());
     833             :             }
     834           9 :             else if (!osExtraContent.empty())
     835             :             {
     836           2 :                 poFeature->SetField("extra_content", osExtraContent.c_str());
     837             :             }
     838             : 
     839          21 :             if (poOutLayer->GetSpatialRef() == nullptr)
     840             :             {
     841           9 :                 if (bIsCSV)
     842             :                 {
     843           8 :                     poFeature->SetField("geom_x", xOri);
     844           8 :                     poFeature->SetField("geom_y", yOri);
     845             :                 }
     846             :                 else
     847             :                 {
     848           2 :                     poFeature->SetGeometry(
     849           2 :                         std::make_unique<OGRPoint>(xOri, yOri));
     850             :                 }
     851             :             }
     852          12 :             else if (bHasGT && poCTSrcCRSToOutCRS)
     853             :             {
     854          12 :                 gt.Apply(dfPixel, dfLine, &x, &y);
     855          12 :                 if (poCTSrcCRSToOutCRS->Transform(1, &x, &y))
     856             :                 {
     857          12 :                     if (bIsCSV)
     858             :                     {
     859           0 :                         poFeature->SetField("geom_x", x);
     860           0 :                         poFeature->SetField("geom_y", y);
     861             :                     }
     862             :                     else
     863             :                     {
     864          24 :                         poFeature->SetGeometry(
     865          24 :                             std::make_unique<OGRPoint>(x, y));
     866             :                     }
     867             :                 }
     868             :             }
     869             : 
     870          21 :             poFeature->SetField("column", dfPixel);
     871          21 :             poFeature->SetField("line", dfLine);
     872             :         }
     873             : 
     874          34 :         CPLJSONArray oBands;
     875             : 
     876          34 :         double zValue = std::numeric_limits<double>::quiet_NaN();
     877          68 :         for (int nBand : m_band)
     878             :         {
     879          34 :             CPLJSONObject oBand;
     880          34 :             oBand.Add("band_number", nBand);
     881             : 
     882             :             auto hBand =
     883          34 :                 GDALRasterBand::ToHandle(poSrcDS->GetRasterBand(nBand));
     884             : 
     885          34 :             int iPixelToQuery = iPixel;
     886          34 :             int iLineToQuery = iLine;
     887             : 
     888          34 :             double dfPixelToQuery = dfPixel;
     889          34 :             double dfLineToQuery = dfLine;
     890             : 
     891          34 :             if (m_overview >= 0 && hBand != nullptr)
     892             :             {
     893           1 :                 GDALRasterBandH hOvrBand = GDALGetOverview(hBand, m_overview);
     894           1 :                 if (hOvrBand != nullptr)
     895             :                 {
     896           1 :                     int nOvrXSize = GDALGetRasterBandXSize(hOvrBand);
     897           1 :                     int nOvrYSize = GDALGetRasterBandYSize(hOvrBand);
     898           1 :                     iPixelToQuery = static_cast<int>(
     899           1 :                         0.5 +
     900           1 :                         1.0 * iPixel / poSrcDS->GetRasterXSize() * nOvrXSize);
     901           1 :                     iLineToQuery = static_cast<int>(
     902           1 :                         0.5 +
     903           1 :                         1.0 * iLine / poSrcDS->GetRasterYSize() * nOvrYSize);
     904           1 :                     if (iPixelToQuery >= nOvrXSize)
     905           0 :                         iPixelToQuery = nOvrXSize - 1;
     906           1 :                     if (iLineToQuery >= nOvrYSize)
     907           0 :                         iLineToQuery = nOvrYSize - 1;
     908           1 :                     dfPixelToQuery =
     909           1 :                         dfPixel / poSrcDS->GetRasterXSize() * nOvrXSize;
     910           1 :                     dfLineToQuery =
     911           1 :                         dfLine / poSrcDS->GetRasterYSize() * nOvrYSize;
     912             :                 }
     913             :                 else
     914             :                 {
     915           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
     916             :                                 "Cannot get overview %d of band %d", m_overview,
     917             :                                 nBand);
     918           0 :                     return false;
     919             :                 }
     920           1 :                 hBand = hOvrBand;
     921             :             }
     922             : 
     923          34 :             double adfPixel[2] = {0, 0};
     924          34 :             const bool bIsComplex = CPL_TO_BOOL(
     925             :                 GDALDataTypeIsComplex(GDALGetRasterDataType(hBand)));
     926             :             int bIgnored;
     927          34 :             const double dfOffset = GDALGetRasterOffset(hBand, &bIgnored);
     928          34 :             const double dfScale = GDALGetRasterScale(hBand, &bIgnored);
     929          34 :             if (GDALRasterInterpolateAtPoint(
     930             :                     hBand, dfPixelToQuery, dfLineToQuery, eInterpolation,
     931          34 :                     &adfPixel[0], &adfPixel[1]) == CE_None)
     932             :             {
     933          32 :                 if (!bIsComplex)
     934             :                 {
     935          29 :                     const double dfUnscaledVal =
     936          29 :                         adfPixel[0] * dfScale + dfOffset;
     937          29 :                     if (m_band.size() == 1 && m_promotePixelValueToZ)
     938           1 :                         zValue = dfUnscaledVal;
     939          29 :                     if (bIsGeoJSON)
     940             :                     {
     941          12 :                         if (GDALDataTypeIsInteger(GDALGetRasterDataType(hBand)))
     942             :                         {
     943          11 :                             oBand.Add("raw_value",
     944          11 :                                       static_cast<GInt64>(adfPixel[0]));
     945             :                         }
     946             :                         else
     947             :                         {
     948           1 :                             oBand.Add("raw_value", adfPixel[0]);
     949             :                         }
     950             : 
     951          12 :                         oBand.Add("unscaled_value", dfUnscaledVal);
     952             :                     }
     953             :                     else
     954             :                     {
     955          17 :                         poFeature->SetField(
     956             :                             CPLSPrintf("band_%d_raw_value", nBand),
     957             :                             adfPixel[0]);
     958          17 :                         poFeature->SetField(
     959             :                             CPLSPrintf("band_%d_unscaled_value", nBand),
     960             :                             dfUnscaledVal);
     961             :                     }
     962             :                 }
     963             :                 else
     964             :                 {
     965           3 :                     if (bIsGeoJSON)
     966             :                     {
     967           1 :                         CPLJSONObject oValue;
     968           1 :                         oValue.Add("real", adfPixel[0]);
     969           1 :                         oValue.Add("imaginary", adfPixel[1]);
     970           1 :                         oBand.Add("value", oValue);
     971             :                     }
     972             :                     else
     973             :                     {
     974           2 :                         poFeature->SetField(
     975             :                             CPLSPrintf("band_%d_real_value", nBand),
     976             :                             adfPixel[0]);
     977           2 :                         poFeature->SetField(
     978             :                             CPLSPrintf("band_%d_imaginary_value", nBand),
     979             :                             adfPixel[1]);
     980             :                     }
     981             :                 }
     982             :             }
     983             : 
     984             :             // Request location info for this location (just a few drivers,
     985             :             // like the VRT driver actually supports this).
     986          68 :             CPLString osItem;
     987          34 :             osItem.Printf("Pixel_%d_%d", iPixelToQuery, iLineToQuery);
     988             : 
     989          34 :             if (const char *pszLI =
     990          34 :                     GDALGetMetadataItem(hBand, osItem, "LocationInfo"))
     991             :             {
     992           4 :                 CPLXMLTreeCloser oTree(CPLParseXMLString(pszLI));
     993             : 
     994           4 :                 if (oTree && oTree->psChild != nullptr &&
     995           6 :                     oTree->eType == CXT_Element &&
     996           2 :                     EQUAL(oTree->pszValue, "LocationInfo"))
     997             :                 {
     998           2 :                     CPLJSONArray oFiles;
     999             : 
    1000           2 :                     for (const CPLXMLNode *psNode = oTree->psChild;
    1001           4 :                          psNode != nullptr; psNode = psNode->psNext)
    1002             :                     {
    1003           2 :                         if (psNode->eType == CXT_Element &&
    1004           2 :                             EQUAL(psNode->pszValue, "File") &&
    1005           2 :                             psNode->psChild != nullptr)
    1006             :                         {
    1007           4 :                             char *pszUnescaped = CPLUnescapeString(
    1008           2 :                                 psNode->psChild->pszValue, nullptr, CPLES_XML);
    1009           2 :                             oFiles.Add(pszUnescaped);
    1010           2 :                             CPLFree(pszUnescaped);
    1011             :                         }
    1012             :                     }
    1013             : 
    1014           2 :                     oBand.Add("files", oFiles);
    1015             :                 }
    1016             :                 else
    1017             :                 {
    1018           0 :                     oBand.Add("location_info", pszLI);
    1019             :                 }
    1020             :             }
    1021             : 
    1022          34 :             oBands.Add(oBand);
    1023             :         }
    1024             : 
    1025          34 :         if (bIsGeoJSON)
    1026             :         {
    1027          13 :             oProperties.Add("bands", oBands);
    1028             : 
    1029          13 :             if (canOutputGeoJSONGeom)
    1030             :             {
    1031          12 :                 x = dfPixel;
    1032          12 :                 y = dfLine;
    1033             : 
    1034          12 :                 gt.Apply(x, y, &x, &y);
    1035             : 
    1036          12 :                 if (poCTToWGS84)
    1037           1 :                     poCTToWGS84->Transform(1, &x, &y);
    1038             : 
    1039          24 :                 CPLJSONObject oGeometry;
    1040          12 :                 oFeature.Add("geometry", oGeometry);
    1041          12 :                 oGeometry.Add("type", "Point");
    1042          12 :                 CPLJSONArray oCoordinates;
    1043          12 :                 oCoordinates.Add(x);
    1044          12 :                 oCoordinates.Add(y);
    1045          12 :                 if (!std::isnan(zValue))
    1046           1 :                     oCoordinates.Add(zValue);
    1047          12 :                 oGeometry.Add("coordinates", oCoordinates);
    1048             :             }
    1049             :             else
    1050             :             {
    1051           1 :                 oFeature.AddNull("geometry");
    1052             :             }
    1053             : 
    1054          13 :             if (isInteractive)
    1055             :             {
    1056           0 :                 CPLJSONDocument oDoc;
    1057           0 :                 oDoc.SetRoot(oFeature);
    1058           0 :                 printf("%s\n", oDoc.SaveAsString().c_str());
    1059             :             }
    1060             :             else
    1061             :             {
    1062          13 :                 oFeatures.Add(oFeature);
    1063             :             }
    1064             :         }
    1065             :         else
    1066             :         {
    1067          21 :             if (poOutLayer->CreateFeature(std::move(poFeature)) != OGRERR_NONE)
    1068             :             {
    1069           0 :                 return false;
    1070             :             }
    1071             :         }
    1072             : 
    1073          34 :     } while (m_pos.empty() || iVal + 1 < m_pos.size());
    1074             : 
    1075          34 :     if (bIsGeoJSON && !isInteractive)
    1076             :     {
    1077          26 :         CPLJSONDocument oDoc;
    1078          13 :         oDoc.SetRoot(oCollection);
    1079          26 :         std::string osRet = oDoc.SaveAsString();
    1080          13 :         if (m_outputFile)
    1081           1 :             m_outputFile->Write(osRet.data(), osRet.size());
    1082             :         else
    1083          25 :             m_output = std::move(osRet);
    1084             :     }
    1085          21 :     else if (poOutDS)
    1086             :     {
    1087          27 :         if (m_format != "MEM" && m_outputDataset.GetName().empty() &&
    1088           6 :             m_standaloneStep)
    1089             :         {
    1090           6 :             poOutDS.reset();
    1091           6 :             if (!isInteractive)
    1092             :             {
    1093           6 :                 CPLAssert(!m_osTmpFilename.empty());
    1094             :                 const GByte *pabyData =
    1095           6 :                     VSIGetMemFileBuffer(m_osTmpFilename.c_str(), nullptr,
    1096             :                                         /* bUnlinkAndSeize = */ false);
    1097           6 :                 m_output = reinterpret_cast<const char *>(pabyData);
    1098             :             }
    1099             :         }
    1100             :         else
    1101             :         {
    1102          15 :             m_outputDataset.Set(std::move(poOutDS));
    1103             :         }
    1104             :     }
    1105             : 
    1106          34 :     bool bRet = true;
    1107          34 :     if (m_outputFile)
    1108             :     {
    1109           1 :         bRet = m_outputFile->Close() == 0;
    1110           1 :         if (bRet)
    1111             :         {
    1112           1 :             poOutDS.reset(
    1113           1 :                 GDALDataset::Open(m_outputDataset.GetName().c_str(),
    1114             :                                   GDAL_OF_VECTOR | GDAL_OF_VERBOSE_ERROR));
    1115           1 :             bRet = poOutDS != nullptr;
    1116           1 :             m_outputDataset.Set(std::move(poOutDS));
    1117             :         }
    1118             :     }
    1119             : 
    1120          34 :     return bRet;
    1121             : }
    1122             : 
    1123             : GDALRasterPixelInfoAlgorithmStandalone::
    1124             :     ~GDALRasterPixelInfoAlgorithmStandalone() = default;
    1125             : 
    1126             : //! @endcond

Generated by: LCOV version 1.14