LCOV - code coverage report
Current view: top level - apps - gdalalg_raster_pixel_info.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 276 292 94.5 %
Date: 2025-05-15 18:21:54 Functions: 5 5 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 "ogr_spatialref.h"
      20             : 
      21             : #include <algorithm>
      22             : #include <cmath>
      23             : 
      24             : //! @cond Doxygen_Suppress
      25             : 
      26             : #ifndef _
      27             : #define _(x) (x)
      28             : #endif
      29             : 
      30             : /************************************************************************/
      31             : /*       GDALRasterPixelInfoAlgorithm::GDALRasterPixelInfoAlgorithm()   */
      32             : /************************************************************************/
      33             : 
      34          25 : GDALRasterPixelInfoAlgorithm::GDALRasterPixelInfoAlgorithm()
      35          25 :     : GDALAlgorithm(NAME, DESCRIPTION, HELP_URL)
      36             : {
      37          25 :     AddOutputFormatArg(&m_format)
      38          25 :         .SetDefault("geojson")
      39          25 :         .SetChoices("geojson", "csv")
      40          25 :         .SetHiddenChoices("json")
      41          25 :         .SetDefault(m_format);
      42          25 :     AddOpenOptionsArg(&m_openOptions);
      43          25 :     AddInputFormatsArg(&m_inputFormats)
      44          50 :         .AddMetadataItem(GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_RASTER});
      45          25 :     AddInputDatasetArg(&m_dataset, GDAL_OF_RASTER).AddAlias("dataset");
      46          25 :     AddOutputStringArg(&m_output);
      47             : 
      48          25 :     AddBandArg(&m_band);
      49             :     AddArg("overview", 0, _("Which overview level of source file must be used"),
      50          50 :            &m_overview)
      51          25 :         .SetMinValueIncluded(0);
      52             : 
      53          50 :     AddArg("position", 'p', _("Pixel position"), &m_pos)
      54          50 :         .AddAlias("pos")
      55          50 :         .SetMetaVar("<column,line> or <X,Y>")
      56          25 :         .SetPositional()
      57             :         .AddValidationAction(
      58          19 :             [this]
      59             :             {
      60          18 :                 if ((m_pos.size() % 2) != 0)
      61             :                 {
      62           1 :                     ReportError(CE_Failure, CPLE_IllegalArg,
      63             :                                 "An even number of values must be specified "
      64             :                                 "for 'position' argument");
      65           1 :                     return false;
      66             :                 }
      67          17 :                 return true;
      68          25 :             });
      69          50 :     AddArg("position-crs", 0, _("CRS of position"), &m_posCrs)
      70         100 :         .SetIsCRSArg(false, {"pixel", "dataset"})
      71          25 :         .SetDefault("pixel")
      72          25 :         .AddHiddenAlias("l_srs");
      73             : 
      74             :     AddArg("resampling", 'r', _("Resampling algorithm for interpolation"),
      75          50 :            &m_resampling)
      76          25 :         .SetDefault(m_resampling)
      77          25 :         .SetChoices("nearest", "bilinear", "cubic", "cubicspline")
      78          25 :         .SetHiddenChoices("near");
      79             : 
      80          25 :     AddValidationAction(
      81          53 :         [this]
      82             :         {
      83          24 :             if (auto poSrcDS = m_dataset.GetDatasetRef())
      84             :             {
      85             :                 const int nOvrCount =
      86          24 :                     poSrcDS->GetRasterBand(1)->GetOverviewCount();
      87          27 :                 if (m_overview >= 0 && poSrcDS->GetRasterCount() > 0 &&
      88           3 :                     m_overview >= nOvrCount)
      89             :                 {
      90           2 :                     if (nOvrCount == 0)
      91             :                     {
      92           1 :                         ReportError(
      93             :                             CE_Failure, CPLE_IllegalArg,
      94             :                             "Source dataset has no overviews. "
      95             :                             "Argument 'overview' must not be specified.");
      96             :                     }
      97             :                     else
      98             :                     {
      99           1 :                         ReportError(
     100             :                             CE_Failure, CPLE_IllegalArg,
     101             :                             "Source dataset has only %d overview level%s. "
     102             :                             "'overview' "
     103             :                             "value must be strictly lower than this number.",
     104             :                             nOvrCount, nOvrCount > 1 ? "s" : "");
     105             :                     }
     106           2 :                     return false;
     107             :                 }
     108             :             }
     109          22 :             return true;
     110             :         });
     111          25 : }
     112             : 
     113             : /************************************************************************/
     114             : /*              GDALRasterPixelInfoAlgorithm::PrintLine()               */
     115             : /************************************************************************/
     116             : 
     117          12 : void GDALRasterPixelInfoAlgorithm::PrintLine(const std::string &str)
     118             : {
     119          12 :     if (IsCalledFromCommandLine())
     120             :     {
     121           2 :         printf("%s\n", str.c_str());
     122             :     }
     123             :     else
     124             :     {
     125          10 :         m_output += str;
     126          10 :         m_output += '\n';
     127             :     }
     128          12 : }
     129             : 
     130             : /************************************************************************/
     131             : /*                GDALRasterPixelInfoAlgorithm::RunImpl()               */
     132             : /************************************************************************/
     133             : 
     134          20 : bool GDALRasterPixelInfoAlgorithm::RunImpl(GDALProgressFunc, void *)
     135             : {
     136          20 :     auto poSrcDS = m_dataset.GetDatasetRef();
     137          20 :     CPLAssert(poSrcDS);
     138             : 
     139          20 :     if (m_pos.empty() && !IsCalledFromCommandLine())
     140             :     {
     141           1 :         ReportError(CE_Failure, CPLE_AppDefined,
     142             :                     "Argument 'position' must be specified.");
     143           1 :         return false;
     144             :     }
     145             : 
     146             :     const GDALRIOResampleAlg eInterpolation =
     147          19 :         GDALRasterIOGetResampleAlg(m_resampling.c_str());
     148             : 
     149          19 :     const auto poSrcCRS = poSrcDS->GetSpatialRef();
     150          19 :     double adfGeoTransform[6] = {0, 0, 0, 0, 0, 0};
     151          19 :     const bool bHasGT = poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None;
     152          19 :     double adfInvGeoTransform[6] = {0, 0, 0, 0, 0, 0};
     153             : 
     154          19 :     if (m_posCrs != "pixel")
     155             :     {
     156           5 :         if (!poSrcCRS)
     157             :         {
     158           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     159             :                         "Dataset has no CRS. Only 'position-crs' = 'pixel' is "
     160             :                         "supported.");
     161           1 :             return false;
     162             :         }
     163             : 
     164           4 :         if (!bHasGT)
     165             :         {
     166           1 :             ReportError(CE_Failure, CPLE_AppDefined, "Cannot get geotransform");
     167           1 :             return false;
     168             :         }
     169             : 
     170           3 :         if (!GDALInvGeoTransform(adfGeoTransform, adfInvGeoTransform))
     171             :         {
     172           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     173             :                         "Cannot invert geotransform");
     174           1 :             return false;
     175             :         }
     176             :     }
     177             : 
     178          16 :     std::unique_ptr<OGRCoordinateTransformation> poCT;
     179          16 :     if (m_posCrs != "pixel" && m_posCrs != "dataset")
     180             :     {
     181           1 :         OGRSpatialReference oUserCRS;
     182           1 :         oUserCRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     183             :         // Already validated due SetIsCRSArg()
     184           1 :         CPL_IGNORE_RET_VAL(oUserCRS.SetFromUserInput(m_posCrs.c_str()));
     185           1 :         poCT.reset(OGRCreateCoordinateTransformation(&oUserCRS, poSrcCRS));
     186           1 :         if (!poCT)
     187           0 :             return false;
     188             :     }
     189             : 
     190          16 :     if (m_band.empty())
     191             :     {
     192          32 :         for (int i = 1; i <= poSrcDS->GetRasterCount(); ++i)
     193          16 :             m_band.push_back(i);
     194             :     }
     195             : 
     196          16 :     if (m_format == "csv")
     197             :     {
     198          12 :         std::string line = "input_x,input_y,extra_input,column,line";
     199          12 :         for (int nBand : m_band)
     200             :         {
     201             :             auto hBand =
     202           6 :                 GDALRasterBand::ToHandle(poSrcDS->GetRasterBand(nBand));
     203           6 :             const bool bIsComplex = CPL_TO_BOOL(
     204             :                 GDALDataTypeIsComplex(GDALGetRasterDataType(hBand)));
     205           6 :             if (bIsComplex)
     206             :                 line +=
     207             :                     CPLSPrintf(",band_%d_real_value,band_%d_imaginary_value",
     208           2 :                                nBand, nBand);
     209             :             else
     210             :             {
     211             :                 line += CPLSPrintf(",band_%d_raw_value,band_%d_unscaled_value",
     212           4 :                                    nBand, nBand);
     213             :             }
     214             :         }
     215           6 :         PrintLine(line);
     216             :     }
     217             : 
     218             :     const bool isInteractive =
     219          16 :         m_pos.empty() && IsCalledFromCommandLine() && CPLIsInteractive(stdin);
     220             : 
     221          32 :     CPLJSONObject oCollection;
     222          16 :     oCollection.Add("type", "FeatureCollection");
     223          16 :     std::unique_ptr<OGRCoordinateTransformation> poCTToWGS84;
     224          16 :     bool canOutputGeoJSONGeom = false;
     225          16 :     if (poSrcCRS && bHasGT)
     226             :     {
     227          14 :         const char *pszAuthName = poSrcCRS->GetAuthorityName(nullptr);
     228          14 :         const char *pszAuthCode = poSrcCRS->GetAuthorityCode(nullptr);
     229          14 :         if (pszAuthName && pszAuthCode && EQUAL(pszAuthName, "EPSG"))
     230             :         {
     231          13 :             canOutputGeoJSONGeom = true;
     232          26 :             CPLJSONObject jCRS;
     233          13 :             CPLJSONObject oProperties;
     234          13 :             if (EQUAL(pszAuthCode, "4326"))
     235           2 :                 oProperties.Add("name", "urn:ogc:def:crs:OGC:1.3:CRS84");
     236             :             else
     237          11 :                 oProperties.Add(
     238             :                     "name",
     239          22 :                     std::string("urn:ogc:def:crs:EPSG::").append(pszAuthCode));
     240          13 :             jCRS.Add("type", "name");
     241          13 :             jCRS.Add("properties", oProperties);
     242          26 :             oCollection.Add("crs", jCRS);
     243             :         }
     244             :         else
     245             :         {
     246           2 :             OGRSpatialReference oCRS;
     247           1 :             oCRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     248           1 :             oCRS.importFromEPSG(4326);
     249           1 :             poCTToWGS84.reset(
     250             :                 OGRCreateCoordinateTransformation(poSrcCRS, &oCRS));
     251           1 :             if (poCTToWGS84)
     252             :             {
     253           1 :                 canOutputGeoJSONGeom = true;
     254           2 :                 CPLJSONObject jCRS;
     255           1 :                 CPLJSONObject oProperties;
     256           1 :                 oProperties.Add("name", "urn:ogc:def:crs:OGC:1.3:CRS84");
     257           1 :                 jCRS.Add("type", "name");
     258           1 :                 jCRS.Add("properties", oProperties);
     259           1 :                 oCollection.Add("crs", jCRS);
     260             :             }
     261             :         }
     262             :     }
     263          32 :     CPLJSONArray oFeatures;
     264          16 :     oCollection.Add("features", oFeatures);
     265             : 
     266             :     char szLine[1024];
     267          16 :     int nLine = 0;
     268          16 :     size_t iVal = 0;
     269           2 :     do
     270             :     {
     271          18 :         double x = 0, y = 0;
     272          18 :         std::string osExtraContent;
     273          18 :         if (iVal + 1 < m_pos.size())
     274             :         {
     275          14 :             x = m_pos[iVal++];
     276          14 :             y = m_pos[iVal++];
     277             :         }
     278             :         else
     279             :         {
     280           4 :             if (CPLIsInteractive(stdin))
     281             :             {
     282           0 :                 if (m_posCrs != "pixel")
     283             :                 {
     284           0 :                     fprintf(stderr, "Enter X Y values separated by space, and "
     285             :                                     "press Return.\n");
     286             :                 }
     287             :                 else
     288             :                 {
     289           0 :                     fprintf(stderr,
     290             :                             "Enter pixel line values separated by space, "
     291             :                             "and press Return.\n");
     292             :                 }
     293             :             }
     294             : 
     295           4 :             if (fgets(szLine, sizeof(szLine) - 1, stdin) && szLine[0] != '\n')
     296             :             {
     297           2 :                 const CPLStringList aosTokens(CSLTokenizeString(szLine));
     298           2 :                 const int nCount = aosTokens.size();
     299             : 
     300           2 :                 ++nLine;
     301           2 :                 if (nCount < 2)
     302             :                 {
     303           0 :                     fprintf(stderr, "Not enough values at line %d\n", nLine);
     304           0 :                     return false;
     305             :                 }
     306             :                 else
     307             :                 {
     308           2 :                     x = CPLAtof(aosTokens[0]);
     309           2 :                     y = CPLAtof(aosTokens[1]);
     310             : 
     311           6 :                     for (int i = 2; i < nCount; ++i)
     312             :                     {
     313           4 :                         if (!osExtraContent.empty())
     314           2 :                             osExtraContent += ' ';
     315           4 :                         osExtraContent += aosTokens[i];
     316             :                     }
     317           4 :                     while (!osExtraContent.empty() &&
     318           2 :                            isspace(static_cast<int>(osExtraContent.back())))
     319             :                     {
     320           0 :                         osExtraContent.pop_back();
     321             :                     }
     322             :                 }
     323             :             }
     324             :             else
     325             :             {
     326           2 :                 break;
     327             :             }
     328             :         }
     329             : 
     330          16 :         const double xOri = x;
     331          16 :         const double yOri = y;
     332          16 :         double dfPixel{0}, dfLine{0};
     333             : 
     334          16 :         if (poCT)
     335             :         {
     336           1 :             if (!poCT->Transform(1, &x, &y, nullptr))
     337           0 :                 return false;
     338             :         }
     339             : 
     340          16 :         if (m_posCrs != "pixel")
     341             :         {
     342           2 :             GDALApplyGeoTransform(adfInvGeoTransform, x, y, &dfPixel, &dfLine);
     343             :         }
     344             :         else
     345             :         {
     346          14 :             dfPixel = x;
     347          14 :             dfLine = y;
     348             :         }
     349             :         const int iPixel = static_cast<int>(
     350          32 :             std::clamp(std::floor(dfPixel), static_cast<double>(INT_MIN),
     351          16 :                        static_cast<double>(INT_MAX)));
     352             :         const int iLine = static_cast<int>(
     353          32 :             std::clamp(std::floor(dfLine), static_cast<double>(INT_MIN),
     354          16 :                        static_cast<double>(INT_MAX)));
     355             : 
     356          16 :         std::string line;
     357          16 :         CPLJSONObject oFeature;
     358          16 :         CPLJSONObject oProperties;
     359          16 :         if (m_format == "csv")
     360             :         {
     361           6 :             line = CPLSPrintf("%.17g,%.17g", xOri, yOri);
     362           6 :             line += ",\"";
     363           6 :             line += CPLString(osExtraContent).replaceAll('"', "\"\"");
     364           6 :             line += '"';
     365           6 :             line += CPLSPrintf(",%.17g,%.17g", dfPixel, dfLine);
     366             :         }
     367             :         else
     368             :         {
     369          10 :             oFeature.Add("type", "Feature");
     370          10 :             oFeature.Add("properties", oProperties);
     371             :             {
     372          10 :                 CPLJSONArray oArray;
     373          10 :                 oArray.Add(xOri);
     374          10 :                 oArray.Add(yOri);
     375          10 :                 oProperties.Add("input_coordinate", oArray);
     376             :             }
     377          10 :             if (!osExtraContent.empty())
     378           1 :                 oProperties.Add("extra_content", osExtraContent);
     379          10 :             oProperties.Add("column", dfPixel);
     380          10 :             oProperties.Add("line", dfLine);
     381             :         }
     382             : 
     383          16 :         CPLJSONArray oBands;
     384             : 
     385          32 :         for (int nBand : m_band)
     386             :         {
     387          16 :             CPLJSONObject oBand;
     388          16 :             oBand.Add("band_number", nBand);
     389             : 
     390             :             auto hBand =
     391          16 :                 GDALRasterBand::ToHandle(poSrcDS->GetRasterBand(nBand));
     392             : 
     393          16 :             int iPixelToQuery = iPixel;
     394          16 :             int iLineToQuery = iLine;
     395             : 
     396          16 :             double dfPixelToQuery = dfPixel;
     397          16 :             double dfLineToQuery = dfLine;
     398             : 
     399          16 :             if (m_overview >= 0 && hBand != nullptr)
     400             :             {
     401           1 :                 GDALRasterBandH hOvrBand = GDALGetOverview(hBand, m_overview);
     402           1 :                 if (hOvrBand != nullptr)
     403             :                 {
     404           1 :                     int nOvrXSize = GDALGetRasterBandXSize(hOvrBand);
     405           1 :                     int nOvrYSize = GDALGetRasterBandYSize(hOvrBand);
     406           1 :                     iPixelToQuery = static_cast<int>(
     407           1 :                         0.5 +
     408           1 :                         1.0 * iPixel / poSrcDS->GetRasterXSize() * nOvrXSize);
     409           1 :                     iLineToQuery = static_cast<int>(
     410           1 :                         0.5 +
     411           1 :                         1.0 * iLine / poSrcDS->GetRasterYSize() * nOvrYSize);
     412           1 :                     if (iPixelToQuery >= nOvrXSize)
     413           0 :                         iPixelToQuery = nOvrXSize - 1;
     414           1 :                     if (iLineToQuery >= nOvrYSize)
     415           0 :                         iLineToQuery = nOvrYSize - 1;
     416           1 :                     dfPixelToQuery =
     417           1 :                         dfPixel / poSrcDS->GetRasterXSize() * nOvrXSize;
     418           1 :                     dfLineToQuery =
     419           1 :                         dfLine / poSrcDS->GetRasterYSize() * nOvrYSize;
     420             :                 }
     421             :                 else
     422             :                 {
     423           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
     424             :                                 "Cannot get overview %d of band %d", m_overview,
     425             :                                 nBand);
     426           0 :                     return false;
     427             :                 }
     428           1 :                 hBand = hOvrBand;
     429             :             }
     430             : 
     431          16 :             double adfPixel[2] = {0, 0};
     432          16 :             const bool bIsComplex = CPL_TO_BOOL(
     433             :                 GDALDataTypeIsComplex(GDALGetRasterDataType(hBand)));
     434             :             int bIgnored;
     435          16 :             const double dfOffset = GDALGetRasterOffset(hBand, &bIgnored);
     436          16 :             const double dfScale = GDALGetRasterScale(hBand, &bIgnored);
     437          16 :             if (GDALRasterInterpolateAtPoint(
     438             :                     hBand, dfPixelToQuery, dfLineToQuery, eInterpolation,
     439          16 :                     &adfPixel[0], &adfPixel[1]) == CE_None)
     440             :             {
     441          14 :                 if (!bIsComplex)
     442             :                 {
     443          12 :                     const double dfUnscaledVal =
     444          12 :                         adfPixel[0] * dfScale + dfOffset;
     445          12 :                     if (m_format == "csv")
     446             :                     {
     447           3 :                         line += CPLSPrintf(",%.17g", adfPixel[0]);
     448           3 :                         line += CPLSPrintf(",%.17g", dfUnscaledVal);
     449             :                     }
     450             :                     else
     451             :                     {
     452           9 :                         if (GDALDataTypeIsInteger(GDALGetRasterDataType(hBand)))
     453             :                         {
     454           8 :                             oBand.Add("raw_value",
     455           8 :                                       static_cast<GInt64>(adfPixel[0]));
     456             :                         }
     457             :                         else
     458             :                         {
     459           1 :                             oBand.Add("raw_value", adfPixel[0]);
     460             :                         }
     461             : 
     462           9 :                         oBand.Add("unscaled_value", dfUnscaledVal);
     463             :                     }
     464             :                 }
     465             :                 else
     466             :                 {
     467           2 :                     if (m_format == "csv")
     468             :                     {
     469             :                         line += CPLSPrintf(",%.17g,%.17g", adfPixel[0],
     470           1 :                                            adfPixel[1]);
     471             :                     }
     472             :                     else
     473             :                     {
     474           1 :                         CPLJSONObject oValue;
     475           1 :                         oValue.Add("real", adfPixel[0]);
     476           1 :                         oValue.Add("imaginary", adfPixel[1]);
     477           1 :                         oBand.Add("value", oValue);
     478             :                     }
     479             :                 }
     480             :             }
     481           2 :             else if (m_format == "csv")
     482             :             {
     483           2 :                 line += ",,";
     484             :             }
     485             : 
     486             :             // Request location info for this location (just a few drivers,
     487             :             // like the VRT driver actually supports this).
     488          32 :             CPLString osItem;
     489          16 :             osItem.Printf("Pixel_%d_%d", iPixelToQuery, iLineToQuery);
     490             : 
     491          16 :             if (const char *pszLI =
     492          16 :                     GDALGetMetadataItem(hBand, osItem, "LocationInfo"))
     493             :             {
     494           2 :                 CPLXMLTreeCloser oTree(CPLParseXMLString(pszLI));
     495             : 
     496           2 :                 if (oTree && oTree->psChild != nullptr &&
     497           3 :                     oTree->eType == CXT_Element &&
     498           1 :                     EQUAL(oTree->pszValue, "LocationInfo"))
     499             :                 {
     500           1 :                     CPLJSONArray oFiles;
     501             : 
     502           1 :                     for (const CPLXMLNode *psNode = oTree->psChild;
     503           2 :                          psNode != nullptr; psNode = psNode->psNext)
     504             :                     {
     505           1 :                         if (psNode->eType == CXT_Element &&
     506           1 :                             EQUAL(psNode->pszValue, "File") &&
     507           1 :                             psNode->psChild != nullptr)
     508             :                         {
     509           2 :                             char *pszUnescaped = CPLUnescapeString(
     510           1 :                                 psNode->psChild->pszValue, nullptr, CPLES_XML);
     511           1 :                             oFiles.Add(pszUnescaped);
     512           1 :                             CPLFree(pszUnescaped);
     513             :                         }
     514             :                     }
     515             : 
     516           1 :                     oBand.Add("files", oFiles);
     517             :                 }
     518             :                 else
     519             :                 {
     520           0 :                     oBand.Add("location_info", pszLI);
     521             :                 }
     522             :             }
     523             : 
     524          16 :             oBands.Add(oBand);
     525             :         }
     526             : 
     527          16 :         if (m_format == "csv")
     528             :         {
     529           6 :             PrintLine(line);
     530             :         }
     531             :         else
     532             :         {
     533          10 :             oProperties.Add("bands", oBands);
     534             : 
     535          10 :             if (canOutputGeoJSONGeom)
     536             :             {
     537           9 :                 x = dfPixel;
     538           9 :                 y = dfLine;
     539             : 
     540           9 :                 GDALApplyGeoTransform(adfGeoTransform, x, y, &x, &y);
     541             : 
     542           9 :                 if (poCTToWGS84)
     543           1 :                     poCTToWGS84->Transform(1, &x, &y);
     544             : 
     545          18 :                 CPLJSONObject oGeometry;
     546           9 :                 oFeature.Add("geometry", oGeometry);
     547           9 :                 oGeometry.Add("type", "Point");
     548           9 :                 CPLJSONArray oCoordinates;
     549           9 :                 oCoordinates.Add(x);
     550           9 :                 oCoordinates.Add(y);
     551           9 :                 oGeometry.Add("coordinates", oCoordinates);
     552             :             }
     553             :             else
     554             :             {
     555           1 :                 oFeature.AddNull("geometry");
     556             :             }
     557             : 
     558          10 :             if (isInteractive)
     559             :             {
     560           0 :                 CPLJSONDocument oDoc;
     561           0 :                 oDoc.SetRoot(oFeature);
     562           0 :                 printf("%s\n", oDoc.SaveAsString().c_str());
     563             :             }
     564             :             else
     565             :             {
     566          10 :                 oFeatures.Add(oFeature);
     567             :             }
     568             :         }
     569             : 
     570          16 :     } while (m_pos.empty() || iVal + 1 < m_pos.size());
     571             : 
     572          16 :     if (m_format != "csv" && !isInteractive)
     573             :     {
     574          10 :         CPLJSONDocument oDoc;
     575          10 :         oDoc.SetRoot(oCollection);
     576          10 :         m_output = oDoc.SaveAsString();
     577             :     }
     578             : 
     579          16 :     return true;
     580             : }
     581             : 
     582             : //! @endcond

Generated by: LCOV version 1.14