310 lines
10 KiB
C++
310 lines
10 KiB
C++
/******************************************************************************
|
|
*
|
|
* Project: High Performance Image Reprojector
|
|
* Purpose: Test program for high performance warper API.
|
|
* Author: Frank Warmerdam <warmerdam@pobox.com>
|
|
*
|
|
******************************************************************************
|
|
* Copyright (c) 2002, i3 - information integration and imaging
|
|
* Fort Collin, CO
|
|
* Copyright (c) 2007-2015, Even Rouault <even dot rouault at spatialys.com>
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
****************************************************************************/
|
|
|
|
#include "cpl_string.h"
|
|
#include "cpl_error_internal.h"
|
|
#include "gdal_version.h"
|
|
#include "commonutils.h"
|
|
#include "gdal_utils_priv.h"
|
|
|
|
#include <vector>
|
|
|
|
/************************************************************************/
|
|
/* GDALExit() */
|
|
/* This function exits and cleans up GDAL and OGR resources */
|
|
/* Perhaps it should be added to C api and used in all apps? */
|
|
/************************************************************************/
|
|
|
|
static int GDALExit(int nCode)
|
|
{
|
|
const char *pszDebug = CPLGetConfigOption("CPL_DEBUG", nullptr);
|
|
if (pszDebug && (EQUAL(pszDebug, "ON") || EQUAL(pszDebug, "")))
|
|
{
|
|
GDALDumpOpenDatasets(stderr);
|
|
CPLDumpSharedList(nullptr);
|
|
}
|
|
|
|
GDALDestroyDriverManager();
|
|
|
|
OGRCleanupAll();
|
|
|
|
exit(nCode);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Usage() */
|
|
/************************************************************************/
|
|
|
|
static void Usage()
|
|
|
|
{
|
|
fprintf(stderr, "%s\n", GDALWarpAppGetParserUsage().c_str());
|
|
GDALExit(1);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* WarpTermProgress() */
|
|
/************************************************************************/
|
|
|
|
static int gnSrcCount = 0;
|
|
|
|
static int CPL_STDCALL WarpTermProgress(double dfProgress,
|
|
const char *pszMessage, void *)
|
|
{
|
|
static CPLString osLastMsg;
|
|
static int iSrc = -1;
|
|
if (pszMessage == nullptr)
|
|
{
|
|
iSrc = 0;
|
|
}
|
|
else if (pszMessage != osLastMsg)
|
|
{
|
|
if (!osLastMsg.empty())
|
|
GDALTermProgress(1.0, nullptr, nullptr);
|
|
printf("%s : ", pszMessage);
|
|
osLastMsg = pszMessage;
|
|
iSrc++;
|
|
}
|
|
return GDALTermProgress(dfProgress * gnSrcCount - iSrc, nullptr, nullptr);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* main() */
|
|
/************************************************************************/
|
|
|
|
MAIN_START(argc, argv)
|
|
|
|
{
|
|
EarlySetConfigOptions(argc, argv);
|
|
CPLDebugOnly("GDAL", "Start");
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Register standard GDAL drivers, and process generic GDAL */
|
|
/* command options. */
|
|
/* -------------------------------------------------------------------- */
|
|
GDALAllRegister();
|
|
argc = GDALGeneralCmdLineProcessor(argc, &argv, 0);
|
|
if (argc < 1)
|
|
GDALExit(-argc);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Set optimal setting for best performance with huge input VRT. */
|
|
/* The rationale for 450 is that typical Linux process allow */
|
|
/* only 1024 file descriptors per process and we need to keep some */
|
|
/* spare for shared libraries, etc. so let's go down to 900. */
|
|
/* And some datasets may need 2 file descriptors, so divide by 2 */
|
|
/* for security. */
|
|
/* -------------------------------------------------------------------- */
|
|
if (CPLGetConfigOption("GDAL_MAX_DATASET_POOL_SIZE", nullptr) == nullptr)
|
|
{
|
|
#if defined(__MACH__) && defined(__APPLE__)
|
|
// On Mach, the default limit is 256 files per process
|
|
// TODO We should eventually dynamically query the limit for all OS
|
|
CPLSetConfigOption("GDAL_MAX_DATASET_POOL_SIZE", "100");
|
|
#else
|
|
CPLSetConfigOption("GDAL_MAX_DATASET_POOL_SIZE", "450");
|
|
#endif
|
|
}
|
|
|
|
GDALWarpAppOptionsForBinary sOptionsForBinary;
|
|
/* coverity[tainted_data] */
|
|
GDALWarpAppOptions *psOptions =
|
|
GDALWarpAppOptionsNew(argv + 1, &sOptionsForBinary);
|
|
CSLDestroy(argv);
|
|
|
|
if (psOptions == nullptr)
|
|
{
|
|
Usage();
|
|
}
|
|
|
|
if (sOptionsForBinary.aosSrcFiles.size() == 1 &&
|
|
sOptionsForBinary.aosSrcFiles[0] == sOptionsForBinary.osDstFilename &&
|
|
sOptionsForBinary.bOverwrite)
|
|
{
|
|
CPLError(CE_Failure, CPLE_IllegalArg,
|
|
"Source and destination datasets must be different.\n");
|
|
GDALExit(1);
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Open source files. */
|
|
/* -------------------------------------------------------------------- */
|
|
GDALDatasetH *pahSrcDS = nullptr;
|
|
int nSrcCount = 0;
|
|
for (int i = 0; i < sOptionsForBinary.aosSrcFiles.size(); ++i)
|
|
{
|
|
nSrcCount++;
|
|
pahSrcDS = static_cast<GDALDatasetH *>(
|
|
CPLRealloc(pahSrcDS, sizeof(GDALDatasetH) * nSrcCount));
|
|
pahSrcDS[i] =
|
|
GDALOpenEx(sOptionsForBinary.aosSrcFiles[i],
|
|
GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
|
|
sOptionsForBinary.aosAllowedInputDrivers.List(),
|
|
sOptionsForBinary.aosOpenOptions.List(), nullptr);
|
|
|
|
if (pahSrcDS[i] == nullptr)
|
|
{
|
|
CPLError(CE_Failure, CPLE_OpenFailed,
|
|
"Failed to open source file %s\n",
|
|
sOptionsForBinary.aosSrcFiles[i]);
|
|
while (nSrcCount--)
|
|
{
|
|
GDALClose(pahSrcDS[nSrcCount]);
|
|
pahSrcDS[nSrcCount] = nullptr;
|
|
}
|
|
CPLFree(pahSrcDS);
|
|
GDALWarpAppOptionsFree(psOptions);
|
|
GDALExit(2);
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Does the output dataset already exist? */
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* FIXME ? source filename=target filename and -overwrite is definitely */
|
|
/* an error. But I can't imagine of a valid case (without -overwrite), */
|
|
/* where it would make sense. In doubt, let's keep that dubious
|
|
* possibility... */
|
|
|
|
bool bOutStreaming = false;
|
|
if (sOptionsForBinary.osDstFilename == "/vsistdout/")
|
|
{
|
|
sOptionsForBinary.bQuiet = true;
|
|
bOutStreaming = true;
|
|
}
|
|
#ifdef S_ISFIFO
|
|
else
|
|
{
|
|
VSIStatBufL sStat;
|
|
if (VSIStatExL(sOptionsForBinary.osDstFilename.c_str(), &sStat,
|
|
VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG) == 0 &&
|
|
S_ISFIFO(sStat.st_mode))
|
|
{
|
|
bOutStreaming = true;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
GDALDatasetH hDstDS = nullptr;
|
|
if (bOutStreaming)
|
|
{
|
|
GDALWarpAppOptionsSetWarpOption(psOptions, "STREAMABLE_OUTPUT", "YES");
|
|
}
|
|
else
|
|
{
|
|
std::vector<CPLErrorHandlerAccumulatorStruct> aoErrors;
|
|
CPLInstallErrorHandlerAccumulator(aoErrors);
|
|
hDstDS = GDALOpenEx(
|
|
sOptionsForBinary.osDstFilename.c_str(),
|
|
GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR | GDAL_OF_UPDATE, nullptr,
|
|
sOptionsForBinary.aosDestOpenOptions.List(), nullptr);
|
|
CPLUninstallErrorHandlerAccumulator();
|
|
if (hDstDS != nullptr)
|
|
{
|
|
for (size_t i = 0; i < aoErrors.size(); i++)
|
|
{
|
|
CPLError(aoErrors[i].type, aoErrors[i].no, "%s",
|
|
aoErrors[i].msg.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hDstDS != nullptr && sOptionsForBinary.bOverwrite)
|
|
{
|
|
GDALClose(hDstDS);
|
|
hDstDS = nullptr;
|
|
}
|
|
|
|
bool bCheckExistingDstFile =
|
|
!bOutStreaming && hDstDS == nullptr && !sOptionsForBinary.bOverwrite;
|
|
|
|
if (hDstDS != nullptr && sOptionsForBinary.bCreateOutput)
|
|
{
|
|
if (sOptionsForBinary.aosCreateOptions.FetchBool("APPEND_SUBDATASET",
|
|
false))
|
|
{
|
|
GDALClose(hDstDS);
|
|
hDstDS = nullptr;
|
|
bCheckExistingDstFile = false;
|
|
}
|
|
else
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Output dataset %s exists,\n"
|
|
"but some command line options were provided indicating a "
|
|
"new dataset\n"
|
|
"should be created. Please delete existing dataset and "
|
|
"run again.\n",
|
|
sOptionsForBinary.osDstFilename.c_str());
|
|
GDALExit(1);
|
|
}
|
|
}
|
|
|
|
/* Avoid overwriting an existing destination file that cannot be opened in
|
|
*/
|
|
/* update mode with a new GTiff file */
|
|
if (bCheckExistingDstFile)
|
|
{
|
|
CPLPushErrorHandler(CPLQuietErrorHandler);
|
|
hDstDS = GDALOpen(sOptionsForBinary.osDstFilename.c_str(), GA_ReadOnly);
|
|
CPLPopErrorHandler();
|
|
|
|
if (hDstDS)
|
|
{
|
|
CPLError(CE_Failure, CPLE_AppDefined,
|
|
"Output dataset %s exists, but cannot be opened in update "
|
|
"mode\n",
|
|
sOptionsForBinary.osDstFilename.c_str());
|
|
GDALClose(hDstDS);
|
|
GDALExit(1);
|
|
}
|
|
}
|
|
|
|
if (!(sOptionsForBinary.bQuiet))
|
|
{
|
|
gnSrcCount = nSrcCount;
|
|
GDALWarpAppOptionsSetProgress(psOptions, WarpTermProgress, nullptr);
|
|
GDALWarpAppOptionsSetQuiet(psOptions, false);
|
|
}
|
|
|
|
int bUsageError = FALSE;
|
|
GDALDatasetH hOutDS =
|
|
GDALWarp(sOptionsForBinary.osDstFilename.c_str(), hDstDS, nSrcCount,
|
|
pahSrcDS, psOptions, &bUsageError);
|
|
if (bUsageError)
|
|
Usage();
|
|
int nRetCode = (hOutDS) ? 0 : 1;
|
|
|
|
GDALWarpAppOptionsFree(psOptions);
|
|
|
|
// Close first hOutDS since it might reference sources (case of VRT)
|
|
if (GDALClose(hOutDS ? hOutDS : hDstDS) != CE_None)
|
|
nRetCode = 1;
|
|
|
|
for (int i = 0; i < nSrcCount; i++)
|
|
{
|
|
GDALClose(pahSrcDS[i]);
|
|
}
|
|
CPLFree(pahSrcDS);
|
|
|
|
GDALDumpOpenDatasets(stderr);
|
|
|
|
OGRCleanupAll();
|
|
|
|
return nRetCode;
|
|
}
|
|
|
|
MAIN_END
|