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

Generated by: LCOV version 1.14