From 7b1f8b4dde28878ab1e2f308e13a25ca18165890 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 11 Mar 2024 20:44:44 +0100 Subject: [PATCH] gdal_viewshed: use GDALArgumentParser --- apps/gdal_viewshed.cpp | 342 ++++++++++------------- autotest/utilities/test_gdal_viewshed.py | 14 +- 2 files changed, 152 insertions(+), 204 deletions(-) diff --git a/apps/gdal_viewshed.cpp b/apps/gdal_viewshed.cpp index 08180fab49cc..2dcf9d0357cc 100644 --- a/apps/gdal_viewshed.cpp +++ b/apps/gdal_viewshed.cpp @@ -35,215 +35,172 @@ #include "ogr_srs_api.h" #include "ogr_spatialref.h" #include "commonutils.h" +#include "gdalargumentparser.h" /************************************************************************/ -/* Usage() */ +/* main() */ /************************************************************************/ -static void Usage(bool bIsError, const char *pszErrorMsg = nullptr) +MAIN_START(argc, argv) { - fprintf( - bIsError ? stderr : stdout, - "Usage: gdal_viewshed [--help] [--help-general]\n" - " [-b ]\n" - " [-a_nodata ] [-f ]\n" - " [-oz ] [-tz ] " - "[-md ]\n" - " -ox -oy \n" - " [-vv ] [-iv ]\n" - " [-ov ] [-cc ]\n" - " [-co =]...\n" - " [-q] [-om ]\n" - " \n"); - - if (pszErrorMsg != nullptr) - fprintf(stderr, "\nFAILURE: %s\n", pszErrorMsg); - - exit(bIsError ? 1 : 0); -} + EarlySetConfigOptions(argc, argv); -static double CPLAtofTaintedSuppressed(const char *pszVal) -{ - // coverity[tainted_data] - return CPLAtof(pszVal); -} + GDALAllRegister(); -/************************************************************************/ -/* main() */ -/************************************************************************/ + argc = GDALGeneralCmdLineProcessor(argc, &argv, 0); + CPLStringList aosArgv; + aosArgv.Assign(argv, /* bTakeOwnership= */ true); + if (argc < 1) + std::exit(-argc); -#define CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(nExtraArg) \ - do \ - { \ - if (i + nExtraArg >= argc) \ - Usage(true, CPLSPrintf("%s option requires %d argument(s)", \ - argv[i], nExtraArg)); \ - } while (false) + GDALArgumentParser argParser(aosArgv[0], /* bForBinary=*/true); -MAIN_START(argc, argv) + argParser.add_description( + _("Calculates a viewshed raster from an input raster DEM.")); + + argParser.add_epilog(_("For more details, consult " + "https://gdal.org/programs/gdal_viewshed.html")); + + std::string osFormat; + argParser.add_output_format_argument(osFormat); + + double dfObserverX = 0; + argParser.add_argument("-ox") + .store_into(dfObserverX) + .required() + .metavar("") + .help(_("The X position of the observer (in SRS units).")); + + double dfObserverY = 0; + argParser.add_argument("-oy") + .store_into(dfObserverY) + .required() + .metavar("") + .help(_("The Y position of the observer (in SRS units).")); + + double dfObserverHeight = 2; + argParser.add_argument("-oz") + .store_into(dfObserverHeight) + .default_value(dfObserverHeight) + .metavar("") + .nargs(1) + .help(_("The height of the observer above the DEM surface in the " + "height unit of the DEM.")); + + double dfVisibleVal = 255; + argParser.add_argument("-vv") + .store_into(dfVisibleVal) + .default_value(dfVisibleVal) + .metavar("") + .nargs(1) + .help(_("Pixel value to set for visible areas.")); -{ - int nBandIn = 1; - double dfObserverHeight = 2.0; - double dfTargetHeight = 0.0; - double dfMaxDistance = 0.0; - bool bObserverXSpecified = false; - double dfObserverX = 0.0; - bool bObserverYSpecified = false; - double dfObserverY = 0.0; - double dfVisibleVal = 255.0; double dfInvisibleVal = 0.0; + argParser.add_argument("-iv") + .store_into(dfInvisibleVal) + .default_value(dfInvisibleVal) + .metavar("") + .nargs(1) + .help(_("Pixel value to set for invisible areas.")); + double dfOutOfRangeVal = 0.0; + argParser.add_argument("-ov") + .store_into(dfOutOfRangeVal) + .default_value(dfOutOfRangeVal) + .metavar("") + .nargs(1) + .help( + _("Pixel value to set for the cells that fall outside of the range " + "specified by the observer location and the maximum distance.")); + + CPLStringList aosCreationOptions; + argParser.add_creation_options_argument(aosCreationOptions); + double dfNoDataVal = -1.0; + argParser.add_argument("-a_nodata") + .store_into(dfNoDataVal) + .default_value(dfNoDataVal) + .metavar("") + .nargs(1) + .help(_("The value to be set for the cells in the output raster that " + "have no data.")); + + double dfTargetHeight = 0.0; + argParser.add_argument("-tz") + .store_into(dfTargetHeight) + .default_value(dfTargetHeight) + .metavar("") + .nargs(1) + .help(_("The height of the target above the DEM surface in the height " + "unit of the DEM.")); + + double dfMaxDistance = 0.0; + argParser.add_argument("-md") + .store_into(dfMaxDistance) + .default_value(dfMaxDistance) + .metavar("") + .nargs(1) + .help(_("Maximum distance from observer to compute visibility.")); + // Value for standard atmospheric refraction. See // doc/source/programs/gdal_viewshed.rst - bool bCurvCoeffSpecified = false; double dfCurvCoeff = 0.85714; - const char *pszDriverName = nullptr; - const char *pszSrcFilename = nullptr; - const char *pszDstFilename = nullptr; - bool bQuiet = false; - GDALProgressFunc pfnProgress = nullptr; - char **papszCreateOptions = nullptr; - const char *pszOutputMode = nullptr; + argParser.add_argument("-cc") + .store_into(dfCurvCoeff) + .default_value(dfCurvCoeff) + .metavar("") + .nargs(1) + .help(_("Coefficient to consider the effect of the curvature and " + "refraction.")); - GDALAllRegister(); + int nBandIn = 1; + argParser.add_argument("-b") + .store_into(nBandIn) + .default_value(nBandIn) + .metavar("") + .nargs(1) + .help(_("Select an input band band containing the DEM data.")); - argc = GDALGeneralCmdLineProcessor(argc, &argv, 0); + std::string osOutputMode; + argParser.add_argument("-om") + .store_into(osOutputMode) + .choices("NORMAL", "DEM", "GROUND") + .metavar("NORMAL|DEM|GROUND") + .default_value("NORMAL") + .nargs(1) + .help(_("Sets what information the output contains.")); - /* -------------------------------------------------------------------- */ - /* Parse arguments. */ - /* -------------------------------------------------------------------- */ - for (int i = 1; i < argc; i++) - { - if (EQUAL(argv[i], "--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[i], "--help")) - Usage(false); - else if (EQUAL(argv[i], "-f") || EQUAL(argv[i], "-of")) - { - CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1); - pszDriverName = argv[++i]; - } - else if (EQUAL(argv[i], "-ox")) - { - CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1); - bObserverXSpecified = true; - dfObserverX = CPLAtofTaintedSuppressed(argv[++i]); - } - else if (EQUAL(argv[i], "-oy")) - { - CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1); - bObserverYSpecified = true; - dfObserverY = CPLAtofTaintedSuppressed(argv[++i]); - } - else if (EQUAL(argv[i], "-oz")) - { - CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1); - dfObserverHeight = CPLAtofTaintedSuppressed(argv[++i]); - } - else if (EQUAL(argv[i], "-vv")) - { - CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1); - dfVisibleVal = CPLAtofTaintedSuppressed(argv[++i]); - } - else if (EQUAL(argv[i], "-iv")) - { - CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1); - dfInvisibleVal = CPLAtofTaintedSuppressed(argv[++i]); - } - else if (EQUAL(argv[i], "-ov")) - { - CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1); - dfOutOfRangeVal = CPLAtofTaintedSuppressed(argv[++i]); - } - else if (EQUAL(argv[i], "-co")) - { - CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1); - papszCreateOptions = CSLAddString(papszCreateOptions, argv[++i]); - } - else if (EQUAL(argv[i], "-a_nodata")) - { - CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1); - dfNoDataVal = CPLAtofM(argv[++i]); - ; - } - else if (EQUAL(argv[i], "-tz")) - { - CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1); - dfTargetHeight = CPLAtofTaintedSuppressed(argv[++i]); - } - else if (EQUAL(argv[i], "-md")) - { - CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1); - dfMaxDistance = CPLAtofTaintedSuppressed(argv[++i]); - } - else if (EQUAL(argv[i], "-cc")) - { - CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1); - bCurvCoeffSpecified = true; - dfCurvCoeff = CPLAtofTaintedSuppressed(argv[++i]); - } - else if (EQUAL(argv[i], "-b")) - { - CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1); - nBandIn = atoi(argv[++i]); - } - else if (EQUAL(argv[i], "-om")) - { - CHECK_HAS_ENOUGH_ADDITIONAL_ARGS(1); - pszOutputMode = argv[++i]; - } - else if (EQUAL(argv[i], "-q") || EQUAL(argv[i], "-quiet")) - { - bQuiet = TRUE; - } - else if (pszSrcFilename == nullptr) - { - pszSrcFilename = argv[i]; - } - else if (pszDstFilename == nullptr) - { - pszDstFilename = argv[i]; - } - else - Usage(true, "Too many command options."); - } + bool bQuiet = false; + argParser.add_quiet_argument(&bQuiet); - if (pszSrcFilename == nullptr) - { - Usage(true, "Missing source filename."); - } + std::string osSrcFilename; + argParser.add_argument("src_filename") + .store_into(osSrcFilename) + .metavar(""); - if (pszDstFilename == nullptr) - { - Usage(true, "Missing destination filename."); - } + std::string osDstFilename; + argParser.add_argument("dst_filename") + .store_into(osDstFilename) + .metavar(""); - if (!bObserverXSpecified) + try { - Usage(true, "Missing -ox."); + argParser.parse_args(aosArgv); } - - if (!bObserverYSpecified) + catch (const std::exception &err) { - Usage(true, "Missing -oy."); + argParser.display_error_and_usage(err); + std::exit(1); } + GDALProgressFunc pfnProgress = nullptr; if (!bQuiet) pfnProgress = GDALTermProgress; - CPLString osFormat; - if (pszDriverName == nullptr) + if (osFormat.empty()) { - osFormat = GetOutputDriverForRaster(pszDstFilename); + osFormat = GetOutputDriverForRaster(osDstFilename.c_str()); if (osFormat.empty()) { exit(2); @@ -251,29 +208,19 @@ MAIN_START(argc, argv) } GDALViewshedOutputType outputMode = GVOT_NORMAL; - if (pszOutputMode != nullptr) + if (EQUAL(osOutputMode.c_str(), "DEM")) { - if (EQUAL(pszOutputMode, "NORMAL")) - { - } - else if (EQUAL(pszOutputMode, "DEM")) - { - outputMode = GVOT_MIN_TARGET_HEIGHT_FROM_DEM; - } - else if (EQUAL(pszOutputMode, "GROUND")) - { - outputMode = GVOT_MIN_TARGET_HEIGHT_FROM_GROUND; - } - else - { - Usage(true, "-om must be either NORMAL, DEM or GROUND"); - } + outputMode = GVOT_MIN_TARGET_HEIGHT_FROM_DEM; + } + else if (EQUAL(osOutputMode.c_str(), "GROUND")) + { + outputMode = GVOT_MIN_TARGET_HEIGHT_FROM_GROUND; } /* -------------------------------------------------------------------- */ /* Open source raster file. */ /* -------------------------------------------------------------------- */ - GDALDatasetH hSrcDS = GDALOpen(pszSrcFilename, GA_ReadOnly); + GDALDatasetH hSrcDS = GDALOpen(osSrcFilename.c_str(), GA_ReadOnly); if (hSrcDS == nullptr) exit(2); @@ -285,6 +232,7 @@ MAIN_START(argc, argv) exit(2); } + const bool bCurvCoeffSpecified = argParser.is_used("-cc"); if (!bCurvCoeffSpecified) { const OGRSpatialReference *poSRS = @@ -308,8 +256,8 @@ MAIN_START(argc, argv) /* Invoke. */ /* -------------------------------------------------------------------- */ GDALDatasetH hDstDS = GDALViewshedGenerate( - hBand, pszDriverName ? pszDriverName : osFormat.c_str(), pszDstFilename, - papszCreateOptions, dfObserverX, dfObserverY, dfObserverHeight, + hBand, osFormat.c_str(), osDstFilename.c_str(), + aosCreationOptions.List(), dfObserverX, dfObserverY, dfObserverHeight, dfTargetHeight, dfVisibleVal, dfInvisibleVal, dfOutOfRangeVal, dfNoDataVal, dfCurvCoeff, GVM_Edge, dfMaxDistance, pfnProgress, nullptr, outputMode, nullptr); @@ -318,8 +266,6 @@ MAIN_START(argc, argv) if (GDALClose(hDstDS) != CE_None) bSuccess = false; - CSLDestroy(argv); - CSLDestroy(papszCreateOptions); GDALDestroyDriverManager(); OGRCleanupAll(); diff --git a/autotest/utilities/test_gdal_viewshed.py b/autotest/utilities/test_gdal_viewshed.py index 01ea6688e1d1..5773a0b0279e 100755 --- a/autotest/utilities/test_gdal_viewshed.py +++ b/autotest/utilities/test_gdal_viewshed.py @@ -217,8 +217,8 @@ def test_gdal_viewshed_all_options(gdal_viewshed_path, tmp_path, viewshed_input) def test_gdal_viewshed_missing_source(gdal_viewshed_path): - _, err = gdaltest.runexternal_out_and_err(gdal_viewshed_path) - assert "Missing source filename" in err + _, err = gdaltest.runexternal_out_and_err(gdal_viewshed_path + " -ox 0 -oy 0") + assert "dst_filename: 1 argument(s) expected. 0 provided" in err ############################################################################### @@ -226,8 +226,10 @@ def test_gdal_viewshed_missing_source(gdal_viewshed_path): def test_gdal_viewshed_missing_destination(gdal_viewshed_path): - _, err = gdaltest.runexternal_out_and_err(gdal_viewshed_path + " /dev/null") - assert "Missing destination filename" in err + _, err = gdaltest.runexternal_out_and_err( + gdal_viewshed_path + " -ox 0 -oy 0 /dev/null" + ) + assert "Error: dst_filename: 1 argument(s) expected. 0 provided" in err ############################################################################### @@ -238,7 +240,7 @@ def test_gdal_viewshed_missing_ox(gdal_viewshed_path): _, err = gdaltest.runexternal_out_and_err( gdal_viewshed_path + " /dev/null /dev/null" ) - assert "Missing -ox" in err + assert "-ox: required" in err ############################################################################### @@ -249,7 +251,7 @@ def test_gdal_viewshed_missing_oy(gdal_viewshed_path): _, err = gdaltest.runexternal_out_and_err( gdal_viewshed_path + " -ox 0 /dev/null /dev/null" ) - assert "Missing -oy" in err + assert "-oy: required" in err ###############################################################################