LCOV - code coverage report
Current view: top level - apps - gdal_contour_lib.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 256 307 83.4 %
Date: 2025-05-15 18:21:54 Functions: 11 12 91.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Contour Generator
       4             :  * Purpose:  Contour Generator mainline.
       5             :  * Author:   Frank Warmerdam <warmerdam@pobox.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2003, Applied Coherent Technology (www.actgate.com).
       9             :  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  * Copyright (c) 2018, Oslandia <infos at oslandia dot com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include "cpl_conv.h"
      16             : #include "cpl_string.h"
      17             : #include "gdal_version.h"
      18             : #include "gdal.h"
      19             : #include "gdal_alg.h"
      20             : #include "gdalargumentparser.h"
      21             : #include "ogr_api.h"
      22             : #include "ogr_srs_api.h"
      23             : #include "commonutils.h"
      24             : #include "gdal_utils_priv.h"
      25             : 
      26             : /************************************************************************/
      27             : /*                     GDALContourOptions                               */
      28             : /************************************************************************/
      29             : 
      30             : /** Options for use with GDALContour(). GDALContourOptions must be allocated
      31             :  *  with GDALContourOptionsNew() and deallocated with GDALContourOptionsFree().
      32             :  */
      33             : struct GDALContourOptions
      34             : {
      35             :     int nBand = 1;
      36             :     double dfInterval = 0.0;
      37             :     double dfNoData = 0.0;
      38             :     double dfOffset = 0.0;
      39             :     double dfExpBase = 0.0;
      40             :     bool b3D = false;
      41             :     bool bPolygonize = false;
      42             :     bool bNoDataSet = false;
      43             :     bool bIgnoreNoData = false;
      44             :     std::string osNewLayerName = "contour";
      45             :     std::string osFormat{};
      46             :     std::string osElevAttrib{};
      47             :     std::string osElevAttribMin{};
      48             :     std::string osElevAttribMax{};
      49             :     std::vector<std::string> aosFixedLevels{};
      50             :     CPLStringList aosOpenOptions{};
      51             :     CPLStringList aosCreationOptions{};
      52             :     CPLStringList aosLayerCreationOptions{};
      53             :     bool bQuiet = false;
      54             :     std::string osDestDataSource{};
      55             :     std::string osSrcDataSource{};
      56             :     GIntBig nGroupTransactions = 100 * 1000;
      57             :     GDALProgressFunc pfnProgress = GDALDummyProgress;
      58             :     void *pProgressData = nullptr;
      59             : };
      60             : 
      61             : /************************************************************************/
      62             : /*                 GDALContourOptionsSetDestDataSource                  */
      63             : /************************************************************************/
      64           6 : void GDALContourOptionsSetDestDataSource(GDALContourOptions *psOptions,
      65             :                                          const char *pszDestDatasource)
      66             : {
      67           6 :     psOptions->osDestDataSource = pszDestDatasource;
      68           6 : }
      69             : 
      70             : /************************************************************************/
      71             : /*                  GDALContourOptionsSetProgress()                   */
      72             : /************************************************************************/
      73             : 
      74             : /**
      75             :  * Set a progress function.
      76             :  *
      77             :  * @param psOptions the options struct for GDALContour().
      78             :  * @param pfnProgress the progress callback.
      79             :  * @param pProgressData the user data for the progress callback.
      80             :  *
      81             :  */
      82             : 
      83           0 : void GDALContourOptionsSetProgress(GDALContourOptions *psOptions,
      84             :                                    GDALProgressFunc pfnProgress,
      85             :                                    void *pProgressData)
      86             : {
      87           0 :     psOptions->pfnProgress = pfnProgress ? pfnProgress : GDALDummyProgress;
      88           0 :     psOptions->pProgressData = pProgressData;
      89           0 : }
      90             : 
      91             : ///@cond Doxygen_Suppress
      92             : 
      93             : /************************************************************************/
      94             : /*                  CreateElevAttrib()                                  */
      95             : /************************************************************************/
      96             : 
      97          48 : static bool CreateElevAttrib(const char *pszElevAttrib, OGRLayerH hLayer)
      98             : {
      99          48 :     OGRFieldDefnH hFld = OGR_Fld_Create(pszElevAttrib, OFTReal);
     100          48 :     OGRErr eErr = OGR_L_CreateField(hLayer, hFld, FALSE);
     101          48 :     OGR_Fld_Destroy(hFld);
     102          48 :     return eErr == OGRERR_NONE;
     103             : }
     104             : 
     105             : /************************************************************************/
     106             : /*                  GDALContourProcessOptions()                      */
     107             : /************************************************************************/
     108             : 
     109          43 : CPLErr GDALContourProcessOptions(GDALContourOptions *psOptions,
     110             :                                  char ***ppapszStringOptions,
     111             :                                  GDALDatasetH *hSrcDS, GDALRasterBandH *hBand,
     112             :                                  GDALDatasetH *hDstDS, OGRLayerH *hLayer)
     113             : {
     114             : 
     115             :     /* -------------------------------------------------------------------- */
     116             :     /*      Open source raster file.                                        */
     117             :     /* -------------------------------------------------------------------- */
     118          43 :     if (!*hSrcDS)
     119             :     {
     120          19 :         *hSrcDS = GDALOpen(psOptions->osSrcDataSource.c_str(), GA_ReadOnly);
     121             :     }
     122             : 
     123          43 :     if (*hSrcDS == nullptr)
     124             :     {
     125           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     126             :                  "Unable to open source raster file '%s'.",
     127           0 :                  psOptions->osSrcDataSource.c_str());
     128           0 :         return CE_Failure;
     129             :     }
     130             : 
     131          43 :     if (!*hBand)
     132             :     {
     133          43 :         *hBand = GDALGetRasterBand(*hSrcDS, psOptions->nBand);
     134             :     }
     135             : 
     136          43 :     if (*hBand == nullptr)
     137             :     {
     138           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     139             :                  "Band %d does not exist on dataset.", psOptions->nBand);
     140           0 :         return CE_Failure;
     141             :     }
     142             : 
     143          43 :     if (!psOptions->bNoDataSet && !psOptions->bIgnoreNoData)
     144             :     {
     145             :         int bNoDataSet;
     146          42 :         psOptions->dfNoData = GDALGetRasterNoDataValue(*hBand, &bNoDataSet);
     147          42 :         psOptions->bNoDataSet = bNoDataSet;
     148             :     }
     149             : 
     150             :     /* -------------------------------------------------------------------- */
     151             :     /*      Try to get a coordinate system from the raster.                 */
     152             :     /* -------------------------------------------------------------------- */
     153          43 :     OGRSpatialReferenceH hSRS = GDALGetSpatialRef(*hSrcDS);
     154             : 
     155             :     // Dedup lambda to create the layer
     156          37 :     auto CreateLayer = [&]() -> OGRLayerH
     157             :     {
     158          74 :         return GDALDatasetCreateLayer(
     159          37 :             *hDstDS, psOptions->osNewLayerName.c_str(), hSRS,
     160          37 :             psOptions->bPolygonize
     161          12 :                 ? (psOptions->b3D ? wkbMultiPolygon25D : wkbMultiPolygon)
     162          25 :                 : (psOptions->b3D ? wkbLineString25D : wkbLineString),
     163          37 :             psOptions->aosLayerCreationOptions);
     164          43 :     };
     165             : 
     166             :     /* -------------------------------------------------------------------- */
     167             :     /*      Create the output file.                                         */
     168             :     /* -------------------------------------------------------------------- */
     169          43 :     if (!*hDstDS && !*hLayer)
     170             :     {
     171          37 :         CPLString osFormat;
     172          37 :         if (psOptions->osFormat.empty())
     173             :         {
     174             :             const auto aoDrivers = GetOutputDriversFor(
     175          37 :                 psOptions->osDestDataSource.c_str(), GDAL_OF_VECTOR);
     176          37 :             if (aoDrivers.empty())
     177             :             {
     178           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     179             :                          "Cannot guess driver for %s",
     180           0 :                          psOptions->osDestDataSource.c_str());
     181           0 :                 return CE_Failure;
     182             :             }
     183             :             else
     184             :             {
     185          37 :                 if (aoDrivers.size() > 1)
     186             :                 {
     187           0 :                     CPLError(
     188             :                         CE_Warning, CPLE_AppDefined,
     189             :                         "Several drivers matching %s extension. Using %s",
     190           0 :                         CPLGetExtensionSafe(psOptions->osDestDataSource.c_str())
     191             :                             .c_str(),
     192           0 :                         aoDrivers[0].c_str());
     193             :                 }
     194          37 :                 osFormat = aoDrivers[0];
     195             :             }
     196             :         }
     197             :         else
     198             :         {
     199           0 :             osFormat = psOptions->osFormat;
     200             :         }
     201             : 
     202          37 :         OGRSFDriverH hDriver = OGRGetDriverByName(osFormat.c_str());
     203             : 
     204          37 :         if (hDriver == nullptr)
     205             :         {
     206           0 :             fprintf(stderr, "Unable to find format driver named %s.\n",
     207             :                     osFormat.c_str());
     208           0 :             return CE_Failure;
     209             :         }
     210             : 
     211          37 :         if (!*hDstDS)
     212             :         {
     213          74 :             *hDstDS =
     214          37 :                 GDALCreate(hDriver, psOptions->osDestDataSource.c_str(), 0, 0,
     215          37 :                            0, GDT_Unknown, psOptions->aosCreationOptions);
     216             :         }
     217             : 
     218          37 :         if (*hDstDS == nullptr)
     219             :         {
     220           0 :             return CE_Failure;
     221             :         }
     222             : 
     223             :         // Create the layer
     224          37 :         *hLayer = CreateLayer();
     225             :     }
     226             : 
     227          43 :     if (!*hLayer)
     228             :     {
     229           6 :         auto hDriver = GDALGetDatasetDriver(*hDstDS);
     230             :         // Try to load the layer if it already exists
     231           6 :         if (GDALGetMetadataItem(hDriver, GDAL_DCAP_MULTIPLE_VECTOR_LAYERS,
     232           6 :                                 nullptr))
     233             :         {
     234           6 :             *hLayer = GDALDatasetGetLayerByName(
     235           6 :                 *hDstDS, psOptions->osNewLayerName.c_str());
     236          12 :             if (!*hLayer &&
     237           6 :                 GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE_LAYER, nullptr) &&
     238           0 :                 !GDALDatasetTestCapability(*hDstDS, ODsCCreateLayer))
     239             :             {
     240           0 :                 *hLayer = CreateLayer();
     241             :             }
     242             :         }
     243             :         else
     244             :         {
     245           0 :             *hLayer = GDALDatasetGetLayer(*hDstDS, 0);
     246             :         }
     247             :     }
     248             : 
     249          43 :     if (*hLayer == nullptr)
     250             :     {
     251           0 :         return CE_Failure;
     252             :     }
     253             : 
     254          43 :     if (!OGR_L_TestCapability(*hLayer, OLCTransactions))
     255             :     {
     256          39 :         psOptions->nGroupTransactions = 0;
     257             :     }
     258             : 
     259          43 :     OGRFieldDefnH hFld = OGR_Fld_Create("ID", OFTInteger);
     260          43 :     OGR_Fld_SetWidth(hFld, 8);
     261          43 :     OGR_L_CreateField(*hLayer, hFld, FALSE);
     262          43 :     OGR_Fld_Destroy(hFld);
     263             : 
     264          43 :     if (psOptions->bPolygonize)
     265             :     {
     266          13 :         if (!psOptions->osElevAttrib.empty())
     267             :         {
     268           0 :             psOptions->osElevAttrib.clear();
     269           0 :             CPLError(CE_Warning, CPLE_NotSupported,
     270             :                      "-a is ignored in polygonal contouring mode. "
     271             :                      "Use -amin and/or -amax instead");
     272             :         }
     273             :     }
     274             :     else
     275             :     {
     276          58 :         if (!psOptions->osElevAttribMin.empty() ||
     277          28 :             !psOptions->osElevAttribMax.empty())
     278             :         {
     279           2 :             psOptions->osElevAttribMin.clear();
     280           2 :             psOptions->osElevAttribMax.clear();
     281           2 :             CPLError(CE_Warning, CPLE_NotSupported,
     282             :                      "-amin and/or -amax are ignored in line contouring mode. "
     283             :                      "Use -a instead");
     284             :         }
     285             :     }
     286             : 
     287          43 :     OGRFeatureDefnH hFeatureDefn = OGR_L_GetLayerDefn(*hLayer);
     288             : 
     289          43 :     if (!psOptions->osElevAttrib.empty())
     290             :     {
     291             :         // Skip if field already exists
     292          24 :         if (OGR_FD_GetFieldIndex(hFeatureDefn,
     293          48 :                                  psOptions->osElevAttrib.c_str()) == -1)
     294             :         {
     295          24 :             if (!CreateElevAttrib(psOptions->osElevAttrib.c_str(), *hLayer))
     296             :             {
     297           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     298             :                          "Failed to create elevation field '%s'",
     299           0 :                          psOptions->osElevAttrib.c_str());
     300           0 :                 return CE_Failure;
     301             :             }
     302             :         }
     303             :     }
     304             : 
     305          43 :     if (!psOptions->osElevAttribMin.empty())
     306             :     {
     307             :         // Skip if field already exists
     308          13 :         if (OGR_FD_GetFieldIndex(hFeatureDefn,
     309          26 :                                  psOptions->osElevAttribMin.c_str()) == -1)
     310             :         {
     311          12 :             if (!CreateElevAttrib(psOptions->osElevAttribMin.c_str(), *hLayer))
     312             :             {
     313           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     314             :                          "Failed to create elevation min field '%s'",
     315           0 :                          psOptions->osElevAttribMin.c_str());
     316           0 :                 return CE_Failure;
     317             :             }
     318             :         }
     319             :     }
     320             : 
     321          43 :     if (!psOptions->osElevAttribMax.empty())
     322             :     {
     323             :         // Skip if field already exists
     324          13 :         if (OGR_FD_GetFieldIndex(hFeatureDefn,
     325          26 :                                  psOptions->osElevAttribMax.c_str()) == -1)
     326             :         {
     327          12 :             if (!CreateElevAttrib(psOptions->osElevAttribMax.c_str(), *hLayer))
     328             :             {
     329           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     330             :                          "Failed to create elevation max field '%s'",
     331           0 :                          psOptions->osElevAttribMax.c_str());
     332           0 :                 return CE_Failure;
     333             :             }
     334             :         }
     335             :     }
     336             : 
     337             :     /* -------------------------------------------------------------------- */
     338             :     /*      Invoke.                                                         */
     339             :     /* -------------------------------------------------------------------- */
     340          43 :     int iIDField = OGR_FD_GetFieldIndex(hFeatureDefn, "ID");
     341          43 :     int iElevField = (psOptions->osElevAttrib.empty())
     342          43 :                          ? -1
     343          24 :                          : OGR_FD_GetFieldIndex(
     344          24 :                                hFeatureDefn, psOptions->osElevAttrib.c_str());
     345             : 
     346             :     int iElevFieldMin =
     347          43 :         (psOptions->osElevAttribMin.empty())
     348          43 :             ? -1
     349          13 :             : OGR_FD_GetFieldIndex(hFeatureDefn,
     350          13 :                                    psOptions->osElevAttribMin.c_str());
     351             : 
     352             :     int iElevFieldMax =
     353          43 :         (psOptions->osElevAttribMax.empty())
     354          43 :             ? -1
     355          13 :             : OGR_FD_GetFieldIndex(hFeatureDefn,
     356          13 :                                    psOptions->osElevAttribMax.c_str());
     357             : 
     358          43 :     if (!psOptions->aosFixedLevels.empty())
     359             :     {
     360          17 :         std::string values = "FIXED_LEVELS=";
     361          62 :         for (size_t i = 0; i < psOptions->aosFixedLevels.size(); i++)
     362             :         {
     363          45 :             if (i == psOptions->aosFixedLevels.size() - 1)
     364             :             {
     365          17 :                 values = values + psOptions->aosFixedLevels[i];
     366             :             }
     367             :             else
     368             :             {
     369          28 :                 values = values + psOptions->aosFixedLevels[i] + ",";
     370             :             }
     371             :         }
     372          17 :         *ppapszStringOptions =
     373          17 :             CSLAddString(*ppapszStringOptions, values.c_str());
     374             :     }
     375             : 
     376          43 :     if (psOptions->dfExpBase != 0.0)
     377             :     {
     378           6 :         *ppapszStringOptions = CSLAppendPrintf(
     379             :             *ppapszStringOptions, "LEVEL_EXP_BASE=%f", psOptions->dfExpBase);
     380             :     }
     381          37 :     else if (psOptions->dfInterval != 0.0)
     382             :     {
     383          29 :         *ppapszStringOptions = CSLAppendPrintf(
     384             :             *ppapszStringOptions, "LEVEL_INTERVAL=%f", psOptions->dfInterval);
     385             :     }
     386             : 
     387          43 :     if (psOptions->dfOffset != 0.0)
     388             :     {
     389           4 :         *ppapszStringOptions = CSLAppendPrintf(
     390             :             *ppapszStringOptions, "LEVEL_BASE=%f", psOptions->dfOffset);
     391             :     }
     392             : 
     393          43 :     if (psOptions->bNoDataSet)
     394             :     {
     395          11 :         *ppapszStringOptions = CSLAppendPrintf(
     396             :             *ppapszStringOptions, "NODATA=%.19g", psOptions->dfNoData);
     397             :     }
     398          43 :     if (iIDField != -1)
     399             :     {
     400          43 :         *ppapszStringOptions =
     401          43 :             CSLAppendPrintf(*ppapszStringOptions, "ID_FIELD=%d", iIDField);
     402             :     }
     403          43 :     if (iElevField != -1)
     404             :     {
     405          24 :         *ppapszStringOptions =
     406          24 :             CSLAppendPrintf(*ppapszStringOptions, "ELEV_FIELD=%d", iElevField);
     407             :     }
     408          43 :     if (iElevFieldMin != -1)
     409             :     {
     410          13 :         *ppapszStringOptions = CSLAppendPrintf(
     411             :             *ppapszStringOptions, "ELEV_FIELD_MIN=%d", iElevFieldMin);
     412             :     }
     413          43 :     if (iElevFieldMax != -1)
     414             :     {
     415          13 :         *ppapszStringOptions = CSLAppendPrintf(
     416             :             *ppapszStringOptions, "ELEV_FIELD_MAX=%d", iElevFieldMax);
     417             :     }
     418          43 :     if (psOptions->bPolygonize)
     419             :     {
     420          13 :         *ppapszStringOptions =
     421          13 :             CSLAppendPrintf(*ppapszStringOptions, "POLYGONIZE=YES");
     422             :     }
     423          43 :     if (psOptions->nGroupTransactions)
     424             :     {
     425           3 :         *ppapszStringOptions = CSLAppendPrintf(*ppapszStringOptions,
     426             :                                                "COMMIT_INTERVAL=" CPL_FRMT_GIB,
     427             :                                                psOptions->nGroupTransactions);
     428             :     }
     429             : 
     430          43 :     return CE_None;
     431             : }
     432             : 
     433             : ///@endcond
     434             : 
     435             : /************************************************************************/
     436             : /*                     GDALContourAppOptionsGetParser()                 */
     437             : /************************************************************************/
     438             : 
     439             : static std::unique_ptr<GDALArgumentParser>
     440          45 : GDALContourAppOptionsGetParser(GDALContourOptions *psOptions,
     441             :                                GDALContourOptionsForBinary *psOptionsForBinary)
     442             : {
     443             : 
     444             :     auto argParser = std::make_unique<GDALArgumentParser>(
     445          45 :         "gdal_contour", /* bForBinary=*/psOptionsForBinary != nullptr);
     446             : 
     447          45 :     argParser->add_description(_("Creates contour lines from a raster file."));
     448          45 :     argParser->add_epilog(_(
     449             :         "For more details, consult the full documentation for the gdal_contour "
     450          45 :         "utility: http://gdal.org/gdal_contour.html"));
     451             : 
     452          45 :     argParser->add_extra_usage_hint(
     453             :         _("One of -i, -fl or -e must be specified."));
     454             : 
     455          45 :     argParser->add_argument("-b")
     456          90 :         .metavar("<name>")
     457          45 :         .default_value(1)
     458          45 :         .nargs(1)
     459          45 :         .scan<'i', int>()
     460          45 :         .store_into(psOptions->nBand)
     461          45 :         .help(_("Select an input band band containing the DEM data."));
     462             : 
     463          45 :     argParser->add_argument("-a")
     464          90 :         .metavar("<name>")
     465          45 :         .store_into(psOptions->osElevAttrib)
     466             :         .help(_("Provides a name for the attribute in which to put the "
     467          45 :                 "elevation."));
     468             : 
     469          45 :     argParser->add_argument("-amin")
     470          90 :         .metavar("<name>")
     471          45 :         .store_into(psOptions->osElevAttribMin)
     472             :         .help(_("Provides a name for the attribute in which to put the minimum "
     473          45 :                 "elevation."));
     474             : 
     475          45 :     argParser->add_argument("-amax")
     476          90 :         .metavar("<name>")
     477          45 :         .store_into(psOptions->osElevAttribMax)
     478             :         .help(_("Provides a name for the attribute in which to put the maximum "
     479          45 :                 "elevation."));
     480             : 
     481          45 :     argParser->add_argument("-3d")
     482          45 :         .flag()
     483          45 :         .store_into(psOptions->b3D)
     484          45 :         .help(_("Force production of 3D vectors instead of 2D."));
     485             : 
     486          45 :     argParser->add_argument("-inodata")
     487          45 :         .flag()
     488          45 :         .store_into(psOptions->bIgnoreNoData)
     489             :         .help(_("Ignore any nodata value implied in the dataset - treat all "
     490          45 :                 "values as valid."));
     491             : 
     492          45 :     argParser->add_argument("-snodata")
     493          90 :         .metavar("<value>")
     494          45 :         .scan<'g', double>()
     495             :         .action(
     496           2 :             [psOptions](const auto &d)
     497             :             {
     498           1 :                 psOptions->bNoDataSet = true;
     499           1 :                 psOptions->dfNoData = CPLAtofM(d.c_str());
     500          45 :             })
     501          45 :         .help(_("Input pixel value to treat as \"nodata\"."));
     502             : 
     503          45 :     auto &group = argParser->add_mutually_exclusive_group();
     504             : 
     505          45 :     group.add_argument("-i")
     506          90 :         .metavar("<interval>")
     507          45 :         .scan<'g', double>()
     508          45 :         .store_into(psOptions->dfInterval)
     509          45 :         .help(_("Elevation interval between contours."));
     510             : 
     511          45 :     group.add_argument("-e")
     512          90 :         .metavar("<base>")
     513          45 :         .scan<'g', double>()
     514          45 :         .store_into(psOptions->dfExpBase)
     515             :         .help(_("Generate levels on an exponential scale: base ^ k, for k an "
     516          45 :                 "integer."));
     517             : 
     518             :     // Dealt manually as argparse::nargs_pattern::at_least_one is problematic
     519          90 :     argParser->add_argument("-fl").metavar("<level>").help(
     520          45 :         _("Name one or more \"fixed levels\" to extract."));
     521             : 
     522          45 :     argParser->add_argument("-off")
     523          90 :         .metavar("<offset>")
     524          45 :         .scan<'g', double>()
     525          45 :         .store_into(psOptions->dfOffset)
     526          45 :         .help(_("Offset from zero relative to which to interpret intervals."));
     527             : 
     528          45 :     argParser->add_argument("-nln")
     529          90 :         .metavar("<name>")
     530          45 :         .store_into(psOptions->osNewLayerName)
     531             :         .help(_("Provide a name for the output vector layer. Defaults to "
     532          45 :                 "\"contour\"."));
     533             : 
     534          45 :     argParser->add_argument("-p")
     535          45 :         .flag()
     536          45 :         .store_into(psOptions->bPolygonize)
     537          45 :         .help(_("Generate contour polygons instead of lines."));
     538             : 
     539          45 :     argParser->add_argument("-gt")
     540          90 :         .metavar("<n>|unlimited")
     541             :         .action(
     542          30 :             [psOptions](const std::string &s)
     543             :             {
     544          15 :                 if (EQUAL(s.c_str(), "unlimited"))
     545           1 :                     psOptions->nGroupTransactions = -1;
     546             :                 else
     547          14 :                     psOptions->nGroupTransactions = atoi(s.c_str());
     548          45 :             })
     549          45 :         .help(_("Group <n> features per transaction."));
     550             : 
     551             :     // Written that way so that in library mode, users can still use the -q
     552             :     // switch, even if it has no effect
     553             :     argParser->add_quiet_argument(
     554          45 :         psOptionsForBinary ? &(psOptionsForBinary->bQuiet) : nullptr);
     555             : 
     556          45 :     if (psOptionsForBinary)
     557             :     {
     558             :         argParser->add_open_options_argument(
     559          32 :             psOptionsForBinary->aosOpenOptions);
     560             : 
     561          32 :         argParser->add_argument("src_filename")
     562          32 :             .store_into(psOptions->osSrcDataSource)
     563          32 :             .help("The source raster file.");
     564             : 
     565             :         argParser->add_dataset_creation_options_argument(
     566          32 :             psOptions->aosOpenOptions);
     567             : 
     568          32 :         argParser->add_argument("dst_filename")
     569          32 :             .store_into(psOptions->osDestDataSource)
     570          32 :             .help("The destination vector file.");
     571             : 
     572          32 :         argParser->add_output_format_argument(psOptions->osFormat);
     573             : 
     574          32 :         argParser->add_creation_options_argument(psOptions->aosCreationOptions);
     575             : 
     576             :         argParser->add_layer_creation_options_argument(
     577          32 :             psOptions->aosLayerCreationOptions);
     578             :     }
     579             : 
     580          45 :     return argParser;
     581             : }
     582             : 
     583             : /************************************************************************/
     584             : /*                         GDALContourGetParserUsage()                     */
     585             : /************************************************************************/
     586             : 
     587           1 : std::string GDALContourGetParserUsage()
     588             : {
     589             :     try
     590             :     {
     591           2 :         GDALContourOptions sOptions;
     592           2 :         auto argParser = GDALContourAppOptionsGetParser(&sOptions, nullptr);
     593           1 :         return argParser->usage();
     594             :     }
     595           0 :     catch (const std::exception &err)
     596             :     {
     597           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
     598           0 :                  err.what());
     599           0 :         return std::string();
     600             :     }
     601             : }
     602             : 
     603             : /************************************************************************/
     604             : /*                     GDALContourOptionsNew()                          */
     605             : /************************************************************************/
     606             : 
     607             : /**
     608             :  * Create a new GDALContourOptions object.
     609             :  *
     610             :  * @param papszArgv the command line arguments.
     611             :  * @param psOptionsForBinary the options for binary.
     612             :  *
     613             :  * @return the new GDALContourOptions object.
     614             :  */
     615             : GDALContourOptions *
     616          44 : GDALContourOptionsNew(char **papszArgv,
     617             :                       GDALContourOptionsForBinary *psOptionsForBinary)
     618             : {
     619             : 
     620          88 :     auto psOptions = std::make_unique<GDALContourOptions>();
     621             : 
     622             :     /*-------------------------------------------------------------------- */
     623             :     /*      Parse arguments.                                               */
     624             :     /*-------------------------------------------------------------------- */
     625             : 
     626          88 :     CPLStringList aosArgv;
     627             : 
     628             :     /* -------------------------------------------------------------------- */
     629             :     /*      Pre-processing for custom syntax that ArgumentParser does not   */
     630             :     /*      support.                                                        */
     631             :     /* -------------------------------------------------------------------- */
     632          44 :     const int argc = CSLCount(papszArgv);
     633             : 
     634             :     /* -------------------------------------------------------------------- */
     635             :     /*      Pre-processing for custom syntax that ArgumentParser does not   */
     636             :     /*      support.                                                        */
     637             :     /* -------------------------------------------------------------------- */
     638         444 :     for (int i = 0; i < argc && papszArgv != nullptr && papszArgv[i] != nullptr;
     639             :          i++)
     640             :     {
     641             :         // argparser is confused by arguments that have at_least_one
     642             :         // cardinality, if they immediately precede positional arguments.
     643         400 :         if (EQUAL(papszArgv[i], "-fl") && papszArgv[i + 1])
     644             :         {
     645          23 :             if (strchr(papszArgv[i + 1], ' '))
     646             :             {
     647             :                 const CPLStringList aosTokens(
     648           0 :                     CSLTokenizeString(papszArgv[i + 1]));
     649           0 :                 for (const char *pszToken : aosTokens)
     650             :                 {
     651             :                     // Handle min/max special values
     652           0 :                     if (EQUAL(pszToken, "MIN"))
     653             :                     {
     654           0 :                         psOptions->aosFixedLevels.push_back("MIN");
     655             :                     }
     656           0 :                     else if (EQUAL(pszToken, "MAX"))
     657             :                     {
     658           0 :                         psOptions->aosFixedLevels.push_back("MAX");
     659             :                     }
     660             :                     else
     661             :                     {
     662           0 :                         psOptions->aosFixedLevels.push_back(
     663           0 :                             std::to_string(CPLAtof(pszToken)));
     664             :                     }
     665             :                 }
     666           0 :                 i += 1;
     667             :             }
     668             :             else
     669             :             {
     670          68 :                 auto isNumericOrMinMax = [](const char *pszArg) -> bool
     671             :                 {
     672          68 :                     if (EQUAL(pszArg, "MIN") || EQUAL(pszArg, "MAX"))
     673           4 :                         return true;
     674          64 :                     char *pszEnd = nullptr;
     675          64 :                     CPLStrtod(pszArg, &pszEnd);
     676          64 :                     return pszEnd != nullptr && pszEnd[0] == '\0';
     677             :                 };
     678             : 
     679          68 :                 while (i < argc - 1 && isNumericOrMinMax(papszArgv[i + 1]))
     680             :                 {
     681          45 :                     if (EQUAL(papszArgv[i + 1], "MIN"))
     682             :                     {
     683           2 :                         psOptions->aosFixedLevels.push_back("MIN");
     684             :                     }
     685          43 :                     else if (EQUAL(papszArgv[i + 1], "MAX"))
     686             :                     {
     687           2 :                         psOptions->aosFixedLevels.push_back("MAX");
     688             :                     }
     689             :                     else
     690             :                     {
     691          82 :                         psOptions->aosFixedLevels.push_back(
     692          82 :                             std::to_string(CPLAtof(papszArgv[i + 1])));
     693             :                     }
     694          45 :                     i += 1;
     695             :                 }
     696          23 :             }
     697             :         }
     698             :         else
     699             :         {
     700         377 :             aosArgv.AddString(papszArgv[i]);
     701             :         }
     702             :     }
     703             : 
     704             :     try
     705             :     {
     706             : 
     707             :         auto argParser =
     708          88 :             GDALContourAppOptionsGetParser(psOptions.get(), psOptionsForBinary);
     709          44 :         argParser->parse_args_without_binary_name(aosArgv.List());
     710             : 
     711          46 :         if (psOptions->dfInterval == 0.0 && psOptions->aosFixedLevels.empty() &&
     712           2 :             psOptions->dfExpBase == 0.0)
     713             :         {
     714           1 :             fprintf(stderr, "%s\n", argParser->usage().c_str());
     715           1 :             return nullptr;
     716             :         }
     717             : 
     718          43 :         if (psOptions->osSrcDataSource.find("/vsistdout/") !=
     719          86 :                 std::string::npos ||
     720          43 :             psOptions->osDestDataSource.find("/vsistdout/") !=
     721             :                 std::string::npos)
     722             :         {
     723           0 :             psOptions->bQuiet = true;
     724             :         }
     725             : 
     726          43 :         if (psOptionsForBinary)
     727             :         {
     728          31 :             psOptionsForBinary->bQuiet = psOptions->bQuiet;
     729          31 :             psOptionsForBinary->osDestDataSource = psOptions->osDestDataSource;
     730          31 :             psOptionsForBinary->osSrcDataSource = psOptions->osSrcDataSource;
     731          31 :             psOptionsForBinary->aosOpenOptions = psOptions->aosOpenOptions;
     732             :         }
     733             : 
     734          43 :         return psOptions.release();
     735             :     }
     736           0 :     catch (const std::exception &e)
     737             :     {
     738           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
     739           0 :         return nullptr;
     740             :     }
     741             : }
     742             : 
     743             : /************************************************************************/
     744             : /*                     GDALContourOptionsFree()                         */
     745             : /************************************************************************/
     746             : 
     747             : /**
     748             :  * Free a GDALContourOptions object.
     749             :  *
     750             :  * @param psOptions the GDALContourOptions object to free.
     751             :  */
     752          43 : void GDALContourOptionsFree(GDALContourOptions *psOptions)
     753             : {
     754          43 :     delete psOptions;
     755          43 : }

Generated by: LCOV version 1.14