Skip to content

Commit

Permalink
Merge pull request #9947 from elpaso/gdaldem-argparser
Browse files Browse the repository at this point in the history
[gdaldem] Use GDALArgumentParser
  • Loading branch information
rouault committed May 17, 2024
2 parents 1a1bbde + 7ce0b08 commit 24e799a
Show file tree
Hide file tree
Showing 5 changed files with 699 additions and 377 deletions.
18 changes: 13 additions & 5 deletions apps/gdal_utils_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ struct GDALInfoOptionsForBinary

struct GDALDEMProcessingOptionsForBinary
{
char *pszProcessing;
char *pszSrcFilename;
char *pszColorFilename;
char *pszDstFilename;
int bQuiet;
std::string osProcessing;
std::string osSrcFilename;
std::string osColorFilename;
std::string osDstFilename;
bool bQuiet = false;
};

CPL_C_END
Expand Down Expand Up @@ -251,6 +251,14 @@ std::string CPL_DLL GDALTileIndexAppGetParserUsage();

std::string CPL_DLL GDALFootprintAppGetParserUsage();

/**
* Returns the gdaldem usage help string
* @param osProcessingMode Processing mode (subparser name)
* @return gdaldem usage help string
*/
std::string CPL_DLL
GDALDEMAppGetParserUsage(const std::string &osProcessingMode);

#endif /* #ifndef DOXYGEN_SKIP */

#endif /* GDAL_UTILS_PRIV_H_INCLUDED */
74 changes: 74 additions & 0 deletions apps/gdalargumentparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,58 @@ Argument &GDALArgumentParser::add_inverted_logic_flag(const std::string &name,
.help(help);
}

GDALArgumentParser *
GDALArgumentParser::add_subparser(const std::string &description,
bool bForBinary)
{
auto parser = std::make_unique<GDALArgumentParser>(description, bForBinary);
ArgumentParser::add_subparser(*parser.get());
aoSubparsers.emplace_back(std::move(parser));
return aoSubparsers.back().get();
}

GDALArgumentParser *GDALArgumentParser::get_subparser(const std::string &name)
{
auto it = std::find_if(
aoSubparsers.begin(), aoSubparsers.end(),
[&name](const auto &parser)
{ return EQUAL(name.c_str(), parser->m_program_name.c_str()); });
return it != aoSubparsers.end() ? it->get() : nullptr;
}

bool GDALArgumentParser::is_used_globally(const std::string &name)
{
try
{
return ArgumentParser::is_used(name);
}
catch (std::logic_error &)
{
// ignore
}

// Check if it is used by a subparser
// loop through subparsers
for (const auto &subparser : aoSubparsers)
{
// convert subparser name to lower case
std::string subparser_name = subparser->m_program_name;
std::transform(subparser_name.begin(), subparser_name.end(),
subparser_name.begin(),
[](int c) -> char
{ return static_cast<char>(::tolower(c)); });
if (m_subparser_used.find(subparser_name) != m_subparser_used.end())
{
if (subparser->is_used_globally(name))
{
return true;
}
}
}

return false;
}

/************************************************************************/
/* parse_args() */
/************************************************************************/
Expand Down Expand Up @@ -431,6 +483,28 @@ void GDALArgumentParser::parse_args(const CPLStringList &aosArgs)
}
else
{
// Check sub-parsers
auto subparser = get_subparser(current_argument);
if (subparser)
{

// build list of remaining args
const auto unprocessed_arguments =
CPLStringList(std::vector<std::string>(it, end));

// invoke subparser
m_is_parsed = true;
// convert to lower case
std::string current_argument_lower = current_argument;
std::transform(current_argument_lower.begin(),
current_argument_lower.end(),
current_argument_lower.begin(),
[](int c) -> char
{ return static_cast<char>(::tolower(c)); });
m_subparser_used[current_argument_lower] = true;
return subparser->parse_args(unprocessed_arguments);
}

if (m_positional_arguments.empty())
{
throw std::runtime_error(
Expand Down
25 changes: 25 additions & 0 deletions apps/gdalargumentparser.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,34 @@ class GDALArgumentParser : public ArgumentParser
bool *store_into = nullptr,
const std::string &help = "");

/**
* Create and add a subparser to the argument parser, keeping ownership
* @param description Subparser description
* @param bForBinary True if the subparser is for a binary utility, false for a library
* @return A pointer to the created subparser
*/
GDALArgumentParser *add_subparser(const std::string &description,
bool bForBinary);

/**
* Get a subparser by name (case insensitive)
* @param name Subparser name
* @return The subparser or nullptr if not found
*/
GDALArgumentParser *get_subparser(const std::string &name);

/**
* Return true if the argument is used in the command line (also checking subparsers, if any)
* @param name Argument name
* @return True if the argument is used, false if it is not used.
* @note Opposite to the is_used() function this is case insensitive, also checks subparsers and never throws
*/
bool is_used_globally(const std::string &name);

private:
std::map<std::string, ArgumentParser::argument_it>::iterator
find_argument(const std::string &name);
std::vector<std::unique_ptr<GDALArgumentParser>> aoSubparsers;
};

#endif /* GDALARGUMENTPARSER_H */
188 changes: 40 additions & 148 deletions apps/gdaldem_bin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,111 +37,25 @@
#include "gdal_priv.h"
#include "commonutils.h"

/************************************************************************/
/* Usage() */
/************************************************************************/

static void Usage(bool bIsError, const char *pszErrorMsg = nullptr)

/**
* @brief Makes sure the GDAL library is properly cleaned up before exiting.
* @param nCode exit code
* @todo Move to API
*/
static void GDALExit(const int nCode)
{
fprintf(
bIsError ? stderr : stdout,
" Usage: [--help] [--help-general]\n"
" - To generate a shaded relief map from any GDAL-supported elevation "
"raster : \n\n"
" gdaldem hillshade <input_dem> <output_hillshade> \n"
" [-z <zfactor>] [-s <scale>] \n"
" [-az <azimuth>] [-alt <altitude>]\n"
" [-alg ZevenbergenThorne] [-combined | "
"-multidirectional | -igor]\n"
" [-compute_edges] [-b <Band>] [-of <format>] "
"[-co <NAME>=<VALUE>]... [-q]\n"
"\n"
" - To generates a slope map from any GDAL-supported elevation raster "
":\n\n"
" gdaldem slope <input_dem> <output_slope_map> \n"
" [-p] [-s <scale>]\n"
" [-alg ZevenbergenThorne]\n"
" [-compute_edges] [-b <band>] [-of <format>] "
"[-co <NAME>=<VALUE>]... [-q]\n"
"\n"
" - To generate an aspect map from any GDAL-supported elevation "
"raster\n"
" Outputs a 32-bit float tiff with pixel values from 0-360 "
"indicating azimuth :\n\n"
" gdaldem aspect <input_dem> <output_aspect_map> \n"
" [-trigonometric] [-zero_for_flat]\n"
" [-alg ZevenbergenThorne]\n"
" [-compute_edges] [-b <band>] [-of format] "
"[-co <NAME>=<VALUE>]... [-q]\n"
"\n"
" - To generate a color relief map from any GDAL-supported elevation "
"raster\n"
" gdaldem color-relief <input_dem> <color_text_file> "
"<output_color_relief_map>\n"
" [-alpha] [-exact_color_entry | "
"-nearest_color_entry]\n"
" [-b <band>] [-of format] "
"[-co <NAME>=<VALUE>]... [-q]\n"
" where color_text_file contains lines of the format "
"\"elevation_value red green blue\"\n"
"\n"
" - To generate a Terrain Ruggedness Index (TRI) map from any "
"GDAL-supported elevation raster\n"
" gdaldem TRI <input_dem> <output_TRI_map>\n"
" [-alg Wilson|Riley]\n"
" [-compute_edges] [-b <band>] [-of <format>] "
"[-co <NAME>=<VALUE>]... [-q]\n"
"\n"
" - To generate a Topographic Position Index (TPI) map from any "
"GDAL-supported elevation raster\n"
" gdaldem TPI <input_dem> <output_TPI_map>\n"
" [-compute_edges] [-b <band>] [-of <format>] "
"[-co <NAME>=<VALUE>]... [-q]\n"
"\n"
" - To generate a roughness map from any GDAL-supported elevation "
"raster\n"
" gdaldem roughness <input_dem> <output_roughness_map>\n"
" [-compute_edges] [-b <band>] [-of <format>] "
"[-co <NAME>=<VALUE>]... [-q]\n"
"\n"
" Notes : \n"
" Scale is the ratio of vertical units to horizontal\n"
" for Feet:Latlong use scale=370400, for Meters:LatLong use "
"scale=111120 \n\n");

if (pszErrorMsg != nullptr)
fprintf(stderr, "\nFAILURE: %s\n", pszErrorMsg);

exit(bIsError ? 1 : 0);
GDALDestroy();
exit(nCode);
}

/************************************************************************/
/* GDALDEMProcessingOptionsForBinaryNew() */
/************************************************************************/

static GDALDEMProcessingOptionsForBinary *
GDALDEMProcessingOptionsForBinaryNew(void)
{
return static_cast<GDALDEMProcessingOptionsForBinary *>(
CPLCalloc(1, sizeof(GDALDEMProcessingOptionsForBinary)));
}

/************************************************************************/
/* GDALDEMProcessingOptionsForBinaryFree() */
/* Usage() */
/************************************************************************/

static void GDALDEMProcessingOptionsForBinaryFree(
GDALDEMProcessingOptionsForBinary *psOptionsForBinary)
static void Usage(const std::string &osProcessingMode = "")
{
if (psOptionsForBinary)
{
CPLFree(psOptionsForBinary->pszProcessing);
CPLFree(psOptionsForBinary->pszSrcFilename);
CPLFree(psOptionsForBinary->pszColorFilename);
CPLFree(psOptionsForBinary->pszDstFilename);
CPLFree(psOptionsForBinary);
}
fprintf(stderr, "%s\n", GDALDEMAppGetParserUsage(osProcessingMode).c_str());
GDALExit(1);
}

/************************************************************************/
Expand All @@ -153,92 +67,70 @@ MAIN_START(argc, argv)
{
/* Check strict compilation and runtime library version as we use C++ API */
if (!GDAL_CHECK_VERSION(argv[0]))
exit(1);
GDALExit(1);

EarlySetConfigOptions(argc, argv);

GDALAllRegister();

/* -------------------------------------------------------------------- */
/* Register standard GDAL drivers, and process generic GDAL */
/* command options. */
/* -------------------------------------------------------------------- */
GDALAllRegister();
argc = GDALGeneralCmdLineProcessor(argc, &argv, 0);
if (argc < 2)
{
Usage(true, "Not enough arguments.");
Usage();
}

if (EQUAL(argv[1], "--utility_version") ||
EQUAL(argv[1], "--utility-version"))
{
printf(
"%s was compiled against GDAL %s and is running against GDAL %s\n",
argv[0], GDAL_RELEASE_NAME, GDALVersionInfo("RELEASE_NAME"));
CSLDestroy(argv);
return 0;
}
else if (EQUAL(argv[1], "--help"))
Usage(false);

GDALDEMProcessingOptionsForBinary *psOptionsForBinary =
GDALDEMProcessingOptionsForBinaryNew();
// coverity[tainted_data]
GDALDEMProcessingOptions *psOptions =
GDALDEMProcessingOptionsNew(argv + 1, psOptionsForBinary);
GDALDEMProcessingOptionsForBinary sOptionsForBinary;

const std::string osProcessingMode = argv[1];

std::unique_ptr<GDALDEMProcessingOptions,
decltype(&GDALDEMProcessingOptionsFree)>
psOptions{GDALDEMProcessingOptionsNew(argv + 1, &sOptionsForBinary),
GDALDEMProcessingOptionsFree};

CSLDestroy(argv);

if (psOptions == nullptr)
{
Usage(true);
}
if (!psOptions)
Usage(osProcessingMode);

if (!(psOptionsForBinary->bQuiet))
if (!(sOptionsForBinary.bQuiet))
{
GDALDEMProcessingOptionsSetProgress(psOptions, GDALTermProgress,
GDALDEMProcessingOptionsSetProgress(psOptions.get(), GDALTermProgress,
nullptr);
}

if (psOptionsForBinary->pszSrcFilename == nullptr)
{
Usage(true, "Missing source.");
}
if (EQUAL(psOptionsForBinary->pszProcessing, "color-relief") &&
psOptionsForBinary->pszColorFilename == nullptr)
{
Usage(true, "Missing color file.");
}
if (psOptionsForBinary->pszDstFilename == nullptr)
{
Usage(true, "Missing destination.");
}

// Open Dataset and get raster band.
GDALDatasetH hSrcDataset =
GDALOpen(psOptionsForBinary->pszSrcFilename, GA_ReadOnly);
GDALOpen(sOptionsForBinary.osSrcFilename.c_str(), GA_ReadOnly);

if (hSrcDataset == nullptr)
{
fprintf(stderr, "GDALOpen failed - %d\n%s\n", CPLGetLastErrorNo(),
CPLGetLastErrorMsg());
GDALDestroyDriverManager();
exit(1);
GDALExit(1);
}

int bUsageError = FALSE;
GDALDatasetH hOutDS = GDALDEMProcessing(
psOptionsForBinary->pszDstFilename, hSrcDataset,
psOptionsForBinary->pszProcessing, psOptionsForBinary->pszColorFilename,
psOptions, &bUsageError);
GDALDatasetH hOutDS =
GDALDEMProcessing(sOptionsForBinary.osDstFilename.c_str(), hSrcDataset,
sOptionsForBinary.osProcessing.c_str(),
sOptionsForBinary.osColorFilename.c_str(),
psOptions.get(), &bUsageError);

if (bUsageError)
Usage(true);
Usage(osProcessingMode);

const int nRetCode = hOutDS ? 0 : 1;

GDALClose(hSrcDataset);
GDALClose(hOutDS);
GDALDEMProcessingOptionsFree(psOptions);
GDALDEMProcessingOptionsForBinaryFree(psOptionsForBinary);

GDALDestroyDriverManager();
GDALDestroy();

return nRetCode;
}
Expand Down

0 comments on commit 24e799a

Please sign in to comment.