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

Generated by: LCOV version 1.14