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

Generated by: LCOV version 1.14