Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: High Performance Image Reprojector
4 : * Purpose: Test program for high performance warper API.
5 : * Author: Frank Warmerdam <warmerdam@pobox.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2002, i3 - information integration and imaging
9 : * Fort Collin, CO
10 : * Copyright (c) 2007-2015, Even Rouault <even dot rouault at spatialys.com>
11 : *
12 : * SPDX-License-Identifier: MIT
13 : ****************************************************************************/
14 :
15 : #include "cpl_string.h"
16 : #include "cpl_error_internal.h"
17 : #include "gdal_version.h"
18 : #include "commonutils.h"
19 : #include "gdal_utils_priv.h"
20 :
21 : #include <vector>
22 :
23 : /************************************************************************/
24 : /* GDALExit() */
25 : /* This function exits and cleans up GDAL and OGR resources */
26 : /* Perhaps it should be added to C api and used in all apps? */
27 : /************************************************************************/
28 :
29 4 : static int GDALExit(int nCode)
30 : {
31 4 : const char *pszDebug = CPLGetConfigOption("CPL_DEBUG", nullptr);
32 4 : if (pszDebug && (EQUAL(pszDebug, "ON") || EQUAL(pszDebug, "")))
33 : {
34 0 : GDALDumpOpenDatasets(stderr);
35 0 : CPLDumpSharedList(nullptr);
36 : }
37 :
38 4 : GDALDestroyDriverManager();
39 :
40 4 : OGRCleanupAll();
41 :
42 4 : exit(nCode);
43 : }
44 :
45 : /************************************************************************/
46 : /* Usage() */
47 : /************************************************************************/
48 :
49 2 : static void Usage()
50 :
51 : {
52 2 : fprintf(stderr, "%s\n", GDALWarpAppGetParserUsage().c_str());
53 2 : GDALExit(1);
54 0 : }
55 :
56 : /************************************************************************/
57 : /* WarpTermProgress() */
58 : /************************************************************************/
59 :
60 : static int gnSrcCount = 0;
61 :
62 14398 : static int CPL_STDCALL WarpTermProgress(double dfProgress,
63 : const char *pszMessage, void *)
64 : {
65 14398 : static CPLString osLastMsg;
66 : static int iSrc = -1;
67 14398 : if (pszMessage == nullptr)
68 : {
69 0 : iSrc = 0;
70 : }
71 14398 : else if (pszMessage != osLastMsg)
72 : {
73 98 : if (!osLastMsg.empty())
74 5 : GDALTermProgress(1.0, nullptr, nullptr);
75 98 : printf("%s : ", pszMessage);
76 98 : osLastMsg = pszMessage;
77 98 : iSrc++;
78 : }
79 14398 : return GDALTermProgress(dfProgress * gnSrcCount - iSrc, nullptr, nullptr);
80 : }
81 :
82 : /************************************************************************/
83 : /* main() */
84 : /************************************************************************/
85 :
86 104 : MAIN_START(argc, argv)
87 :
88 : {
89 104 : EarlySetConfigOptions(argc, argv);
90 104 : CPLDebugOnly("GDAL", "Start");
91 :
92 : /* -------------------------------------------------------------------- */
93 : /* Register standard GDAL drivers, and process generic GDAL */
94 : /* command options. */
95 : /* -------------------------------------------------------------------- */
96 104 : GDALAllRegister();
97 104 : argc = GDALGeneralCmdLineProcessor(argc, &argv, 0);
98 104 : if (argc < 1)
99 0 : GDALExit(-argc);
100 :
101 : /* -------------------------------------------------------------------- */
102 : /* Set optimal setting for best performance with huge input VRT. */
103 : /* The rationale for 450 is that typical Linux process allow */
104 : /* only 1024 file descriptors per process and we need to keep some */
105 : /* spare for shared libraries, etc. so let's go down to 900. */
106 : /* And some datasets may need 2 file descriptors, so divide by 2 */
107 : /* for security. */
108 : /* -------------------------------------------------------------------- */
109 104 : if (CPLGetConfigOption("GDAL_MAX_DATASET_POOL_SIZE", nullptr) == nullptr)
110 : {
111 : #if defined(__MACH__) && defined(__APPLE__)
112 : // On Mach, the default limit is 256 files per process
113 : // TODO We should eventually dynamically query the limit for all OS
114 : CPLSetConfigOption("GDAL_MAX_DATASET_POOL_SIZE", "100");
115 : #else
116 104 : CPLSetConfigOption("GDAL_MAX_DATASET_POOL_SIZE", "450");
117 : #endif
118 : }
119 :
120 104 : GDALWarpAppOptionsForBinary sOptionsForBinary;
121 : /* coverity[tainted_data] */
122 : GDALWarpAppOptions *psOptions =
123 104 : GDALWarpAppOptionsNew(argv + 1, &sOptionsForBinary);
124 102 : CSLDestroy(argv);
125 :
126 102 : if (psOptions == nullptr)
127 : {
128 2 : Usage();
129 : }
130 :
131 195 : if (sOptionsForBinary.aosSrcFiles.size() == 1 &&
132 195 : sOptionsForBinary.aosSrcFiles[0] == sOptionsForBinary.osDstFilename &&
133 0 : sOptionsForBinary.bOverwrite)
134 : {
135 0 : CPLError(CE_Failure, CPLE_IllegalArg,
136 : "Source and destination datasets must be different.\n");
137 0 : GDALExit(1);
138 : }
139 :
140 : /* -------------------------------------------------------------------- */
141 : /* Open source files. */
142 : /* -------------------------------------------------------------------- */
143 100 : GDALDatasetH *pahSrcDS = nullptr;
144 100 : int nSrcCount = 0;
145 203 : for (int i = 0; i < sOptionsForBinary.aosSrcFiles.size(); ++i)
146 : {
147 105 : nSrcCount++;
148 : pahSrcDS = static_cast<GDALDatasetH *>(
149 105 : CPLRealloc(pahSrcDS, sizeof(GDALDatasetH) * nSrcCount));
150 210 : pahSrcDS[i] =
151 105 : GDALOpenEx(sOptionsForBinary.aosSrcFiles[i],
152 : GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
153 105 : sOptionsForBinary.aosAllowedInputDrivers.List(),
154 105 : sOptionsForBinary.aosOpenOptions.List(), nullptr);
155 :
156 105 : if (pahSrcDS[i] == nullptr)
157 : {
158 2 : CPLError(CE_Failure, CPLE_OpenFailed,
159 : "Failed to open source file %s\n",
160 : sOptionsForBinary.aosSrcFiles[i]);
161 4 : while (nSrcCount--)
162 : {
163 2 : GDALClose(pahSrcDS[nSrcCount]);
164 2 : pahSrcDS[nSrcCount] = nullptr;
165 : }
166 2 : CPLFree(pahSrcDS);
167 2 : GDALWarpAppOptionsFree(psOptions);
168 2 : GDALExit(2);
169 : }
170 : }
171 :
172 : /* -------------------------------------------------------------------- */
173 : /* Does the output dataset already exist? */
174 : /* -------------------------------------------------------------------- */
175 :
176 : /* FIXME ? source filename=target filename and -overwrite is definitely */
177 : /* an error. But I can't imagine of a valid case (without -overwrite), */
178 : /* where it would make sense. In doubt, let's keep that dubious
179 : * possibility... */
180 :
181 98 : bool bOutStreaming = false;
182 98 : if (sOptionsForBinary.osDstFilename == "/vsistdout/")
183 : {
184 0 : sOptionsForBinary.bQuiet = true;
185 0 : bOutStreaming = true;
186 : }
187 : #ifdef S_ISFIFO
188 : else
189 : {
190 : VSIStatBufL sStat;
191 98 : if (VSIStatExL(sOptionsForBinary.osDstFilename.c_str(), &sStat,
192 129 : VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG) == 0 &&
193 31 : S_ISFIFO(sStat.st_mode))
194 : {
195 0 : bOutStreaming = true;
196 : }
197 : }
198 : #endif
199 :
200 98 : GDALDatasetH hDstDS = nullptr;
201 98 : if (bOutStreaming)
202 : {
203 0 : GDALWarpAppOptionsSetWarpOption(psOptions, "STREAMABLE_OUTPUT", "YES");
204 : }
205 : else
206 : {
207 196 : std::vector<CPLErrorHandlerAccumulatorStruct> aoErrors;
208 98 : CPLInstallErrorHandlerAccumulator(aoErrors);
209 98 : hDstDS = GDALOpenEx(
210 : sOptionsForBinary.osDstFilename.c_str(),
211 : GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR | GDAL_OF_UPDATE, nullptr,
212 98 : sOptionsForBinary.aosDestOpenOptions.List(), nullptr);
213 98 : CPLUninstallErrorHandlerAccumulator();
214 98 : if (hDstDS != nullptr)
215 : {
216 31 : for (size_t i = 0; i < aoErrors.size(); i++)
217 : {
218 0 : CPLError(aoErrors[i].type, aoErrors[i].no, "%s",
219 0 : aoErrors[i].msg.c_str());
220 : }
221 : }
222 : }
223 :
224 98 : if (hDstDS != nullptr && sOptionsForBinary.bOverwrite)
225 : {
226 27 : GDALClose(hDstDS);
227 27 : hDstDS = nullptr;
228 : }
229 :
230 98 : bool bCheckExistingDstFile =
231 98 : !bOutStreaming && hDstDS == nullptr && !sOptionsForBinary.bOverwrite;
232 :
233 98 : if (hDstDS != nullptr && sOptionsForBinary.bCreateOutput)
234 : {
235 1 : if (sOptionsForBinary.aosCreateOptions.FetchBool("APPEND_SUBDATASET",
236 : false))
237 : {
238 1 : GDALClose(hDstDS);
239 1 : hDstDS = nullptr;
240 1 : bCheckExistingDstFile = false;
241 : }
242 : else
243 : {
244 0 : CPLError(CE_Failure, CPLE_AppDefined,
245 : "Output dataset %s exists,\n"
246 : "but some command line options were provided indicating a "
247 : "new dataset\n"
248 : "should be created. Please delete existing dataset and "
249 : "run again.\n",
250 : sOptionsForBinary.osDstFilename.c_str());
251 0 : GDALExit(1);
252 : }
253 : }
254 :
255 : /* Avoid overwriting an existing destination file that cannot be opened in
256 : */
257 : /* update mode with a new GTiff file */
258 98 : if (bCheckExistingDstFile)
259 : {
260 41 : CPLPushErrorHandler(CPLQuietErrorHandler);
261 41 : hDstDS = GDALOpen(sOptionsForBinary.osDstFilename.c_str(), GA_ReadOnly);
262 41 : CPLPopErrorHandler();
263 :
264 41 : if (hDstDS)
265 : {
266 0 : CPLError(CE_Failure, CPLE_AppDefined,
267 : "Output dataset %s exists, but cannot be opened in update "
268 : "mode\n",
269 : sOptionsForBinary.osDstFilename.c_str());
270 0 : GDALClose(hDstDS);
271 0 : GDALExit(1);
272 : }
273 : }
274 :
275 98 : if (!(sOptionsForBinary.bQuiet))
276 : {
277 93 : gnSrcCount = nSrcCount;
278 93 : GDALWarpAppOptionsSetProgress(psOptions, WarpTermProgress, nullptr);
279 93 : GDALWarpAppOptionsSetQuiet(psOptions, false);
280 : }
281 :
282 98 : int bUsageError = FALSE;
283 : GDALDatasetH hOutDS =
284 98 : GDALWarp(sOptionsForBinary.osDstFilename.c_str(), hDstDS, nSrcCount,
285 : pahSrcDS, psOptions, &bUsageError);
286 98 : if (bUsageError)
287 0 : Usage();
288 98 : int nRetCode = (hOutDS) ? 0 : 1;
289 :
290 98 : GDALWarpAppOptionsFree(psOptions);
291 :
292 : // Close first hOutDS since it might reference sources (case of VRT)
293 98 : if (GDALClose(hOutDS ? hOutDS : hDstDS) != CE_None)
294 0 : nRetCode = 1;
295 :
296 201 : for (int i = 0; i < nSrcCount; i++)
297 : {
298 103 : GDALClose(pahSrcDS[i]);
299 : }
300 98 : CPLFree(pahSrcDS);
301 :
302 98 : GDALDumpOpenDatasets(stderr);
303 :
304 98 : OGRCleanupAll();
305 :
306 98 : return nRetCode;
307 : }
308 :
309 0 : MAIN_END
|