diff --git a/apps/gdal_translate_bin.cpp b/apps/gdal_translate_bin.cpp index 23bdf661f2cf..d3a684fa57cb 100644 --- a/apps/gdal_translate_bin.cpp +++ b/apps/gdal_translate_bin.cpp @@ -38,69 +38,11 @@ /* Usage() */ /* ******************************************************************** */ -static void Usage(bool bIsError, const char *pszErrorMsg = nullptr, - bool bShort = true) CPL_NO_RETURN; - -static void Usage(bool bIsError, const char *pszErrorMsg, bool bShort) - +static void Usage() { - fprintf( - bIsError ? stderr : stdout, - "Usage: gdal_translate [--help] [--help-general] [--long-usage]\n" - " [-ot " - "{Byte/Int8/Int16/UInt16/UInt32/Int32/UInt64/Int64/Float32/Float64/\n" - " CInt16/CInt32/CFloat32/CFloat64}] [-strict]\n" - " [-if ]... [-of ]\n" - " [-b ] [-mask ] [-expand {gray|rgb|rgba}]\n" - " [-outsize [%%]|0 [%%]|0] [-tr ]\n" - " [-ovr |AUTO|AUTO-|NONE]\n" - " [-r " - "{nearest,bilinear,cubic,cubicspline,lanczos,average,mode}]\n" - " [-unscale] [-scale[_bn] [ [ " - "]]]... " - "[-exponent[_bn] ]...\n" - " [-srcwin ] [-epo] [-eco]\n" - " [-projwin ] [-projwin_srs ]\n" - " [-a_srs ] [-a_coord_epoch ]\n" - " [-a_ullr ] [-a_nodata ]\n" - " [-a_gt ]\n" - " [-a_scale ] [-a_offset ]\n" - " [-nogcp] [-gcp " - "[]]...\n" - " |-colorinterp{_bn} {red|green|blue|alpha|gray|undefined}]\n" - " |-colorinterp {red|green|blue|alpha|gray|undefined},...]\n" - " [-mo =]... [-dmo " - "=]... [-q] [-sds]\n" - " [-co =]... [-stats] [-norat] [-noxmp]\n" - " [-oo =]...\n" - " \n"); - - if (!bShort) - { - printf("\n%s\n\n", GDALVersionInfo("--version")); - printf("The following format drivers are configured and support " - "output:\n"); - for (int iDr = 0; iDr < GDALGetDriverCount(); iDr++) - { - GDALDriverH hDriver = GDALGetDriver(iDr); - - if (GDALGetMetadataItem(hDriver, GDAL_DCAP_RASTER, nullptr) != - nullptr && - (GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE, nullptr) != - nullptr || - GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATECOPY, nullptr) != - nullptr)) - { - printf(" %s: %s\n", GDALGetDriverShortName(hDriver), - GDALGetDriverLongName(hDriver)); - } - } - } - - if (pszErrorMsg != nullptr) - fprintf(stderr, "\nFAILURE: %s\n", pszErrorMsg); + fprintf(stderr, "%s\n", GDALTranslateGetParserUsage().c_str()); - exit(bIsError ? 1 : 0); + exit(1); } /************************************************************************/ @@ -125,26 +67,6 @@ MAIN_START(argc, argv) if (argc < 1) exit(-argc); - for (int i = 0; argv != nullptr && argv[i] != nullptr; 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, nullptr); - } - else if (EQUAL(argv[i], "--long-usage")) - { - Usage(false, nullptr, FALSE); - } - } - /* -------------------------------------------------------------------- */ /* Set optimal setting for best performance with huge input VRT. */ /* The rationale for 450 is that typical Linux process allow */ @@ -171,17 +93,7 @@ MAIN_START(argc, argv) if (psOptions == nullptr) { - Usage(true, nullptr); - } - - if (sOptionsForBinary.osSource.empty()) - { - Usage(true, "No source dataset specified."); - } - - if (sOptionsForBinary.osDest.empty()) - { - Usage(true, "No target dataset specified."); + Usage(); } if (sOptionsForBinary.osDest == "/vsistdout/") @@ -350,7 +262,7 @@ MAIN_START(argc, argv) } if (bUsageError == TRUE) - Usage(true); + Usage(); GDALClose(hDataset); GDALTranslateOptionsFree(psOptions); @@ -365,7 +277,7 @@ MAIN_START(argc, argv) hOutDS = GDALTranslate(sOptionsForBinary.osDest.c_str(), hDataset, psOptions, &bUsageError); if (bUsageError == TRUE) - Usage(true); + Usage(); int nRetCode = hOutDS ? 0 : 1; /* Close hOutDS before hDataset for the -f VRT case */ diff --git a/apps/gdal_translate_lib.cpp b/apps/gdal_translate_lib.cpp index 8181c2531e88..aab01d65f888 100644 --- a/apps/gdal_translate_lib.cpp +++ b/apps/gdal_translate_lib.cpp @@ -31,6 +31,7 @@ #include "cpl_port.h" #include "gdal_utils.h" #include "gdal_utils_priv.h" +#include "gdalargumentparser.h" #include #include @@ -126,6 +127,9 @@ struct GDALTranslateOptions /*! for the output bands to be of the indicated data type */ GDALDataType eOutputType = GDT_Unknown; + /*! Used only by parser logic */ + bool bParsedMaskArgument = false; + MaskMode eMaskMode = MASK_AUTO; /*! number of input bands to write to the output file, or to reorder bands @@ -2751,6 +2755,433 @@ static int GetColorInterp(const char *pszStr) return -1; } +/************************************************************************/ +/* GDALTranslateOptionsGetParser() */ +/************************************************************************/ + +static std::unique_ptr +GDALTranslateOptionsGetParser(GDALTranslateOptions *psOptions, + GDALTranslateOptionsForBinary *psOptionsForBinary) +{ + auto argParser = std::make_unique( + "gdal_translate", /* bForBinary=*/psOptionsForBinary != nullptr); + + argParser->add_description( + _("Convert raster data between different formats, with potential " + "subsetting, resampling, and rescaling pixels in the process.")); + + argParser->add_epilog(_("https://gdal.org/programs/gdal_translate.html")); + + argParser->add_output_format_argument(psOptions->osFormat); + + argParser->add_quiet_argument( + psOptionsForBinary ? &(psOptionsForBinary->bQuiet) : nullptr); + + argParser->add_output_type_argument(psOptions->eOutputType); + + argParser->add_argument("-b") + .append() + .metavar("") + .action( + [psOptions](const std::string &s) + { + const char *pszBand = s.c_str(); + bool bMask = false; + if (EQUAL(pszBand, "mask")) + pszBand = "mask,1"; + if (STARTS_WITH_CI(pszBand, "mask,")) + { + bMask = true; + pszBand += 5; + /* If we use the source mask band as a regular band */ + /* don't create a target mask band by default */ + if (!psOptions->bParsedMaskArgument) + psOptions->eMaskMode = MASK_DISABLED; + } + const int nBand = atoi(pszBand); + if (nBand < 1) + { + throw std::invalid_argument(CPLSPrintf( + "Unrecognizable band number (%s).", s.c_str())); + } + + psOptions->nBandCount++; + psOptions->anBandList.emplace_back(nBand * (bMask ? -1 : 1)); + }) + .help(_("Select input band(s)")); + + argParser->add_argument("-mask") + .metavar("") + .action( + [psOptions](const std::string &s) + { + psOptions->bParsedMaskArgument = true; + const char *pszBand = s.c_str(); + if (EQUAL(pszBand, "none")) + { + psOptions->eMaskMode = MASK_DISABLED; + } + else if (EQUAL(pszBand, "auto")) + { + psOptions->eMaskMode = MASK_AUTO; + } + else + { + bool bMask = false; + + if (EQUAL(pszBand, "mask")) + pszBand = "mask,1"; + if (STARTS_WITH_CI(pszBand, "mask,")) + { + bMask = true; + pszBand += 5; + } + const int nBand = atoi(pszBand); + if (nBand < 1) + { + throw std::invalid_argument(CPLSPrintf( + "Unrecognizable band number (%s).", s.c_str())); + } + + psOptions->eMaskMode = MASK_USER; + psOptions->nMaskBand = nBand; + if (bMask) + psOptions->nMaskBand *= -1; + } + }) + .help(_("Select an input band to create output dataset mask band")); + + { + auto &group = argParser->add_mutually_exclusive_group(); + group.add_argument("-strict") + .store_into(psOptions->bStrict) + .help(_("Enable strict mode")); + + group.add_argument("-not_strict") + .flag() + .action([psOptions](const std::string &) + { psOptions->bStrict = false; }) + .help(_("Disable strict mode")); + } + + if (psOptionsForBinary) + { + argParser->add_argument("-sds") + .store_into(psOptionsForBinary->bCopySubDatasets) + .help(_("Copy subdatasets")); + } + + argParser->add_argument("-nogcp") + .store_into(psOptions->bNoGCP) + .help(_("Do not copy the GCPs in the source dataset to the output " + "dataset.")); + + argParser->add_argument("-gcp") + .metavar(" []") + .nargs(4, 5) + .append() + .scan<'g', double>() + .help( + _("Add the indicated ground control point to the output dataset.")); + + if (psOptionsForBinary) + { + argParser->add_argument("input_file") + .metavar("") + .store_into(psOptionsForBinary->osSource) + .help(_("Input file.")); + + argParser->add_argument("output_file") + .metavar("") + .store_into(psOptionsForBinary->osDest) + .help(_("Output file.")); + } + + argParser->add_argument("-a_nodata") + .metavar("|none") + .action( + [psOptions](const std::string &s) + { + if (EQUAL(s.c_str(), "none")) + { + psOptions->bUnsetNoData = true; + } + else + { + psOptions->bSetNoData = true; + psOptions->osNoData = s; + } + }) + .help(_("Assign a specified nodata value to output bands.")); + + argParser->add_argument("-a_scale") + .metavar("") + .store_into(psOptions->dfScale) + .help(_("Set band scaling value.")); + + argParser->add_argument("-a_offset") + .metavar("") + .store_into(psOptions->dfOffset) + .help(_("Set band offset value.")); + + argParser->add_argument("-a_ullr") + .metavar(" ") + .nargs(4) + .scan<'g', double>() + .help( + _("Assign/override the georeferenced bounds of the output file.")); + + argParser->add_argument("-a_gt") + .metavar(" ") + .nargs(6) + .scan<'g', double>() + .help(_("Assign/override the geotransform of the output file.")); + + argParser->add_creation_options_argument(psOptions->aosCreateOptions); + + { + auto &group = argParser->add_mutually_exclusive_group(); + group.add_argument("-scale") + .metavar("[ [ ]]") + //.nargs(0, 4) + .append() + .scan<'g', double>() + .help(_("Rescale the input pixels values from the range src_min to " + "src_max to the range dst_min to dst_max.")); + + group.add_argument("-scale_X") + .metavar("[ [ ]]") + //.nargs(0, 4) + .append() + .scan<'g', double>() + .help(_("Rescale the input pixels values for band X.")); + + group.add_argument("-unscale") + .store_into(psOptions->bUnscale) + .help(_("Apply the scale/offset metadata for the bands to convert " + "scaled values to unscaled values.")); + } + + argParser->add_argument("-exponent") + .metavar("") + .scan<'g', double>() + .help(_("Exponent to apply non-linear scaling with a power function")); + + argParser->add_argument("-exponent_X") + .append() + .metavar("") + .scan<'g', double>() + .help(_("Exponent to apply non-linear scaling with a power function, " + "for band X")); + + argParser->add_metadata_item_options_argument( + psOptions->aosMetadataOptions); + + argParser->add_argument("-dmo") + .metavar("") + .append() + .action([psOptions](const std::string &s) + { psOptions->aosDomainMetadataOptions.AddString(s.c_str()); }) + .help(_("Passes a metadata key and value in specified domain to set on " + "the output dataset if possible.")); + + argParser->add_argument("-outsize") + .metavar(" ") + .nargs(2) + .help(_("Set the size of the output file.")); + + argParser->add_argument("-tr") + .metavar(" ") + .nargs(2) + .scan<'g', double>() + .help(_("Set target resolution.")); + + argParser->add_argument("-srcwin") + .metavar(" ") + .nargs(4) + .scan<'g', double>() + .help(_("Selects a subwindow from the source image based on pixel/line " + "location.")); + + argParser->add_argument("-projwin") + .metavar(" ") + .nargs(4) + .scan<'g', double>() + .help(_("Selects a subwindow from the source image based on " + "georeferenced coordinates.")); + + argParser->add_argument("-projwin_srs") + .metavar("") + .store_into(psOptions->osProjSRS) + .help(_("Specifies the SRS in which to interpret the coordinates given " + "with -projwin.")); + + argParser->add_argument("-epo") + .flag() + .action( + [psOptions](const std::string &) + { + psOptions->bErrorOnPartiallyOutside = true; + psOptions->bErrorOnCompletelyOutside = true; + }) + .help(_("Error when Partially Outside.")); + + argParser->add_argument("-eco") + .store_into(psOptions->bErrorOnCompletelyOutside) + .help(_("Error when Completely Outside.")); + + argParser->add_argument("-a_srs") + .metavar("") + .store_into(psOptions->osOutputSRS) + .help(_("Override the projection for the output file.")); + + argParser->add_argument("-a_coord_epoch") + .metavar("") + .store_into(psOptions->dfOutputCoordinateEpoch) + .help(_("Assign a coordinate epoch.")); + + argParser->add_argument("-expand") + .metavar("gray|rgb|rgba") + .action( + [psOptions](const std::string &s) + { + if (EQUAL(s.c_str(), "gray")) + psOptions->nRGBExpand = 1; + else if (EQUAL(s.c_str(), "rgb")) + psOptions->nRGBExpand = 3; + else if (EQUAL(s.c_str(), "rgba")) + psOptions->nRGBExpand = 4; + else + { + throw std::invalid_argument(CPLSPrintf( + "Value %s unsupported. Only gray, rgb or rgba are " + "supported.", + s.c_str())); + } + }) + .help(_("To expose a dataset with 1 band with a color table as a " + "dataset with 3 (RGB) or 4 (RGBA) bands.")); + + { + auto &group = argParser->add_mutually_exclusive_group(); + group.add_argument("-stats") + .flag() + .action( + [psOptions](const std::string &) + { + psOptions->bStats = true; + psOptions->bApproxStats = false; + }) + .help(_("Force (re)computation of statistics.")); + + group.add_argument("-approx_stats") + .flag() + .action( + [psOptions](const std::string &) + { + psOptions->bStats = true; + psOptions->bApproxStats = true; + }) + .help(_("Force (re)computation of approximate statistics.")); + } + + argParser->add_argument("-norat") + .store_into(psOptions->bNoRAT) + .help(_("Do not copy source RAT into destination dataset.")); + + if (psOptionsForBinary) + { + argParser->add_open_options_argument( + psOptionsForBinary->aosOpenOptions); + } + + argParser->add_argument("-r") + .metavar("nearest,bilinear,cubic,cubicspline,lanczos,average,mode") + .store_into(psOptions->osResampling) + .help(_("Resampling algorithm.")); + + argParser->add_argument("-colorinterp") + .metavar("{red|green|blue|alpha|gray|undefined},...") + .action( + [psOptions](const std::string &s) + { + CPLStringList aosList(CSLTokenizeString2(s.c_str(), ",", 0)); + psOptions->anColorInterp.resize(aosList.size()); + for (int j = 0; j < aosList.size(); j++) + { + psOptions->anColorInterp[j] = GetColorInterp(aosList[j]); + } + }) + .help(_("Override the color interpretation of all specified bands.")); + + argParser->add_argument("-colorinterp_X") + .append() + .metavar("{red|green|blue|alpha|gray|undefined}") + .help(_("Override the color interpretation of band X.")); + + argParser->add_argument("-if") + .append() + .metavar("") + .action( + [psOptionsForBinary](const std::string &s) + { + if (psOptionsForBinary) + { + if (GDALGetDriverByName(s.c_str()) == nullptr) + { + CPLError(CE_Warning, CPLE_AppDefined, + "%s is not a recognized driver", s.c_str()); + } + psOptionsForBinary->aosAllowedInputDrivers.AddString( + s.c_str()); + } + }) + .help( + _("Format/driver name(s) to be attempted to open the input file.")); + + argParser->add_argument("-noxmp") + .store_into(psOptions->bNoXMP) + .help(_("Do not copy the XMP metadata into destination dataset.")); + + argParser->add_argument("-ovr") + .metavar("|AUTO|AUTO-|NONE") + .action( + [psOptions](const std::string &s) + { + const char *pszOvLevel = s.c_str(); + if (EQUAL(pszOvLevel, "AUTO")) + psOptions->nOvLevel = OVR_LEVEL_AUTO; + else if (STARTS_WITH_CI(pszOvLevel, "AUTO-")) + psOptions->nOvLevel = + OVR_LEVEL_AUTO - atoi(pszOvLevel + strlen("AUTO-")); + else if (EQUAL(pszOvLevel, "NONE")) + psOptions->nOvLevel = OVR_LEVEL_NONE; + else if (CPLGetValueType(pszOvLevel) == CPL_VALUE_INTEGER) + psOptions->nOvLevel = atoi(pszOvLevel); + else + { + throw std::invalid_argument(CPLSPrintf( + "Invalid value '%s' for -ovr option", pszOvLevel)); + } + }) + .help(_("Specify which overview level of source file must be used")); + + return argParser; +} + +/************************************************************************/ +/* GDALTranslateGetParserUsage() */ +/************************************************************************/ + +std::string GDALTranslateGetParserUsage() +{ + GDALTranslateOptions sOptions; + GDALTranslateOptionsForBinary sOptionsForBinary; + auto argParser = + GDALTranslateOptionsGetParser(&sOptions, &sOptionsForBinary); + return argParser->usage(); +} + /************************************************************************/ /* GDALTranslateOptionsNew() */ /************************************************************************/ @@ -2775,140 +3206,19 @@ GDALTranslateOptions * GDALTranslateOptionsNew(char **papszArgv, GDALTranslateOptionsForBinary *psOptionsForBinary) { - GDALTranslateOptions *psOptions = new GDALTranslateOptions; - - bool bParsedMaskArgument = false; - bool bOutsizeExplicitlySet = false; - bool bGotSourceFilename = false; - bool bGotDestFilename = false; + auto psOptions = std::make_unique(); /* -------------------------------------------------------------------- */ - /* Handle command line arguments. */ + /* Pre-processing for custom syntax that ArgumentParser does not */ + /* support. */ /* -------------------------------------------------------------------- */ + + CPLStringList aosArgv; const int argc = CSLCount(papszArgv); for (int i = 0; i < argc && papszArgv != nullptr && papszArgv[i] != nullptr; i++) { - if (i < argc - 1 && - (EQUAL(papszArgv[i], "-of") || EQUAL(papszArgv[i], "-f"))) - { - ++i; - psOptions->osFormat = papszArgv[i]; - } - - else if (EQUAL(papszArgv[i], "-q") || EQUAL(papszArgv[i], "-quiet")) - { - if (psOptionsForBinary) - psOptionsForBinary->bQuiet = true; - } - - else if (EQUAL(papszArgv[i], "-ot") && papszArgv[i + 1]) - { - for (int iType = 1; iType < GDT_TypeCount; iType++) - { - if (GDALGetDataTypeName(static_cast(iType)) != - nullptr && - EQUAL(GDALGetDataTypeName(static_cast(iType)), - papszArgv[i + 1])) - { - psOptions->eOutputType = static_cast(iType); - } - } - - if (psOptions->eOutputType == GDT_Unknown) - { - CPLError(CE_Failure, CPLE_NotSupported, - "Unknown output pixel type: %s.", papszArgv[i + 1]); - GDALTranslateOptionsFree(psOptions); - return nullptr; - } - i++; - } - else if (EQUAL(papszArgv[i], "-b") && papszArgv[i + 1]) - { - const char *pszBand = papszArgv[i + 1]; - bool bMask = false; - if (EQUAL(pszBand, "mask")) - pszBand = "mask,1"; - if (STARTS_WITH_CI(pszBand, "mask,")) - { - bMask = true; - pszBand += 5; - /* If we use the source mask band as a regular band */ - /* don't create a target mask band by default */ - if (!bParsedMaskArgument) - psOptions->eMaskMode = MASK_DISABLED; - } - const int nBand = atoi(pszBand); - if (nBand < 1) - { - CPLError(CE_Failure, CPLE_NotSupported, - "Unrecognizable band number (%s).", papszArgv[i + 1]); - GDALTranslateOptionsFree(psOptions); - return nullptr; - } - i++; - - psOptions->nBandCount++; - psOptions->anBandList.emplace_back(nBand * (bMask ? -1 : 1)); - } - else if (EQUAL(papszArgv[i], "-mask") && papszArgv[i + 1]) - { - bParsedMaskArgument = true; - const char *pszBand = papszArgv[i + 1]; - if (EQUAL(pszBand, "none")) - { - psOptions->eMaskMode = MASK_DISABLED; - } - else if (EQUAL(pszBand, "auto")) - { - psOptions->eMaskMode = MASK_AUTO; - } - else - { - bool bMask = false; - if (EQUAL(pszBand, "mask")) - pszBand = "mask,1"; - if (STARTS_WITH_CI(pszBand, "mask,")) - { - bMask = true; - pszBand += 5; - } - const int nBand = atoi(pszBand); - if (nBand < 1) - { - CPLError(CE_Failure, CPLE_NotSupported, - "Unrecognizable band number (%s).", - papszArgv[i + 1]); - GDALTranslateOptionsFree(psOptions); - return nullptr; - } - - psOptions->eMaskMode = MASK_USER; - psOptions->nMaskBand = nBand; - if (bMask) - psOptions->nMaskBand *= -1; - } - i++; - } - else if (EQUAL(papszArgv[i], "-not_strict")) - { - psOptions->bStrict = false; - } - else if (EQUAL(papszArgv[i], "-strict")) - { - psOptions->bStrict = true; - } - else if (EQUAL(papszArgv[i], "-sds")) - { - if (psOptionsForBinary) - psOptionsForBinary->bCopySubDatasets = TRUE; - } - else if (EQUAL(papszArgv[i], "-nogcp")) - { - psOptions->bNoGCP = true; - } - else if (i + 4 < argc && EQUAL(papszArgv[i], "-gcp")) + if (i + 4 < argc && EQUAL(papszArgv[i], "-gcp")) { /* -gcp pixel line easting northing [elev] */ psOptions->nGCPCount++; @@ -2941,61 +3251,6 @@ GDALTranslateOptionsNew(char **papszArgv, /* should set id and info? */ } - else if (EQUAL(papszArgv[i], "-a_nodata") && papszArgv[i + 1]) - { - if (EQUAL(papszArgv[i + 1], "none")) - { - psOptions->bUnsetNoData = true; - } - else - { - psOptions->bSetNoData = true; - psOptions->osNoData = papszArgv[i + 1]; - } - i += 1; - } - - else if (EQUAL(papszArgv[i], "-a_scale") && papszArgv[i + 1]) - { - psOptions->bSetScale = true; - psOptions->dfScale = CPLAtofM(papszArgv[i + 1]); - i += 1; - } - - else if (EQUAL(papszArgv[i], "-a_offset") && papszArgv[i + 1]) - { - psOptions->bSetOffset = true; - psOptions->dfOffset = CPLAtofM(papszArgv[i + 1]); - i += 1; - } - - else if (i + 4 < argc && EQUAL(papszArgv[i], "-a_ullr")) - { - psOptions->adfULLR[0] = CPLAtofM(papszArgv[i + 1]); - psOptions->adfULLR[1] = CPLAtofM(papszArgv[i + 2]); - psOptions->adfULLR[2] = CPLAtofM(papszArgv[i + 3]); - psOptions->adfULLR[3] = CPLAtofM(papszArgv[i + 4]); - - i += 4; - } - - else if (i + 6 < argc && EQUAL(papszArgv[i], "-a_gt")) - { - psOptions->adfGT[0] = CPLAtofM(papszArgv[i + 1]); - psOptions->adfGT[1] = CPLAtofM(papszArgv[i + 2]); - psOptions->adfGT[2] = CPLAtofM(papszArgv[i + 3]); - psOptions->adfGT[3] = CPLAtofM(papszArgv[i + 4]); - psOptions->adfGT[4] = CPLAtofM(papszArgv[i + 5]); - psOptions->adfGT[5] = CPLAtofM(papszArgv[i + 6]); - - i += 6; - } - - else if (EQUAL(papszArgv[i], "-co") && papszArgv[i + 1]) - { - psOptions->aosCreateOptions.AddString(papszArgv[++i]); - } - else if (EQUAL(papszArgv[i], "-scale") || STARTS_WITH_CI(papszArgv[i], "-scale_")) { @@ -3007,7 +3262,6 @@ GDALTranslateOptionsNew(char **papszArgv, { CPLError(CE_Failure, CPLE_NotSupported, "Cannot mix -scale and -scale_XX syntax"); - GDALTranslateOptionsFree(psOptions); return nullptr; } psOptions->bHasUsedExplicitScaleBand = true; @@ -3016,7 +3270,6 @@ GDALTranslateOptionsNew(char **papszArgv, { CPLError(CE_Failure, CPLE_NotSupported, "Invalid parameter name: %s", papszArgv[i]); - GDALTranslateOptionsFree(psOptions); return nullptr; } nIndex--; @@ -3027,7 +3280,6 @@ GDALTranslateOptionsNew(char **papszArgv, { CPLError(CE_Failure, CPLE_NotSupported, "Cannot mix -scale and -scale_XX syntax"); - GDALTranslateOptionsFree(psOptions); return nullptr; } nIndex = static_cast(psOptions->asScaleParams.size()); @@ -3077,7 +3329,6 @@ GDALTranslateOptionsNew(char **papszArgv, { CPLError(CE_Failure, CPLE_NotSupported, "Cannot mix -exponent and -exponent_XX syntax"); - GDALTranslateOptionsFree(psOptions); return nullptr; } psOptions->bHasUsedExplicitExponentBand = true; @@ -3086,7 +3337,6 @@ GDALTranslateOptionsNew(char **papszArgv, { CPLError(CE_Failure, CPLE_NotSupported, "Invalid parameter name: %s", papszArgv[i]); - GDALTranslateOptionsFree(psOptions); return nullptr; } nIndex--; @@ -3097,7 +3347,6 @@ GDALTranslateOptionsNew(char **papszArgv, { CPLError(CE_Failure, CPLE_NotSupported, "Cannot mix -exponent and -exponent_XX syntax"); - GDALTranslateOptionsFree(psOptions); return nullptr; } nIndex = static_cast(psOptions->adfExponent.size()); @@ -3111,155 +3360,6 @@ GDALTranslateOptionsNew(char **papszArgv, psOptions->adfExponent[nIndex] = dfExponent; } - else if (EQUAL(papszArgv[i], "-unscale")) - { - psOptions->bUnscale = true; - } - - else if (EQUAL(papszArgv[i], "-mo") && papszArgv[i + 1]) - { - psOptions->aosMetadataOptions.AddString(papszArgv[++i]); - } - - else if (EQUAL(papszArgv[i], "-dmo") && papszArgv[i + 1]) - { - psOptions->aosDomainMetadataOptions.AddString(papszArgv[++i]); - } - else if (i + 2 < argc && EQUAL(papszArgv[i], "-outsize") && - papszArgv[i + 1] != nullptr) - { - ++i; - if (papszArgv[i][0] != '\0' && - papszArgv[i][strlen(papszArgv[i]) - 1] == '%') - psOptions->dfOXSizePct = CPLAtofM(papszArgv[i]); - else - psOptions->nOXSizePixel = atoi(papszArgv[i]); - ++i; - if (papszArgv[i][0] != '\0' && - papszArgv[i][strlen(papszArgv[i]) - 1] == '%') - psOptions->dfOYSizePct = CPLAtofM(papszArgv[i]); - else - psOptions->nOYSizePixel = atoi(papszArgv[i]); - bOutsizeExplicitlySet = true; - } - - else if (i + 2 < argc && EQUAL(papszArgv[i], "-tr")) - { - psOptions->dfXRes = CPLAtofM(papszArgv[++i]); - psOptions->dfYRes = fabs(CPLAtofM(papszArgv[++i])); - if (psOptions->dfXRes == 0 || psOptions->dfYRes == 0) - { - CPLError(CE_Failure, CPLE_IllegalArg, - "Wrong value for -tr parameters."); - GDALTranslateOptionsFree(psOptions); - return nullptr; - } - } - - else if (i + 4 < argc && EQUAL(papszArgv[i], "-srcwin")) - { - psOptions->adfSrcWin[0] = CPLAtof(papszArgv[++i]); - psOptions->adfSrcWin[1] = CPLAtof(papszArgv[++i]); - psOptions->adfSrcWin[2] = CPLAtof(papszArgv[++i]); - psOptions->adfSrcWin[3] = CPLAtof(papszArgv[++i]); - } - - else if (i + 4 < argc && EQUAL(papszArgv[i], "-projwin")) - { - psOptions->dfULX = CPLAtofM(papszArgv[++i]); - psOptions->dfULY = CPLAtofM(papszArgv[++i]); - psOptions->dfLRX = CPLAtofM(papszArgv[++i]); - psOptions->dfLRY = CPLAtofM(papszArgv[++i]); - } - - else if (i + 1 < argc && EQUAL(papszArgv[i], "-projwin_srs")) - { - psOptions->osProjSRS = papszArgv[i + 1]; - i++; - } - - else if (EQUAL(papszArgv[i], "-epo")) - { - psOptions->bErrorOnPartiallyOutside = true; - psOptions->bErrorOnCompletelyOutside = true; - } - - else if (EQUAL(papszArgv[i], "-eco")) - { - psOptions->bErrorOnCompletelyOutside = true; - } - - else if (i + 1 < argc && EQUAL(papszArgv[i], "-a_srs")) - { - psOptions->osOutputSRS = papszArgv[i + 1]; - i++; - } - - else if (i + 1 < argc && EQUAL(papszArgv[i], "-a_coord_epoch")) - { - psOptions->dfOutputCoordinateEpoch = CPLAtofM(papszArgv[i + 1]); - i++; - } - - else if (i + 1 < argc && EQUAL(papszArgv[i], "-expand") && - papszArgv[i + 1] != nullptr) - { - i++; - if (EQUAL(papszArgv[i], "gray")) - psOptions->nRGBExpand = 1; - else if (EQUAL(papszArgv[i], "rgb")) - psOptions->nRGBExpand = 3; - else if (EQUAL(papszArgv[i], "rgba")) - psOptions->nRGBExpand = 4; - else - { - CPLError(CE_Failure, CPLE_IllegalArg, - "Value %s unsupported. Only gray, rgb or rgba are " - "supported.", - papszArgv[i]); - GDALTranslateOptionsFree(psOptions); - return nullptr; - } - } - - else if (EQUAL(papszArgv[i], "-stats")) - { - psOptions->bStats = true; - psOptions->bApproxStats = false; - } - else if (EQUAL(papszArgv[i], "-approx_stats")) - { - psOptions->bStats = true; - psOptions->bApproxStats = true; - } - else if (EQUAL(papszArgv[i], "-norat")) - { - psOptions->bNoRAT = true; - } - else if (i + 1 < argc && EQUAL(papszArgv[i], "-oo")) - { - i++; - if (psOptionsForBinary) - { - psOptionsForBinary->aosOpenOptions.AddString(papszArgv[i]); - } - } - else if (i + 1 < argc && EQUAL(papszArgv[i], "-r")) - { - psOptions->osResampling = papszArgv[++i]; - } - - else if (EQUAL(papszArgv[i], "-colorinterp") && papszArgv[i + 1]) - { - ++i; - CPLStringList aosList(CSLTokenizeString2(papszArgv[i], ",", 0)); - psOptions->anColorInterp.resize(aosList.size()); - for (int j = 0; j < aosList.size(); j++) - { - psOptions->anColorInterp[j] = GetColorInterp(aosList[j]); - } - } - else if (STARTS_WITH_CI(papszArgv[i], "-colorinterp_") && papszArgv[i + 1]) { @@ -3268,7 +3368,6 @@ GDALTranslateOptionsNew(char **papszArgv, { CPLError(CE_Failure, CPLE_NotSupported, "Invalid parameter name: %s", papszArgv[i]); - GDALTranslateOptionsFree(psOptions); return nullptr; } nIndex--; @@ -3288,80 +3387,88 @@ GDALTranslateOptionsNew(char **papszArgv, i++; } - else if (i + 1 < argc && EQUAL(papszArgv[i], "-if")) + else { - i++; - if (psOptionsForBinary) - { - if (GDALGetDriverByName(papszArgv[i]) == nullptr) - { - CPLError(CE_Warning, CPLE_AppDefined, - "%s is not a recognized driver", papszArgv[i]); - } - psOptionsForBinary->aosAllowedInputDrivers.AddString( - papszArgv[i]); - } + aosArgv.AddString(papszArgv[i]); } + } - else if (EQUAL(papszArgv[i], "-noxmp")) - { - psOptions->bNoXMP = true; - } + auto argParser = + GDALTranslateOptionsGetParser(psOptions.get(), psOptionsForBinary); - else if (EQUAL(papszArgv[i], "-ovr") && i + 1 < argc) - { - const char *pszOvLevel = papszArgv[++i]; - if (EQUAL(pszOvLevel, "AUTO")) - psOptions->nOvLevel = OVR_LEVEL_AUTO; - else if (STARTS_WITH_CI(pszOvLevel, "AUTO-")) - psOptions->nOvLevel = - OVR_LEVEL_AUTO - atoi(pszOvLevel + strlen("AUTO-")); - else if (EQUAL(pszOvLevel, "NONE")) - psOptions->nOvLevel = OVR_LEVEL_NONE; - else if (CPLGetValueType(pszOvLevel) == CPL_VALUE_INTEGER) - psOptions->nOvLevel = atoi(pszOvLevel); - else - { - CPLError(CE_Failure, CPLE_IllegalArg, - "Invalid value '%s' for -ovr option", pszOvLevel); - GDALTranslateOptionsFree(psOptions); - return nullptr; - } - } + try + { + argParser->parse_args_without_binary_name(aosArgv.List()); + } + catch (const std::exception &err) + { + CPLError(CE_Failure, CPLE_AppDefined, "%s", err.what()); + return nullptr; + } - else if (papszArgv[i][0] == '-') - { - CPLError(CE_Failure, CPLE_NotSupported, "Unknown option name '%s'", - papszArgv[i]); - GDALTranslateOptionsFree(psOptions); - return nullptr; - } - else if (!bGotSourceFilename) - { - bGotSourceFilename = true; - if (psOptionsForBinary) - psOptionsForBinary->osSource = papszArgv[i]; - } - else if (!bGotDestFilename) - { - bGotDestFilename = true; - if (psOptionsForBinary) - psOptionsForBinary->osDest = papszArgv[i]; - } + psOptions->bSetScale = argParser->is_used("-a_scale"); + psOptions->bSetOffset = argParser->is_used("-a_offset"); + + if (auto adfULLR = argParser->present>("-a_ullr")) + { + CPLAssert(psOptions->adfULLR.size() == adfULLR->size()); + for (size_t i = 0; i < adfULLR->size(); ++i) + psOptions->adfULLR[i] = (*adfULLR)[i]; + } + + if (auto adfGT = argParser->present>("-a_gt")) + { + CPLAssert(psOptions->adfGT.size() == adfGT->size()); + for (size_t i = 0; i < adfGT->size(); ++i) + psOptions->adfGT[i] = (*adfGT)[i]; + } + + bool bOutsizeExplicitlySet = false; + if (auto aosOutSize = + argParser->present>("-outsize")) + { + if ((*aosOutSize)[0].back() == '%') + psOptions->dfOXSizePct = CPLAtofM((*aosOutSize)[0].c_str()); + else + psOptions->nOXSizePixel = atoi((*aosOutSize)[0].c_str()); + + if ((*aosOutSize)[1].back() == '%') + psOptions->dfOYSizePct = CPLAtofM((*aosOutSize)[1].c_str()); else + psOptions->nOYSizePixel = atoi((*aosOutSize)[1].c_str()); + bOutsizeExplicitlySet = true; + } + + if (auto adfTargetRes = argParser->present>("-tr")) + { + psOptions->dfXRes = (*adfTargetRes)[0]; + psOptions->dfYRes = fabs((*adfTargetRes)[1]); + if (psOptions->dfXRes == 0 || psOptions->dfYRes == 0) { - CPLError(CE_Failure, CPLE_NotSupported, - "Too many command options '%s'", papszArgv[i]); - GDALTranslateOptionsFree(psOptions); + CPLError(CE_Failure, CPLE_IllegalArg, + "Wrong value for -tr parameters."); return nullptr; } } + if (auto adfSrcWin = argParser->present>("-srcwin")) + { + for (int i = 0; i < 4; ++i) + psOptions->adfSrcWin[i] = (*adfSrcWin)[i]; + } + + if (auto adfProjWin = argParser->present>("-projwin")) + { + psOptions->dfULX = (*adfProjWin)[0]; + psOptions->dfULY = (*adfProjWin)[1]; + psOptions->dfLRX = (*adfProjWin)[2]; + psOptions->dfLRY = (*adfProjWin)[3]; + } + if (psOptions->nGCPCount > 0 && psOptions->bNoGCP) { CPLError(CE_Failure, CPLE_IllegalArg, "-nogcp and -gcp cannot be used as the same time"); - GDALTranslateOptionsFree(psOptions); return nullptr; } @@ -3371,7 +3478,6 @@ GDALTranslateOptionsNew(char **papszArgv, { CPLError(CE_Failure, CPLE_NotSupported, "-outsize %d %d invalid.", psOptions->nOXSizePixel, psOptions->nOYSizePixel); - GDALTranslateOptionsFree(psOptions); return nullptr; } @@ -3379,7 +3485,6 @@ GDALTranslateOptionsNew(char **papszArgv, { CPLError(CE_Failure, CPLE_IllegalArg, "-scale and -unscale cannot be used as the same time"); - GDALTranslateOptionsFree(psOptions); return nullptr; } @@ -3389,7 +3494,7 @@ GDALTranslateOptionsNew(char **papszArgv, psOptionsForBinary->osFormat = psOptions->osFormat; } - return psOptions; + return psOptions.release(); } /************************************************************************/ diff --git a/apps/gdal_utils_priv.h b/apps/gdal_utils_priv.h index 235be74bdf98..05f4c1fe8a53 100644 --- a/apps/gdal_utils_priv.h +++ b/apps/gdal_utils_priv.h @@ -234,6 +234,8 @@ std::string CPL_DLL GDALNearblackGetParserUsage(); std::string CPL_DLL GDALVectorInfoGetParserUsage(); +std::string CPL_DLL GDALTranslateGetParserUsage(); + #endif /* #ifndef DOXYGEN_SKIP */ #endif /* GDAL_UTILS_PRIV_H_INCLUDED */ diff --git a/autotest/utilities/test_gdal_translate.py b/autotest/utilities/test_gdal_translate.py index fe18534c634d..01e574fdfbeb 100755 --- a/autotest/utilities/test_gdal_translate.py +++ b/autotest/utilities/test_gdal_translate.py @@ -968,12 +968,12 @@ def test_gdal_translate_34(gdal_translate_path, tmp_path): def test_gdal_translate_35(gdal_translate_path, tmp_vsimem): (_, err) = gdaltest.runexternal_out_and_err(gdal_translate_path) - assert "No source dataset specified" in err + assert "input_file: 1 argument(s) expected. 0 provided." in err (_, err) = gdaltest.runexternal_out_and_err( gdal_translate_path + " ../gcore/data/byte.tif" ) - assert "No target dataset specified" in err + assert "output_file: 1 argument(s) expected. 0 provided." in err (_, err) = gdaltest.runexternal_out_and_err( f"{gdal_translate_path} /non_existing_path/non_existing.tif {tmp_vsimem}/out.tif"