LCOV - code coverage report
Current view: top level - apps - gdal_contour.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 182 229 79.5 %
Date: 2024-05-02 22:57:13 Functions: 4 6 66.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             :  * Permission is hereby granted, free of charge, to any person obtaining a
      13             :  * copy of this software and associated documentation files (the "Software"),
      14             :  * to deal in the Software without restriction, including without limitation
      15             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      16             :  * and/or sell copies of the Software, and to permit persons to whom the
      17             :  * Software is furnished to do so, subject to the following conditions:
      18             :  *
      19             :  * The above copyright notice and this permission notice shall be included
      20             :  * in all copies or substantial portions of the Software.
      21             :  *
      22             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      23             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      24             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      25             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      26             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      27             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      28             :  * DEALINGS IN THE SOFTWARE.
      29             :  ****************************************************************************/
      30             : 
      31             : #include "cpl_conv.h"
      32             : #include "cpl_string.h"
      33             : #include "gdal_version.h"
      34             : #include "gdal.h"
      35             : #include "gdal_alg.h"
      36             : #include "gdalargumentparser.h"
      37             : #include "ogr_api.h"
      38             : #include "ogr_srs_api.h"
      39             : #include "commonutils.h"
      40             : 
      41             : /************************************************************************/
      42             : /*                     GDALContourOptions                               */
      43             : /************************************************************************/
      44             : 
      45             : struct GDALContourOptions
      46             : {
      47             :     int nBand = 1;
      48             :     double dfInterval = 0.0;
      49             :     double dfNoData = 0.0;
      50             :     double dfOffset = 0.0;
      51             :     double dfExpBase = 0.0;
      52             :     bool b3D = false;
      53             :     bool bPolygonize = false;
      54             :     bool bNoDataSet = false;
      55             :     bool bIgnoreNoData = false;
      56             :     std::string osNewLayerName = "contour";
      57             :     std::string osFormat;
      58             :     std::string osElevAttrib;
      59             :     std::string osElevAttribMin;
      60             :     std::string osElevAttribMax;
      61             :     std::vector<double> adfFixedLevels;
      62             :     CPLStringList aosOpenOptions;
      63             :     CPLStringList aosCreationOptions;
      64             :     bool bQuiet = false;
      65             :     std::string aosDestFilename;
      66             :     std::string aosSrcFilename;
      67             : };
      68             : 
      69             : /************************************************************************/
      70             : /*                     GDALContourAppOptionsGetParser()                 */
      71             : /************************************************************************/
      72             : 
      73             : static std::unique_ptr<GDALArgumentParser>
      74           9 : GDALContourAppOptionsGetParser(GDALContourOptions *psOptions)
      75             : {
      76             :     auto argParser = std::make_unique<GDALArgumentParser>(
      77           9 :         "gdal_contour", /* bForBinary */ true);
      78             : 
      79           9 :     argParser->add_description(_("Creates contour lines from a raster file."));
      80           9 :     argParser->add_epilog(_(
      81             :         "For more details, consult the full documentation for the gdal_contour "
      82           9 :         "utility: http://gdal.org/gdal_contour.html"));
      83             : 
      84           9 :     argParser->add_argument("-b")
      85          18 :         .metavar("<name>")
      86           9 :         .default_value(1)
      87           9 :         .nargs(1)
      88           9 :         .scan<'i', int>()
      89           9 :         .store_into(psOptions->nBand)
      90           9 :         .help(_("Select an input band band containing the DEM data."));
      91             : 
      92           9 :     argParser->add_argument("-a")
      93          18 :         .metavar("<name>")
      94           9 :         .store_into(psOptions->osElevAttrib)
      95             :         .help(_("Provides a name for the attribute in which to put the "
      96           9 :                 "elevation."));
      97             : 
      98           9 :     argParser->add_argument("-amin")
      99          18 :         .metavar("<name>")
     100           9 :         .store_into(psOptions->osElevAttribMin)
     101             :         .help(_("Provides a name for the attribute in which to put the minimum "
     102           9 :                 "elevation."));
     103             : 
     104           9 :     argParser->add_argument("-amax")
     105          18 :         .metavar("<name>")
     106           9 :         .store_into(psOptions->osElevAttribMax)
     107             :         .help(_("Provides a name for the attribute in which to put the maximum "
     108           9 :                 "elevation."));
     109             : 
     110           9 :     argParser->add_argument("-3d")
     111           9 :         .flag()
     112           9 :         .store_into(psOptions->b3D)
     113           9 :         .help(_("Force production of 3D vectors instead of 2D."));
     114             : 
     115           9 :     argParser->add_argument("-inodata")
     116           9 :         .flag()
     117           9 :         .store_into(psOptions->bIgnoreNoData)
     118             :         .help(_("Ignore any nodata value implied in the dataset - treat all "
     119           9 :                 "values as valid."));
     120             : 
     121           9 :     argParser->add_argument("-snodata")
     122          18 :         .metavar("<value>")
     123           9 :         .scan<'g', double>()
     124             :         .action(
     125           0 :             [psOptions](const auto &d)
     126             :             {
     127           0 :                 psOptions->bNoDataSet = true;
     128           0 :                 psOptions->dfNoData = CPLAtofM(d.c_str());
     129           9 :             })
     130           9 :         .help(_("Input pixel value to treat as \"nodata\"."));
     131             : 
     132           9 :     argParser->add_output_format_argument(psOptions->osFormat);
     133             : 
     134           9 :     argParser->add_argument("-dsco")
     135          18 :         .metavar("<NAME>=<VALUE>")
     136           9 :         .append()
     137           0 :         .action([psOptions](const std::string &s)
     138           9 :                 { psOptions->aosOpenOptions.AddString(s.c_str()); })
     139           9 :         .help(_("Dataset creation option (format specific)."));
     140             : 
     141             :     argParser->add_layer_creation_options_argument(
     142           9 :         psOptions->aosCreationOptions);
     143             : 
     144           9 :     auto &group = argParser->add_mutually_exclusive_group();
     145             : 
     146           9 :     group.add_argument("-i")
     147          18 :         .metavar("<interval>")
     148           9 :         .scan<'g', double>()
     149           9 :         .store_into(psOptions->dfInterval)
     150           9 :         .help(_("Elevation interval between contours."));
     151             : 
     152           9 :     group.add_argument("-fl")
     153          18 :         .metavar("<level>")
     154           9 :         .nargs(argparse::nargs_pattern::at_least_one)
     155           9 :         .scan<'g', double>()
     156           3 :         .action([psOptions](const std::string &s)
     157          12 :                 { psOptions->adfFixedLevels.push_back(CPLAtof(s.c_str())); })
     158           9 :         .help(_("Name one or more \"fixed levels\" to extract."));
     159             : 
     160           9 :     group.add_argument("-e")
     161          18 :         .metavar("<base>")
     162           9 :         .scan<'g', double>()
     163           9 :         .store_into(psOptions->dfExpBase)
     164             :         .help(_("Generate levels on an exponential scale: base ^ k, for k an "
     165           9 :                 "integer."));
     166             : 
     167           9 :     argParser->add_argument("-off")
     168          18 :         .metavar("<offset>")
     169           9 :         .scan<'g', double>()
     170           9 :         .store_into(psOptions->dfOffset)
     171           9 :         .help(_("Offset from zero relative to which to interpret intervals."));
     172             : 
     173           9 :     argParser->add_argument("-nln")
     174          18 :         .metavar("<name>")
     175           9 :         .store_into(psOptions->osNewLayerName)
     176             :         .help(_("Provide a name for the output vector layer. Defaults to "
     177           9 :                 "\"contour\"."));
     178             : 
     179           9 :     argParser->add_argument("-p")
     180           9 :         .flag()
     181           9 :         .store_into(psOptions->bPolygonize)
     182           9 :         .help(_("Generate contour polygons instead of lines."));
     183             : 
     184           9 :     argParser->add_quiet_argument(&psOptions->bQuiet);
     185             : 
     186           9 :     argParser->add_argument("src_filename")
     187           9 :         .store_into(psOptions->aosSrcFilename)
     188           9 :         .help("The source raster file.");
     189             : 
     190           9 :     argParser->add_argument("dst_filename")
     191           9 :         .store_into(psOptions->aosDestFilename)
     192           9 :         .help("The destination vector file.");
     193             : 
     194           9 :     return argParser;
     195             : }
     196             : 
     197           5 : static void CreateElevAttrib(const char *pszElevAttrib, OGRLayerH hLayer)
     198             : {
     199           5 :     OGRFieldDefnH hFld = OGR_Fld_Create(pszElevAttrib, OFTReal);
     200           5 :     OGRErr eErr = OGR_L_CreateField(hLayer, hFld, FALSE);
     201           5 :     OGR_Fld_Destroy(hFld);
     202           5 :     if (eErr == OGRERR_FAILURE)
     203             :     {
     204           0 :         exit(1);
     205             :     }
     206           5 : }
     207             : 
     208             : /************************************************************************/
     209             : /*                                main()                                */
     210             : /************************************************************************/
     211             : 
     212           9 : MAIN_START(argc, argv)
     213             : 
     214             : {
     215             : 
     216           9 :     GDALProgressFunc pfnProgress = nullptr;
     217             : 
     218           9 :     EarlySetConfigOptions(argc, argv);
     219             : 
     220             :     /* -------------------------------------------------------------------- */
     221             :     /*      Register standard GDAL drivers, and process generic GDAL        */
     222             :     /*      command options.                                                */
     223             :     /* -------------------------------------------------------------------- */
     224             : 
     225           9 :     GDALAllRegister();
     226           9 :     OGRRegisterAll();
     227             : 
     228           9 :     argc = GDALGeneralCmdLineProcessor(argc, &argv, 0);
     229           9 :     if (argc < 1)
     230           0 :         exit(-argc);
     231             : 
     232             :     /* -------------------------------------------------------------------- */
     233             :     /*      Parse arguments.                                                */
     234             :     /* -------------------------------------------------------------------- */
     235             : 
     236           9 :     if (argc < 2)
     237             :     {
     238             :         try
     239             :         {
     240           0 :             GDALContourOptions sOptions;
     241           0 :             auto argParser = GDALContourAppOptionsGetParser(&sOptions);
     242           0 :             fprintf(stderr, "%s\n", argParser->usage().c_str());
     243             :         }
     244           0 :         catch (const std::exception &err)
     245             :         {
     246           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
     247           0 :                      err.what());
     248             :         }
     249           0 :         exit(1);
     250             :     }
     251             : 
     252          17 :     GDALContourOptions sOptions;
     253             : 
     254             :     try
     255             :     {
     256          17 :         auto argParser = GDALContourAppOptionsGetParser(&sOptions);
     257           9 :         argParser->parse_args_without_binary_name(argv + 1);
     258             :     }
     259           0 :     catch (const std::exception &error)
     260             :     {
     261           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", error.what());
     262           0 :         exit(1);
     263             :     }
     264             : 
     265          16 :     if (sOptions.aosSrcFilename.find("/vsistdout/") != std::string::npos ||
     266           8 :         sOptions.aosDestFilename.find("/vsistdout/") != std::string::npos)
     267             :     {
     268           0 :         sOptions.bQuiet = true;
     269             :     }
     270             : 
     271           8 :     if (!sOptions.bQuiet)
     272           8 :         pfnProgress = GDALTermProgress;
     273             : 
     274             :     /* -------------------------------------------------------------------- */
     275             :     /*      Open source raster file.                                        */
     276             :     /* -------------------------------------------------------------------- */
     277             :     GDALDatasetH hSrcDS =
     278           8 :         GDALOpen(sOptions.aosSrcFilename.c_str(), GA_ReadOnly);
     279           8 :     if (hSrcDS == nullptr)
     280           0 :         exit(2);
     281             : 
     282           8 :     GDALRasterBandH hBand = GDALGetRasterBand(hSrcDS, sOptions.nBand);
     283           8 :     if (hBand == nullptr)
     284             :     {
     285           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     286             :                  "Band %d does not exist on dataset.", sOptions.nBand);
     287           0 :         exit(2);
     288             :     }
     289             : 
     290           8 :     if (!sOptions.bNoDataSet && !sOptions.bIgnoreNoData)
     291             :     {
     292             :         int bNoDataSet;
     293           8 :         sOptions.dfNoData = GDALGetRasterNoDataValue(hBand, &bNoDataSet);
     294           8 :         sOptions.bNoDataSet = bNoDataSet;
     295             :     }
     296             : 
     297             :     /* -------------------------------------------------------------------- */
     298             :     /*      Try to get a coordinate system from the raster.                 */
     299             :     /* -------------------------------------------------------------------- */
     300           8 :     OGRSpatialReferenceH hSRS = GDALGetSpatialRef(hSrcDS);
     301             : 
     302             :     /* -------------------------------------------------------------------- */
     303             :     /*      Create the output file.                                         */
     304             :     /* -------------------------------------------------------------------- */
     305           8 :     CPLString osFormat;
     306           8 :     if (sOptions.osFormat.empty())
     307             :     {
     308             :         const auto aoDrivers = GetOutputDriversFor(
     309           8 :             sOptions.aosDestFilename.c_str(), GDAL_OF_VECTOR);
     310           8 :         if (aoDrivers.empty())
     311             :         {
     312           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot guess driver for %s",
     313             :                      sOptions.aosDestFilename.c_str());
     314           0 :             exit(10);
     315             :         }
     316             :         else
     317             :         {
     318           8 :             if (aoDrivers.size() > 1)
     319             :             {
     320           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     321             :                          "Several drivers matching %s extension. Using %s",
     322             :                          CPLGetExtension(sOptions.aosDestFilename.c_str()),
     323           0 :                          aoDrivers[0].c_str());
     324             :             }
     325           8 :             osFormat = aoDrivers[0];
     326             :         }
     327             :     }
     328             :     else
     329             :     {
     330           0 :         osFormat = sOptions.osFormat;
     331             :     }
     332             : 
     333           8 :     OGRSFDriverH hDriver = OGRGetDriverByName(osFormat.c_str());
     334             : 
     335           8 :     if (hDriver == nullptr)
     336             :     {
     337           0 :         fprintf(stderr, "Unable to find format driver named %s.\n",
     338             :                 osFormat.c_str());
     339           0 :         exit(10);
     340             :     }
     341             : 
     342           8 :     OGRDataSourceH hDS = OGR_Dr_CreateDataSource(
     343             :         hDriver, sOptions.aosDestFilename.c_str(), sOptions.aosCreationOptions);
     344           8 :     if (hDS == nullptr)
     345           0 :         exit(1);
     346             : 
     347          16 :     OGRLayerH hLayer = OGR_DS_CreateLayer(
     348             :         hDS, sOptions.osNewLayerName.c_str(), hSRS,
     349           8 :         sOptions.bPolygonize
     350           0 :             ? (sOptions.b3D ? wkbMultiPolygon25D : wkbMultiPolygon)
     351           8 :             : (sOptions.b3D ? wkbLineString25D : wkbLineString),
     352             :         sOptions.aosCreationOptions);
     353           8 :     if (hLayer == nullptr)
     354           0 :         exit(1);
     355             : 
     356           8 :     OGRFieldDefnH hFld = OGR_Fld_Create("ID", OFTInteger);
     357           8 :     OGR_Fld_SetWidth(hFld, 8);
     358           8 :     OGR_L_CreateField(hLayer, hFld, FALSE);
     359           8 :     OGR_Fld_Destroy(hFld);
     360             : 
     361           8 :     if (sOptions.bPolygonize)
     362             :     {
     363           0 :         if (!sOptions.osElevAttrib.empty())
     364             :         {
     365           0 :             sOptions.osElevAttrib.clear();
     366           0 :             CPLError(CE_Warning, CPLE_NotSupported,
     367             :                      "-a is ignored in polygonal contouring mode. "
     368             :                      "Use -amin and/or -amax instead");
     369             :         }
     370             :     }
     371             :     else
     372             :     {
     373          16 :         if (!sOptions.osElevAttribMin.empty() ||
     374           8 :             !sOptions.osElevAttribMax.empty())
     375             :         {
     376           0 :             sOptions.osElevAttribMin.clear();
     377           0 :             sOptions.osElevAttribMax.clear();
     378           0 :             CPLError(CE_Warning, CPLE_NotSupported,
     379             :                      "-amin and/or -amax are ignored in line contouring mode. "
     380             :                      "Use -a instead");
     381             :         }
     382             :     }
     383             : 
     384           8 :     if (!sOptions.osElevAttrib.empty())
     385             :     {
     386           5 :         CreateElevAttrib(sOptions.osElevAttrib.c_str(), hLayer);
     387             :     }
     388             : 
     389           8 :     if (!sOptions.osElevAttribMin.empty())
     390             :     {
     391           0 :         CreateElevAttrib(sOptions.osElevAttribMin.c_str(), hLayer);
     392             :     }
     393             : 
     394           8 :     if (!sOptions.osElevAttribMax.empty())
     395             :     {
     396           0 :         CreateElevAttrib(sOptions.osElevAttribMax.c_str(), hLayer);
     397             :     }
     398             : 
     399             :     /* -------------------------------------------------------------------- */
     400             :     /*      Invoke.                                                         */
     401             :     /* -------------------------------------------------------------------- */
     402           8 :     int iIDField = OGR_FD_GetFieldIndex(OGR_L_GetLayerDefn(hLayer), "ID");
     403           8 :     int iElevField = (sOptions.osElevAttrib.empty())
     404           8 :                          ? -1
     405           5 :                          : OGR_FD_GetFieldIndex(OGR_L_GetLayerDefn(hLayer),
     406           8 :                                                 sOptions.osElevAttrib.c_str());
     407             : 
     408             :     int iElevFieldMin =
     409           8 :         (sOptions.osElevAttribMin.empty())
     410           8 :             ? -1
     411           0 :             : OGR_FD_GetFieldIndex(OGR_L_GetLayerDefn(hLayer),
     412           8 :                                    sOptions.osElevAttribMin.c_str());
     413             : 
     414             :     int iElevFieldMax =
     415           8 :         (sOptions.osElevAttribMax.empty())
     416           8 :             ? -1
     417           0 :             : OGR_FD_GetFieldIndex(OGR_L_GetLayerDefn(hLayer),
     418           8 :                                    sOptions.osElevAttribMax.c_str());
     419             : 
     420           8 :     char **options = nullptr;
     421           8 :     if (!sOptions.adfFixedLevels.empty())
     422             :     {
     423           1 :         std::string values = "FIXED_LEVELS=";
     424           4 :         for (size_t i = 0; i < sOptions.adfFixedLevels.size(); i++)
     425             :         {
     426           3 :             const int sz = 32;
     427           3 :             char *newValue = new char[sz + 1];
     428           3 :             if (i == sOptions.adfFixedLevels.size() - 1)
     429             :             {
     430           1 :                 CPLsnprintf(newValue, sz + 1, "%f", sOptions.adfFixedLevels[i]);
     431             :             }
     432             :             else
     433             :             {
     434           2 :                 CPLsnprintf(newValue, sz + 1, "%f,",
     435           2 :                             sOptions.adfFixedLevels[i]);
     436             :             }
     437           3 :             values = values + std::string(newValue);
     438           3 :             delete[] newValue;
     439             :         }
     440           1 :         options = CSLAddString(options, values.c_str());
     441             :     }
     442           7 :     else if (sOptions.dfExpBase != 0.0)
     443             :     {
     444             :         options =
     445           0 :             CSLAppendPrintf(options, "LEVEL_EXP_BASE=%f", sOptions.dfExpBase);
     446             :     }
     447           7 :     else if (sOptions.dfInterval != 0.0)
     448             :     {
     449             :         options =
     450           7 :             CSLAppendPrintf(options, "LEVEL_INTERVAL=%f", sOptions.dfInterval);
     451             :     }
     452             : 
     453           8 :     if (sOptions.dfOffset != 0.0)
     454             :     {
     455           0 :         options = CSLAppendPrintf(options, "LEVEL_BASE=%f", sOptions.dfOffset);
     456             :     }
     457             : 
     458           8 :     if (sOptions.bNoDataSet)
     459             :     {
     460           5 :         options = CSLAppendPrintf(options, "NODATA=%.19g", sOptions.dfNoData);
     461             :     }
     462           8 :     if (iIDField != -1)
     463             :     {
     464           8 :         options = CSLAppendPrintf(options, "ID_FIELD=%d", iIDField);
     465             :     }
     466           8 :     if (iElevField != -1)
     467             :     {
     468           5 :         options = CSLAppendPrintf(options, "ELEV_FIELD=%d", iElevField);
     469             :     }
     470           8 :     if (iElevFieldMin != -1)
     471             :     {
     472           0 :         options = CSLAppendPrintf(options, "ELEV_FIELD_MIN=%d", iElevFieldMin);
     473             :     }
     474           8 :     if (iElevFieldMax != -1)
     475             :     {
     476           0 :         options = CSLAppendPrintf(options, "ELEV_FIELD_MAX=%d", iElevFieldMax);
     477             :     }
     478           8 :     if (sOptions.bPolygonize)
     479             :     {
     480           0 :         options = CSLAppendPrintf(options, "POLYGONIZE=YES");
     481             :     }
     482             : 
     483             :     CPLErr eErr =
     484           8 :         GDALContourGenerateEx(hBand, hLayer, options, pfnProgress, nullptr);
     485             : 
     486           8 :     CSLDestroy(options);
     487           8 :     if (GDALClose(hDS) != CE_None)
     488           0 :         eErr = CE_Failure;
     489           8 :     GDALClose(hSrcDS);
     490             : 
     491           8 :     CSLDestroy(argv);
     492           8 :     GDALDestroyDriverManager();
     493           8 :     OGRCleanupAll();
     494             : 
     495           8 :     return (eErr == CE_None) ? 0 : 1;
     496             : }
     497             : 
     498           0 : MAIN_END

Generated by: LCOV version 1.14