diff --git a/apps/gdal_utils_priv.h b/apps/gdal_utils_priv.h index f7a310794533..28784130975c 100644 --- a/apps/gdal_utils_priv.h +++ b/apps/gdal_utils_priv.h @@ -65,15 +65,6 @@ struct GDALDEMProcessingOptionsForBinary int bQuiet; }; -struct GDALBuildVRTOptionsForBinary -{ - int nSrcFiles; - char **papszSrcFiles; - char *pszDstFilename; - int bQuiet; - int bOverwrite; -}; - CPL_C_END /* Access modes */ @@ -228,6 +219,14 @@ struct GDALWarpAppOptionsForBinary CPLStringList aosAllowedInputDrivers{}; }; +struct GDALBuildVRTOptionsForBinary +{ + CPLStringList aosSrcFiles{}; + std::string osDstFilename{}; + bool bQuiet = false; + bool bOverwrite = false; +}; + std::string CPL_DLL GDALNearblackGetParserUsage(); std::string CPL_DLL GDALVectorInfoGetParserUsage(); @@ -242,6 +241,8 @@ std::string CPL_DLL GDALInfoAppGetParserUsage(); std::string CPL_DLL GDALGridGetParserUsage(); +std::string CPL_DLL GDALBuildVRTGetParserUsage(); + #endif /* #ifndef DOXYGEN_SKIP */ #endif /* GDAL_UTILS_PRIV_H_INCLUDED */ diff --git a/apps/gdalbuildvrt_bin.cpp b/apps/gdalbuildvrt_bin.cpp index e3ba4d1125ee..d11ddf0cdec5 100644 --- a/apps/gdalbuildvrt_bin.cpp +++ b/apps/gdalbuildvrt_bin.cpp @@ -37,97 +37,13 @@ /* Usage() */ /************************************************************************/ -static void Usage(bool bIsError, - const char *pszErrorMsg = nullptr) CPL_NO_RETURN; +static void Usage() CPL_NO_RETURN; -static void Usage(bool bIsError, const char *pszErrorMsg) +static void Usage() { - fprintf( - bIsError ? stderr : stdout, "%s", - "Usage: gdalbuildvrt [--help] [--help-general]\n" - " [-tileindex ]\n" - " [-resolution {highest|lowest|average|user}]\n" - " [-te ] [-tr " - "] [-tap]\n" - " [-separate] [-b ]... [-sd ]\n" - " [-allow_projection_difference] [-q]\n" - " [-addalpha] [-hidenodata]\n" - " [-srcnodata \"[ ]...\"] [-vrtnodata " - "\"[ ]...\"\n" - " [-ignore_srcmaskband]\n" - " [-nodata_max_mask_threshold ]\n" - " [-a_srs ]\n" - " [-r " - "{nearest|bilinear|cubic|cubicspline|lanczos|average|mode}]\n" - " [-oo =]...\n" - " [-input_file_list ] [-overwrite]\n" - " [-strict | -non_strict]\n" - " " - "[]...\n" - "\n" - "e.g.\n" - " % gdalbuildvrt doq_index.vrt doq/*.tif\n" - " % gdalbuildvrt -input_file_list my_list.txt doq_index.vrt\n" - "\n" - "NOTES:\n" - " o With -separate, each files goes into a separate band in the VRT " - "band.\n" - " Otherwise, the files are considered as tiles of a larger mosaic.\n" - " o -b option selects a band to add into vrt. Multiple bands can be " - "listed.\n" - " By default all bands are queried.\n" - " o The default tile index field is 'location' unless otherwise " - "specified by\n" - " -tileindex.\n" - " o In case the resolution of all input files is not the same, the " - "-resolution\n" - " flag enable the user to control the way the output resolution is " - "computed.\n" - " Average is the default.\n" - " o Input files may be any valid GDAL dataset or a GDAL raster tile " - "index.\n" - " o For a GDAL raster tile index, all entries will be added to the " - "VRT.\n" - " o If one GDAL dataset is made of several subdatasets and has 0 " - "raster bands,\n" - " its datasets will be added to the VRT rather than the dataset " - "itself.\n" - " Single subdataset could be selected by its number using the -sd " - "option.\n" - " o By default, only datasets of same projection and band " - "characteristics\n" - " may be added to the VRT.\n"); - - if (pszErrorMsg != nullptr) - fprintf(stderr, "\nFAILURE: %s\n", pszErrorMsg); - - exit(bIsError ? 1 : 0); -} - -/************************************************************************/ -/* GDALBuildVRTOptionsForBinaryNew() */ -/************************************************************************/ - -static GDALBuildVRTOptionsForBinary *GDALBuildVRTOptionsForBinaryNew(void) -{ - return static_cast( - CPLCalloc(1, sizeof(GDALBuildVRTOptionsForBinary))); -} - -/************************************************************************/ -/* GDALBuildVRTOptionsForBinaryFree() */ -/************************************************************************/ - -static void GDALBuildVRTOptionsForBinaryFree( - GDALBuildVRTOptionsForBinary *psOptionsForBinary) -{ - if (psOptionsForBinary) - { - CSLDestroy(psOptionsForBinary->papszSrcFiles); - CPLFree(psOptionsForBinary->pszDstFilename); - CPLFree(psOptionsForBinary); - } + fprintf(stderr, "%s\n", GDALBuildVRTGetParserUsage().c_str()); + exit(1); } /************************************************************************/ @@ -148,40 +64,18 @@ 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); - } - } - - GDALBuildVRTOptionsForBinary *psOptionsForBinary = - GDALBuildVRTOptionsForBinaryNew(); + GDALBuildVRTOptionsForBinary sOptionsForBinary; /* coverity[tainted_data] */ GDALBuildVRTOptions *psOptions = - GDALBuildVRTOptionsNew(argv + 1, psOptionsForBinary); + GDALBuildVRTOptionsNew(argv + 1, &sOptionsForBinary); CSLDestroy(argv); if (psOptions == nullptr) { - Usage(true, nullptr); - } - - if (psOptionsForBinary->pszDstFilename == nullptr) - { - Usage(true, "No target filename specified."); + Usage(); } - if (!(psOptionsForBinary->bQuiet)) + if (!(sOptionsForBinary.bQuiet)) { GDALBuildVRTOptionsSetProgress(psOptions, GDALTermProgress, nullptr); } @@ -189,18 +83,20 @@ MAIN_START(argc, argv) /* Avoid overwriting a non VRT dataset if the user did not put the */ /* filenames in the right order */ VSIStatBuf sBuf; - if (!psOptionsForBinary->bOverwrite) + if (!sOptionsForBinary.bOverwrite) { - int bExists = (VSIStat(psOptionsForBinary->pszDstFilename, &sBuf) == 0); + int bExists = + (VSIStat(sOptionsForBinary.osDstFilename.c_str(), &sBuf) == 0); if (bExists) { - GDALDriverH hDriver = - GDALIdentifyDriver(psOptionsForBinary->pszDstFilename, nullptr); + GDALDriverH hDriver = GDALIdentifyDriver( + sOptionsForBinary.osDstFilename.c_str(), nullptr); if (hDriver && !(EQUAL(GDALGetDriverShortName(hDriver), "VRT") || (EQUAL(GDALGetDriverShortName(hDriver), "API_PROXY") && - EQUAL(CPLGetExtension(psOptionsForBinary->pszDstFilename), - "VRT")))) + EQUAL( + CPLGetExtension(sOptionsForBinary.osDstFilename.c_str()), + "VRT")))) { fprintf( stderr, @@ -209,24 +105,24 @@ MAIN_START(argc, argv) "right order.\n" "If you want to overwrite %s, add -overwrite option to the " "command line.\n\n", - psOptionsForBinary->pszDstFilename, + sOptionsForBinary.osDstFilename.c_str(), GDALGetDriverShortName(hDriver), - psOptionsForBinary->pszDstFilename); - Usage(true); + sOptionsForBinary.osDstFilename.c_str()); + Usage(); } } } int bUsageError = FALSE; GDALDatasetH hOutDS = GDALBuildVRT( - psOptionsForBinary->pszDstFilename, psOptionsForBinary->nSrcFiles, - nullptr, psOptionsForBinary->papszSrcFiles, psOptions, &bUsageError); + sOptionsForBinary.osDstFilename.c_str(), + sOptionsForBinary.aosSrcFiles.size(), nullptr, + sOptionsForBinary.aosSrcFiles.List(), psOptions, &bUsageError); if (bUsageError) - Usage(true); + Usage(); int nRetCode = (hOutDS) ? 0 : 1; GDALBuildVRTOptionsFree(psOptions); - GDALBuildVRTOptionsForBinaryFree(psOptionsForBinary); CPLErrorReset(); // The flush to disk is only done at that stage, so check if any error has diff --git a/apps/gdalbuildvrt_lib.cpp b/apps/gdalbuildvrt_lib.cpp index b07f416cf5a9..ae642786f383 100644 --- a/apps/gdalbuildvrt_lib.cpp +++ b/apps/gdalbuildvrt_lib.cpp @@ -33,6 +33,7 @@ #include "cpl_port.h" #include "gdal_utils.h" #include "gdal_utils_priv.h" +#include "gdalargumentparser.h" #include #include @@ -52,6 +53,7 @@ #include "cpl_progress.h" #include "cpl_string.h" #include "cpl_vsi.h" +#include "cpl_vsi_virtual.h" #include "gdal.h" #include "gdal_vrt.h" #include "gdal_priv.h" @@ -1662,12 +1664,9 @@ GDALDataset *VRTBuilder::Build(GDALProgressFunc pfnProgress, /************************************************************************/ static bool add_file_to_list(const char *filename, const char *tile_index, - int *pnInputFiles, char ***pppszInputFilenames) + CPLStringList &aosList) { - int nInputFiles = *pnInputFiles; - char **ppszInputFilenames = *pppszInputFilenames; - if (EQUAL(CPLGetExtension(filename), "SHP")) { /* Handle gdaltindex Shapefile as a special case */ @@ -1713,27 +1712,16 @@ static bool add_file_to_list(const char *filename, const char *tile_index, return false; } - ppszInputFilenames = static_cast(CPLRealloc( - ppszInputFilenames, - sizeof(char *) * - (nInputFiles + static_cast(nTileIndexFiles) + 1))); for (auto &&poFeature : poLayer) { - ppszInputFilenames[nInputFiles++] = - CPLStrdup(poFeature->GetFieldAsString(ti_field)); + aosList.AddString(poFeature->GetFieldAsString(ti_field)); } - ppszInputFilenames[nInputFiles] = nullptr; } else { - ppszInputFilenames = static_cast(CPLRealloc( - ppszInputFilenames, sizeof(char *) * (nInputFiles + 1 + 1))); - ppszInputFilenames[nInputFiles++] = CPLStrdup(filename); - ppszInputFilenames[nInputFiles] = nullptr; + aosList.AddString(filename); } - *pnInputFiles = nInputFiles; - *pppszInputFilenames = ppszInputFilenames; return true; } @@ -1747,74 +1735,41 @@ static bool add_file_to_list(const char *filename, const char *tile_index, */ struct GDALBuildVRTOptions { - bool bStrict; - char *pszResolution; - int bSeparate; - int bAllowProjectionDifference; - double we_res; - double ns_res; - int bTargetAlignedPixels; - double xmin; - double ymin; - double xmax; - double ymax; - int bAddAlpha; - int bHideNoData; - int nSubdataset; - char *pszSrcNoData; - char *pszVRTNoData; - char *pszOutputSRS; - int *panSelectedBandList; - int nBandCount; - char *pszResampling; - char **papszOpenOptions; - bool bUseSrcMaskBand; - bool bNoDataFromMask; - double dfMaskValueThreshold; + std::string osTileIndex = "location"; + bool bStrict = false; + std::string osResolution{}; + bool bSeparate = false; + bool bAllowProjectionDifference = false; + double we_res = 0; + double ns_res = 0; + bool bTargetAlignedPixels = false; + double xmin = 0; + double ymin = 0; + double xmax = 0; + double ymax = 0; + bool bAddAlpha = false; + bool bHideNoData = false; + int nSubdataset = -1; + std::string osSrcNoData{}; + std::string osVRTNoData{}; + std::string osOutputSRS{}; + std::vector anSelectedBandList{}; + std::string osResampling{}; + CPLStringList aosOpenOptions{}; + bool bUseSrcMaskBand = true; + bool bNoDataFromMask = false; + double dfMaskValueThreshold = 0; /*! allow or suppress progress monitor and other non-error output */ - int bQuiet; + bool bQuiet = true; /*! the progress function to use */ - GDALProgressFunc pfnProgress; + GDALProgressFunc pfnProgress = GDALDummyProgress; /*! pointer to the progress data variable */ - void *pProgressData; + void *pProgressData = nullptr; }; -/************************************************************************/ -/* GDALBuildVRTOptionsClone() */ -/************************************************************************/ - -static GDALBuildVRTOptions * -GDALBuildVRTOptionsClone(const GDALBuildVRTOptions *psOptionsIn) -{ - GDALBuildVRTOptions *psOptions = static_cast( - CPLMalloc(sizeof(GDALBuildVRTOptions))); - memcpy(psOptions, psOptionsIn, sizeof(GDALBuildVRTOptions)); - if (psOptionsIn->pszResolution) - psOptions->pszResolution = CPLStrdup(psOptionsIn->pszResolution); - if (psOptionsIn->pszSrcNoData) - psOptions->pszSrcNoData = CPLStrdup(psOptionsIn->pszSrcNoData); - if (psOptionsIn->pszVRTNoData) - psOptions->pszVRTNoData = CPLStrdup(psOptionsIn->pszVRTNoData); - if (psOptionsIn->pszOutputSRS) - psOptions->pszOutputSRS = CPLStrdup(psOptionsIn->pszOutputSRS); - if (psOptionsIn->pszResampling) - psOptions->pszResampling = CPLStrdup(psOptionsIn->pszResampling); - if (psOptionsIn->panSelectedBandList) - { - psOptions->panSelectedBandList = static_cast( - CPLMalloc(sizeof(int) * psOptionsIn->nBandCount)); - memcpy(psOptions->panSelectedBandList, psOptionsIn->panSelectedBandList, - sizeof(int) * psOptionsIn->nBandCount); - } - if (psOptionsIn->papszOpenOptions) - psOptions->papszOpenOptions = - CSLDuplicate(psOptionsIn->papszOpenOptions); - return psOptions; -} - /************************************************************************/ /* GDALBuildVRT() */ /************************************************************************/ @@ -1872,93 +1827,89 @@ GDALDatasetH GDALBuildVRT(const char *pszDest, int nSrcCount, return nullptr; } - GDALBuildVRTOptions *psOptions = - (psOptionsIn) ? GDALBuildVRTOptionsClone(psOptionsIn) - : GDALBuildVRTOptionsNew(nullptr, nullptr); + // cppcheck-suppress unreadVariable + GDALBuildVRTOptions sOptions(psOptionsIn ? *psOptionsIn + : GDALBuildVRTOptions()); - if (psOptions->we_res != 0 && psOptions->ns_res != 0 && - psOptions->pszResolution != nullptr && - !EQUAL(psOptions->pszResolution, "user")) + if (sOptions.we_res != 0 && sOptions.ns_res != 0 && + !sOptions.osResolution.empty() && + !EQUAL(sOptions.osResolution.c_str(), "user")) { CPLError(CE_Failure, CPLE_NotSupported, "-tr option is not compatible with -resolution %s", - psOptions->pszResolution); + sOptions.osResolution.c_str()); if (pbUsageError) *pbUsageError = TRUE; - GDALBuildVRTOptionsFree(psOptions); return nullptr; } - if (psOptions->bTargetAlignedPixels && psOptions->we_res == 0 && - psOptions->ns_res == 0) + if (sOptions.bTargetAlignedPixels && sOptions.we_res == 0 && + sOptions.ns_res == 0) { CPLError(CE_Failure, CPLE_NotSupported, "-tap option cannot be used without using -tr"); if (pbUsageError) *pbUsageError = TRUE; - GDALBuildVRTOptionsFree(psOptions); return nullptr; } - if (psOptions->bAddAlpha && psOptions->bSeparate) + if (sOptions.bAddAlpha && sOptions.bSeparate) { CPLError(CE_Failure, CPLE_NotSupported, "-addalpha option is not compatible with -separate."); if (pbUsageError) *pbUsageError = TRUE; - GDALBuildVRTOptionsFree(psOptions); return nullptr; } ResolutionStrategy eStrategy = AVERAGE_RESOLUTION; - if (psOptions->pszResolution == nullptr || - EQUAL(psOptions->pszResolution, "user")) + if (sOptions.osResolution.empty() || + EQUAL(sOptions.osResolution.c_str(), "user")) { - if (psOptions->we_res != 0 || psOptions->ns_res != 0) + if (sOptions.we_res != 0 || sOptions.ns_res != 0) eStrategy = USER_RESOLUTION; - else if (psOptions->pszResolution != nullptr && - EQUAL(psOptions->pszResolution, "user")) + else if (EQUAL(sOptions.osResolution.c_str(), "user")) { CPLError(CE_Failure, CPLE_NotSupported, "-tr option must be used with -resolution user."); if (pbUsageError) *pbUsageError = TRUE; - GDALBuildVRTOptionsFree(psOptions); return nullptr; } } - else if (EQUAL(psOptions->pszResolution, "average")) + else if (EQUAL(sOptions.osResolution.c_str(), "average")) eStrategy = AVERAGE_RESOLUTION; - else if (EQUAL(psOptions->pszResolution, "highest")) + else if (EQUAL(sOptions.osResolution.c_str(), "highest")) eStrategy = HIGHEST_RESOLUTION; - else if (EQUAL(psOptions->pszResolution, "lowest")) + else if (EQUAL(sOptions.osResolution.c_str(), "lowest")) eStrategy = LOWEST_RESOLUTION; /* If -srcnodata is specified, use it as the -vrtnodata if the latter is not */ /* specified */ - if (psOptions->pszSrcNoData != nullptr && - psOptions->pszVRTNoData == nullptr) - psOptions->pszVRTNoData = CPLStrdup(psOptions->pszSrcNoData); + if (!sOptions.osSrcNoData.empty() && sOptions.osVRTNoData.empty()) + sOptions.osVRTNoData = sOptions.osSrcNoData; VRTBuilder oBuilder( - psOptions->bStrict, pszDest, nSrcCount, papszSrcDSNames, pahSrcDS, - psOptions->panSelectedBandList, psOptions->nBandCount, eStrategy, - psOptions->we_res, psOptions->ns_res, psOptions->bTargetAlignedPixels, - psOptions->xmin, psOptions->ymin, psOptions->xmax, psOptions->ymax, - psOptions->bSeparate, psOptions->bAllowProjectionDifference, - psOptions->bAddAlpha, psOptions->bHideNoData, psOptions->nSubdataset, - psOptions->pszSrcNoData, psOptions->pszVRTNoData, - psOptions->bUseSrcMaskBand, psOptions->bNoDataFromMask, - psOptions->dfMaskValueThreshold, psOptions->pszOutputSRS, - psOptions->pszResampling, psOptions->papszOpenOptions); - - GDALDatasetH hDstDS = static_cast( - oBuilder.Build(psOptions->pfnProgress, psOptions->pProgressData)); - - GDALBuildVRTOptionsFree(psOptions); - - return hDstDS; + sOptions.bStrict, pszDest, nSrcCount, papszSrcDSNames, pahSrcDS, + sOptions.anSelectedBandList.empty() + ? nullptr + : sOptions.anSelectedBandList.data(), + static_cast(sOptions.anSelectedBandList.size()), eStrategy, + sOptions.we_res, sOptions.ns_res, sOptions.bTargetAlignedPixels, + sOptions.xmin, sOptions.ymin, sOptions.xmax, sOptions.ymax, + sOptions.bSeparate, sOptions.bAllowProjectionDifference, + sOptions.bAddAlpha, sOptions.bHideNoData, sOptions.nSubdataset, + sOptions.osSrcNoData.empty() ? nullptr : sOptions.osSrcNoData.c_str(), + sOptions.osVRTNoData.empty() ? nullptr : sOptions.osVRTNoData.c_str(), + sOptions.bUseSrcMaskBand, sOptions.bNoDataFromMask, + sOptions.dfMaskValueThreshold, + sOptions.osOutputSRS.empty() ? nullptr : sOptions.osOutputSRS.c_str(), + sOptions.osResampling.empty() ? nullptr : sOptions.osResampling.c_str(), + sOptions.aosOpenOptions.List()); + + return GDALDataset::ToHandle( + oBuilder.Build(sOptions.pfnProgress, sOptions.pProgressData)); } /************************************************************************/ @@ -1987,6 +1938,298 @@ static char *SanitizeSRS(const char *pszUserInput) return pszResult; } +/************************************************************************/ +/* GDALBuildVRTOptionsGetParser() */ +/************************************************************************/ + +static std::unique_ptr +GDALBuildVRTOptionsGetParser(GDALBuildVRTOptions *psOptions, + GDALBuildVRTOptionsForBinary *psOptionsForBinary) +{ + auto argParser = std::make_unique( + "gdalbuildvrt", /* bForBinary=*/psOptionsForBinary != nullptr); + + argParser->add_description(_("Builds a VRT from a list of datasets.")); + + argParser->add_epilog(_( + "\n" + "e.g.\n" + " % gdalbuildvrt doq_index.vrt doq/*.tif\n" + " % gdalbuildvrt -input_file_list my_list.txt doq_index.vrt\n" + "\n" + "NOTES:\n" + " o With -separate, each files goes into a separate band in the VRT " + "band.\n" + " Otherwise, the files are considered as tiles of a larger mosaic.\n" + " o -b option selects a band to add into vrt. Multiple bands can be " + "listed.\n" + " By default all bands are queried.\n" + " o The default tile index field is 'location' unless otherwise " + "specified by\n" + " -tileindex.\n" + " o In case the resolution of all input files is not the same, the " + "-resolution\n" + " flag enable the user to control the way the output resolution is " + "computed.\n" + " Average is the default.\n" + " o Input files may be any valid GDAL dataset or a GDAL raster tile " + "index.\n" + " o For a GDAL raster tile index, all entries will be added to the " + "VRT.\n" + " o If one GDAL dataset is made of several subdatasets and has 0 " + "raster bands,\n" + " its datasets will be added to the VRT rather than the dataset " + "itself.\n" + " Single subdataset could be selected by its number using the -sd " + "option.\n" + " o By default, only datasets of same projection and band " + "characteristics\n" + " may be added to the VRT.\n" + "\n" + "For more details, consult " + "https://gdal.org/programs/gdalbuildvrt.html")); + + argParser->add_quiet_argument( + psOptionsForBinary ? &psOptionsForBinary->bQuiet : nullptr); + + { + auto &group = argParser->add_mutually_exclusive_group(); + + group.add_argument("-strict") + .flag() + .store_into(psOptions->bStrict) + .help(_("Turn warnings as failures.")); + + group.add_argument("-non_strict") + .flag() + .action([psOptions](const std::string &) + { psOptions->bStrict = false; }) + .help(_("Skip source datasets that have issues with warnings, and " + "continue processing.")); + } + + argParser->add_argument("-tile_index") + .metavar("") + .store_into(psOptions->osTileIndex) + .help(_("Use the specified value as the tile index field, instead of " + "the default value which is 'location'.")); + + argParser->add_argument("-resolution") + .metavar("user|average|highest|lowest") + .action( + [psOptions](const std::string &s) + { + psOptions->osResolution = s; + if (!EQUAL(psOptions->osResolution.c_str(), "user") && + !EQUAL(psOptions->osResolution.c_str(), "average") && + !EQUAL(psOptions->osResolution.c_str(), "highest") && + !EQUAL(psOptions->osResolution.c_str(), "lowest")) + { + throw std::invalid_argument( + CPLSPrintf("Illegal resolution value (%s).", + psOptions->osResolution.c_str())); + } + }) + .help(_("Control the way the output resolution is computed.")); + + argParser->add_argument("-tr") + .metavar(" ") + .nargs(2) + .scan<'g', double>() + .help(_("Set target resolution.")); + + if (psOptionsForBinary) + { + argParser->add_argument("-input_file_list") + .metavar("") + .action( + [psOptions, psOptionsForBinary](const std::string &s) + { + const char *input_file_list = s.c_str(); + auto f = VSIVirtualHandleUniquePtr( + VSIFOpenL(input_file_list, "r")); + if (f) + { + while (1) + { + const char *filename = CPLReadLineL(f.get()); + if (filename == nullptr) + break; + if (!add_file_to_list( + filename, psOptions->osTileIndex.c_str(), + psOptionsForBinary->aosSrcFiles)) + { + throw std::invalid_argument( + std::string("Cannot add ") + .append(filename) + .append(" to input file list")); + } + } + } + }) + .help(_("Text file with an input filename on each line")); + } + + argParser->add_argument("-separate") + .flag() + .store_into(psOptions->bSeparate) + .help(_("Place each input file into a separate band.")); + + argParser->add_argument("-allow_projection_difference") + .flag() + .store_into(psOptions->bAllowProjectionDifference) + .help(_("Accept source files not in the same projection (but without " + "reprojecting them!).")); + + argParser->add_argument("-sd") + .metavar("") + .store_into(psOptions->nSubdataset) + .help(_("Use subdataset of specified index (starting at 1), instead of " + "the source dataset itself.")); + + argParser->add_argument("-tap") + .flag() + .store_into(psOptions->bTargetAlignedPixels) + .help(_("Align the coordinates of the extent of the output file to the " + "values of the resolution.")); + + argParser->add_argument("-te") + .metavar(" ") + .nargs(4) + .scan<'g', double>() + .help(_("Set georeferenced extents of output file to be created.")); + + argParser->add_argument("-addalpha") + .flag() + .store_into(psOptions->bAddAlpha) + .help(_("Adds an alpha mask band to the VRT when the source raster " + "have none.")); + + argParser->add_argument("-b") + .metavar("") + .append() + .store_into(psOptions->anSelectedBandList) + .help(_("Specify input band(s) number.")); + + argParser->add_argument("-hidenodata") + .flag() + .store_into(psOptions->bHideNoData) + .help(_("Makes the VRT band not report the NoData.")); + + if (psOptionsForBinary) + { + argParser->add_argument("-overwrite") + .flag() + .store_into(psOptionsForBinary->bOverwrite) + .help(_("Overwrite the VRT if it already exists.")); + } + + argParser->add_argument("-srcnodata") + .metavar("\"[ ]...\"") + .store_into(psOptions->osSrcNoData) + .help(_("Set nodata values for input bands.")); + + argParser->add_argument("-vrtnodata") + .metavar("\"[ ]...\"") + .store_into(psOptions->osVRTNoData) + .help(_("Set nodata values at the VRT band level.")); + + argParser->add_argument("-a_srs") + .metavar("") + .action( + [psOptions](const std::string &s) + { + char *pszSRS = SanitizeSRS(s.c_str()); + if (pszSRS == nullptr) + { + throw std::invalid_argument("Invalid value for -a_srs"); + } + psOptions->osOutputSRS = pszSRS; + CPLFree(pszSRS); + }) + .help(_("Override the projection for the output file..")); + + argParser->add_argument("-r") + .metavar("nearest|bilinear|cubic|cubicspline|lanczos|average|mode") + .store_into(psOptions->osResampling) + .help(_("Resampling algorithm.")); + + argParser->add_open_options_argument(&psOptions->aosOpenOptions); + + argParser->add_argument("-ignore_srcmaskband") + .flag() + .action([psOptions](const std::string &) + { psOptions->bUseSrcMaskBand = false; }) + .help(_("Cause mask band of sources will not be taken into account.")); + + argParser->add_argument("-nodata_max_mask_threshold") + .metavar("") + .scan<'g', double>() + .action( + [psOptions](const std::string &s) + { + psOptions->bNoDataFromMask = true; + psOptions->dfMaskValueThreshold = CPLAtofM(s.c_str()); + }) + .help(_("Replaces the value of the source with the value of -vrtnodata " + "when the value of the mask band of the source is less or " + "equal to the threshold.")); + + if (psOptionsForBinary) + { + if (psOptionsForBinary->osDstFilename.empty()) + { + // We normally go here, unless undocumented -o switch is used + argParser->add_argument("vrt_dataset_name") + .metavar("") + .store_into(psOptionsForBinary->osDstFilename) + .help(_("Output VRT.")); + } + + argParser->add_argument("src_dataset_name") + .metavar("") + .nargs(argparse::nargs_pattern::any) + .action( + [psOptions, psOptionsForBinary](const std::string &s) + { + if (!add_file_to_list(s.c_str(), + psOptions->osTileIndex.c_str(), + psOptionsForBinary->aosSrcFiles)) + { + throw std::invalid_argument( + std::string("Cannot add ") + .append(s) + .append(" to input file list")); + } + }) + .help(_("Input dataset(s).")); + } + + return argParser; +} + +/************************************************************************/ +/* GDALBuildVRTGetParserUsage() */ +/************************************************************************/ + +std::string GDALBuildVRTGetParserUsage() +{ + try + { + GDALBuildVRTOptions sOptions; + GDALBuildVRTOptionsForBinary sOptionsForBinary; + auto argParser = + GDALBuildVRTOptionsGetParser(&sOptions, &sOptionsForBinary); + return argParser->usage(); + } + catch (const std::exception &err) + { + CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s", + err.what()); + return std::string(); + } +} + /************************************************************************/ /* GDALBuildVRTOptionsNew() */ /************************************************************************/ @@ -2011,239 +2254,54 @@ GDALBuildVRTOptions * GDALBuildVRTOptionsNew(char **papszArgv, GDALBuildVRTOptionsForBinary *psOptionsForBinary) { - GDALBuildVRTOptions *psOptions = static_cast( - CPLCalloc(1, sizeof(GDALBuildVRTOptions))); - - const char *tile_index = "location"; - - psOptions->nSubdataset = -1; - psOptions->bQuiet = TRUE; - psOptions->pfnProgress = GDALDummyProgress; - psOptions->pProgressData = nullptr; - psOptions->bUseSrcMaskBand = true; - psOptions->bNoDataFromMask = false; - psOptions->dfMaskValueThreshold = 0; - psOptions->bStrict = false; - - /* -------------------------------------------------------------------- */ - /* Parse arguments. */ - /* -------------------------------------------------------------------- */ - int argc = CSLCount(papszArgv); - for (int iArg = 0; papszArgv != nullptr && iArg < argc; iArg++) + auto psOptions = std::make_unique(); + + CPLStringList aosArgv; + const int nArgc = CSLCount(papszArgv); + for (int i = 0; + i < nArgc && papszArgv != nullptr && papszArgv[i] != nullptr; i++) { - if (strcmp(papszArgv[iArg], "-strict") == 0) - { - psOptions->bStrict = true; - } - else if (strcmp(papszArgv[iArg], "-non_strict") == 0) - { - psOptions->bStrict = false; - } - else if (EQUAL(papszArgv[iArg], "-tileindex") && iArg + 1 < argc) - { - tile_index = papszArgv[++iArg]; - } - else if (EQUAL(papszArgv[iArg], "-resolution") && iArg + 1 < argc) - { - CPLFree(psOptions->pszResolution); - psOptions->pszResolution = CPLStrdup(papszArgv[++iArg]); - if (!EQUAL(psOptions->pszResolution, "user") && - !EQUAL(psOptions->pszResolution, "average") && - !EQUAL(psOptions->pszResolution, "highest") && - !EQUAL(psOptions->pszResolution, "lowest")) - { - CPLError(CE_Failure, CPLE_IllegalArg, - "Illegal resolution value (%s).", - psOptions->pszResolution); - GDALBuildVRTOptionsFree(psOptions); - return nullptr; - } - } - else if (EQUAL(papszArgv[iArg], "-input_file_list") && iArg + 1 < argc) - { - ++iArg; - if (psOptionsForBinary) - { - const char *input_file_list = papszArgv[iArg]; - VSILFILE *f = VSIFOpenL(input_file_list, "r"); - if (f) - { - while (1) - { - const char *filename = CPLReadLineL(f); - if (filename == nullptr) - break; - if (!add_file_to_list( - filename, tile_index, - &psOptionsForBinary->nSrcFiles, - &psOptionsForBinary->papszSrcFiles)) - { - VSIFCloseL(f); - GDALBuildVRTOptionsFree(psOptions); - return nullptr; - } - } - VSIFCloseL(f); - } - } - else - { - CPLError(CE_Failure, CPLE_NotSupported, - "-input_file_list not supported in non binary mode"); - } - } - else if (EQUAL(papszArgv[iArg], "-separate")) - { - psOptions->bSeparate = TRUE; - } - else if (EQUAL(papszArgv[iArg], "-allow_projection_difference")) - { - psOptions->bAllowProjectionDifference = TRUE; - } - else if (EQUAL(papszArgv[iArg], "-sd") && iArg + 1 < argc) - { - psOptions->nSubdataset = atoi(papszArgv[++iArg]); - } - /* Alternate syntax for output file */ - else if (EQUAL(papszArgv[iArg], "-o") && iArg + 1 < argc) - { - ++iArg; - if (psOptionsForBinary) - { - CPLFree(psOptionsForBinary->pszDstFilename); - psOptionsForBinary->pszDstFilename = CPLStrdup(papszArgv[iArg]); - } - else - { - CPLError(CE_Failure, CPLE_NotSupported, - "-o not supported in non binary mode"); - } - } - else if (EQUAL(papszArgv[iArg], "-q") || - EQUAL(papszArgv[iArg], "-quiet")) - { - if (psOptionsForBinary) - { - psOptionsForBinary->bQuiet = TRUE; - } - } - else if (EQUAL(papszArgv[iArg], "-tr") && iArg + 2 < argc) - { - psOptions->we_res = CPLAtofM(papszArgv[++iArg]); - psOptions->ns_res = CPLAtofM(papszArgv[++iArg]); - } - else if (EQUAL(papszArgv[iArg], "-tap")) + if (psOptionsForBinary && EQUAL(papszArgv[i], "-o") && i + 1 < nArgc && + papszArgv[i + 1] != nullptr) { - psOptions->bTargetAlignedPixels = TRUE; + // Undocumented alternate way of specifying the destination file + psOptionsForBinary->osDstFilename = papszArgv[i + 1]; + ++i; } - else if (EQUAL(papszArgv[iArg], "-te") && iArg + 4 < argc) - { - psOptions->xmin = CPLAtofM(papszArgv[++iArg]); - psOptions->ymin = CPLAtofM(papszArgv[++iArg]); - psOptions->xmax = CPLAtofM(papszArgv[++iArg]); - psOptions->ymax = CPLAtofM(papszArgv[++iArg]); - } - else if (EQUAL(papszArgv[iArg], "-addalpha")) + else { - psOptions->bAddAlpha = TRUE; + aosArgv.AddString(papszArgv[i]); } - else if (EQUAL(papszArgv[iArg], "-b") && iArg + 1 < argc) - { - const char *pszBand = papszArgv[++iArg]; - int nBand = atoi(pszBand); - if (nBand < 1) - { - CPLError(CE_Failure, CPLE_IllegalArg, - "Illegal band number (%s).", papszArgv[iArg]); - GDALBuildVRTOptionsFree(psOptions); - return nullptr; - } + } - psOptions->nBandCount++; - psOptions->panSelectedBandList = static_cast( - CPLRealloc(psOptions->panSelectedBandList, - sizeof(int) * psOptions->nBandCount)); - psOptions->panSelectedBandList[psOptions->nBandCount - 1] = nBand; - } - else if (EQUAL(papszArgv[iArg], "-hidenodata")) - { - psOptions->bHideNoData = TRUE; - } - else if (EQUAL(papszArgv[iArg], "-overwrite")) - { - if (psOptionsForBinary) - psOptionsForBinary->bOverwrite = TRUE; - } - else if (EQUAL(papszArgv[iArg], "-srcnodata") && iArg + 1 < argc) - { - CPLFree(psOptions->pszSrcNoData); - psOptions->pszSrcNoData = CPLStrdup(papszArgv[++iArg]); - } - else if (EQUAL(papszArgv[iArg], "-vrtnodata") && iArg + 1 < argc) - { - CPLFree(psOptions->pszVRTNoData); - psOptions->pszVRTNoData = CPLStrdup(papszArgv[++iArg]); - } - else if (EQUAL(papszArgv[iArg], "-a_srs") && iArg + 1 < argc) - { - char *pszSRS = SanitizeSRS(papszArgv[++iArg]); - if (pszSRS == nullptr) - { - GDALBuildVRTOptionsFree(psOptions); - return nullptr; - } - CPLFree(psOptions->pszOutputSRS); - psOptions->pszOutputSRS = pszSRS; - } - else if (EQUAL(papszArgv[iArg], "-r") && iArg + 1 < argc) - { - CPLFree(psOptions->pszResampling); - psOptions->pszResampling = CPLStrdup(papszArgv[++iArg]); - } - else if (EQUAL(papszArgv[iArg], "-oo") && iArg + 1 < argc) - { - psOptions->papszOpenOptions = - CSLAddString(psOptions->papszOpenOptions, papszArgv[++iArg]); - } - else if (EQUAL(papszArgv[iArg], "-ignore_srcmaskband")) - { - psOptions->bUseSrcMaskBand = false; - } - else if (EQUAL(papszArgv[iArg], "-nodata_max_mask_threshold") && - iArg + 1 < argc) - { - psOptions->bNoDataFromMask = true; - psOptions->dfMaskValueThreshold = CPLAtofM(papszArgv[++iArg]); - } - else if (papszArgv[iArg][0] == '-') + try + { + auto argParser = + GDALBuildVRTOptionsGetParser(psOptions.get(), psOptionsForBinary); + + argParser->parse_args_without_binary_name(aosArgv.List()); + + if (auto adfTargetRes = argParser->present>("-tr")) { - CPLError(CE_Failure, CPLE_NotSupported, "Unknown option name '%s'", - papszArgv[iArg]); - GDALBuildVRTOptionsFree(psOptions); - return nullptr; + psOptions->we_res = (*adfTargetRes)[0]; + psOptions->ns_res = (*adfTargetRes)[1]; } - else + + if (auto oTE = argParser->present>("-te")) { - if (psOptionsForBinary) - { - if (psOptionsForBinary->pszDstFilename == nullptr) - psOptionsForBinary->pszDstFilename = - CPLStrdup(papszArgv[iArg]); - else - { - if (!add_file_to_list(papszArgv[iArg], tile_index, - &psOptionsForBinary->nSrcFiles, - &psOptionsForBinary->papszSrcFiles)) - { - GDALBuildVRTOptionsFree(psOptions); - return nullptr; - } - } - } + psOptions->xmin = (*oTE)[0]; + psOptions->ymin = (*oTE)[1]; + psOptions->xmax = (*oTE)[2]; + psOptions->ymax = (*oTE)[3]; } - } - return psOptions; + return psOptions.release(); + } + catch (const std::exception &err) + { + CPLError(CE_Failure, CPLE_AppDefined, "%s", err.what()); + return nullptr; + } } /************************************************************************/ @@ -2260,18 +2318,7 @@ GDALBuildVRTOptionsNew(char **papszArgv, void GDALBuildVRTOptionsFree(GDALBuildVRTOptions *psOptions) { - if (psOptions) - { - CPLFree(psOptions->pszResolution); - CPLFree(psOptions->pszSrcNoData); - CPLFree(psOptions->pszVRTNoData); - CPLFree(psOptions->pszOutputSRS); - CPLFree(psOptions->panSelectedBandList); - CPLFree(psOptions->pszResampling); - CSLDestroy(psOptions->papszOpenOptions); - } - - CPLFree(psOptions); + delete psOptions; } /************************************************************************/ @@ -2295,5 +2342,5 @@ void GDALBuildVRTOptionsSetProgress(GDALBuildVRTOptions *psOptions, psOptions->pfnProgress = pfnProgress ? pfnProgress : GDALDummyProgress; psOptions->pProgressData = pProgressData; if (pfnProgress == GDALTermProgress) - psOptions->bQuiet = FALSE; + psOptions->bQuiet = false; } diff --git a/doc/source/programs/gdalbuildvrt.rst b/doc/source/programs/gdalbuildvrt.rst index f3bf3eec0a11..0a8a28cf9cab 100644 --- a/doc/source/programs/gdalbuildvrt.rst +++ b/doc/source/programs/gdalbuildvrt.rst @@ -19,7 +19,7 @@ Synopsis [-tileindex ] [-resolution {highest|lowest|average|user}] [-te ] [-tr ] [-tap] - [-separate] [-b ]... [-sd ] + [-separate] [-b ]... [-sd ] [-allow_projection_difference] [-q] [-addalpha] [-hidenodata] [-srcnodata "[ ]..."] [-vrtnodata "[ ]..." @@ -72,7 +72,7 @@ changed in later versions. .. include:: options/help_and_help_general.rst -.. option:: -tileindex +.. option:: -tileindex Use the specified value as the tile index field, instead of the default value which is 'location'. @@ -161,12 +161,11 @@ changed in later versions. If input bands not set all bands will be added to vrt. Multiple :option:`-b` switches may be used to select a set of input bands. -.. option:: -sd< +.. option:: -sd - If the input - dataset contains several subdatasets use a subdataset with the specified - number (starting from 1). This is an alternative of giving the full subdataset - name as an input. + If the input dataset contains several subdatasets, use a subdataset with the + specified number (starting from 1). This is an alternative of giving the full subdataset + name as an input to the utility. .. option:: -vrtnodata "[ ]..."