Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Contour Generator
4 : * Purpose: Contour Generator mainline.
5 : * Author: Frank Warmerdam <warmerdam@pobox.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2003, Applied Coherent Technology (www.actgate.com).
9 : * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
10 : * Copyright (c) 2018, Oslandia <infos at oslandia dot com>
11 : *
12 : * SPDX-License-Identifier: MIT
13 : ****************************************************************************/
14 :
15 : #include "cpl_conv.h"
16 : #include "cpl_string.h"
17 : #include "gdal_version.h"
18 : #include "gdal.h"
19 : #include "gdal_alg.h"
20 : #include "gdalargumentparser.h"
21 : #include "ogr_api.h"
22 : #include "ogr_srs_api.h"
23 : #include "commonutils.h"
24 : #include "gdal_utils_priv.h"
25 :
26 : /************************************************************************/
27 : /* GDALContourOptions */
28 : /************************************************************************/
29 :
30 : /** Options for use with GDALContour(). GDALContourOptions must be allocated
31 : * with GDALContourOptionsNew() and deallocated with GDALContourOptionsFree().
32 : */
33 : struct GDALContourOptions
34 : {
35 : int nBand = 1;
36 : double dfInterval = 0.0;
37 : double dfNoData = 0.0;
38 : double dfOffset = 0.0;
39 : double dfExpBase = 0.0;
40 : bool b3D = false;
41 : bool bPolygonize = false;
42 : bool bNoDataSet = false;
43 : bool bIgnoreNoData = false;
44 : std::string osNewLayerName = "contour";
45 : std::string osFormat{};
46 : std::string osElevAttrib{};
47 : std::string osElevAttribMin{};
48 : std::string osElevAttribMax{};
49 : std::vector<std::string> aosFixedLevels{};
50 : CPLStringList aosOpenOptions{};
51 : CPLStringList aosCreationOptions{};
52 : CPLStringList aosLayerCreationOptions{};
53 : bool bQuiet = false;
54 : std::string osDestDataSource{};
55 : std::string osSrcDataSource{};
56 : GIntBig nGroupTransactions = 100 * 1000;
57 : GDALProgressFunc pfnProgress = GDALDummyProgress;
58 : void *pProgressData = nullptr;
59 : };
60 :
61 : /************************************************************************/
62 : /* GDALContourOptionsSetDestDataSource */
63 : /************************************************************************/
64 6 : void GDALContourOptionsSetDestDataSource(GDALContourOptions *psOptions,
65 : const char *pszDestDatasource)
66 : {
67 6 : psOptions->osDestDataSource = pszDestDatasource;
68 6 : }
69 :
70 : /************************************************************************/
71 : /* GDALContourOptionsSetProgress() */
72 : /************************************************************************/
73 :
74 : /**
75 : * Set a progress function.
76 : *
77 : * @param psOptions the options struct for GDALContour().
78 : * @param pfnProgress the progress callback.
79 : * @param pProgressData the user data for the progress callback.
80 : *
81 : */
82 :
83 0 : void GDALContourOptionsSetProgress(GDALContourOptions *psOptions,
84 : GDALProgressFunc pfnProgress,
85 : void *pProgressData)
86 : {
87 0 : psOptions->pfnProgress = pfnProgress ? pfnProgress : GDALDummyProgress;
88 0 : psOptions->pProgressData = pProgressData;
89 0 : }
90 :
91 : ///@cond Doxygen_Suppress
92 :
93 : /************************************************************************/
94 : /* CreateElevAttrib() */
95 : /************************************************************************/
96 :
97 48 : static bool CreateElevAttrib(const char *pszElevAttrib, OGRLayerH hLayer)
98 : {
99 48 : OGRFieldDefnH hFld = OGR_Fld_Create(pszElevAttrib, OFTReal);
100 48 : OGRErr eErr = OGR_L_CreateField(hLayer, hFld, FALSE);
101 48 : OGR_Fld_Destroy(hFld);
102 48 : return eErr == OGRERR_NONE;
103 : }
104 :
105 : /************************************************************************/
106 : /* GDALContourProcessOptions() */
107 : /************************************************************************/
108 :
109 43 : CPLErr GDALContourProcessOptions(GDALContourOptions *psOptions,
110 : char ***ppapszStringOptions,
111 : GDALDatasetH *hSrcDS, GDALRasterBandH *hBand,
112 : GDALDatasetH *hDstDS, OGRLayerH *hLayer)
113 : {
114 :
115 : /* -------------------------------------------------------------------- */
116 : /* Open source raster file. */
117 : /* -------------------------------------------------------------------- */
118 43 : if (!*hSrcDS)
119 : {
120 19 : *hSrcDS = GDALOpen(psOptions->osSrcDataSource.c_str(), GA_ReadOnly);
121 : }
122 :
123 43 : if (*hSrcDS == nullptr)
124 : {
125 0 : CPLError(CE_Failure, CPLE_AppDefined,
126 : "Unable to open source raster file '%s'.",
127 0 : psOptions->osSrcDataSource.c_str());
128 0 : return CE_Failure;
129 : }
130 :
131 43 : if (!*hBand)
132 : {
133 43 : *hBand = GDALGetRasterBand(*hSrcDS, psOptions->nBand);
134 : }
135 :
136 43 : if (*hBand == nullptr)
137 : {
138 0 : CPLError(CE_Failure, CPLE_AppDefined,
139 : "Band %d does not exist on dataset.", psOptions->nBand);
140 0 : return CE_Failure;
141 : }
142 :
143 43 : if (!psOptions->bNoDataSet && !psOptions->bIgnoreNoData)
144 : {
145 : int bNoDataSet;
146 42 : psOptions->dfNoData = GDALGetRasterNoDataValue(*hBand, &bNoDataSet);
147 42 : psOptions->bNoDataSet = bNoDataSet;
148 : }
149 :
150 : /* -------------------------------------------------------------------- */
151 : /* Try to get a coordinate system from the raster. */
152 : /* -------------------------------------------------------------------- */
153 43 : OGRSpatialReferenceH hSRS = GDALGetSpatialRef(*hSrcDS);
154 :
155 : // Dedup lambda to create the layer
156 37 : auto CreateLayer = [&]() -> OGRLayerH
157 : {
158 74 : return GDALDatasetCreateLayer(
159 37 : *hDstDS, psOptions->osNewLayerName.c_str(), hSRS,
160 37 : psOptions->bPolygonize
161 12 : ? (psOptions->b3D ? wkbMultiPolygon25D : wkbMultiPolygon)
162 25 : : (psOptions->b3D ? wkbLineString25D : wkbLineString),
163 37 : psOptions->aosLayerCreationOptions);
164 43 : };
165 :
166 : /* -------------------------------------------------------------------- */
167 : /* Create the output file. */
168 : /* -------------------------------------------------------------------- */
169 43 : if (!*hDstDS && !*hLayer)
170 : {
171 37 : CPLString osFormat;
172 37 : if (psOptions->osFormat.empty())
173 : {
174 : const auto aoDrivers = GetOutputDriversFor(
175 37 : psOptions->osDestDataSource.c_str(), GDAL_OF_VECTOR);
176 37 : if (aoDrivers.empty())
177 : {
178 0 : CPLError(CE_Failure, CPLE_AppDefined,
179 : "Cannot guess driver for %s",
180 0 : psOptions->osDestDataSource.c_str());
181 0 : return CE_Failure;
182 : }
183 : else
184 : {
185 37 : if (aoDrivers.size() > 1)
186 : {
187 0 : CPLError(
188 : CE_Warning, CPLE_AppDefined,
189 : "Several drivers matching %s extension. Using %s",
190 0 : CPLGetExtensionSafe(psOptions->osDestDataSource.c_str())
191 : .c_str(),
192 0 : aoDrivers[0].c_str());
193 : }
194 37 : osFormat = aoDrivers[0];
195 : }
196 : }
197 : else
198 : {
199 0 : osFormat = psOptions->osFormat;
200 : }
201 :
202 37 : OGRSFDriverH hDriver = OGRGetDriverByName(osFormat.c_str());
203 :
204 37 : if (hDriver == nullptr)
205 : {
206 0 : fprintf(stderr, "Unable to find format driver named %s.\n",
207 : osFormat.c_str());
208 0 : return CE_Failure;
209 : }
210 :
211 37 : if (!*hDstDS)
212 : {
213 74 : *hDstDS =
214 37 : GDALCreate(hDriver, psOptions->osDestDataSource.c_str(), 0, 0,
215 37 : 0, GDT_Unknown, psOptions->aosCreationOptions);
216 : }
217 :
218 37 : if (*hDstDS == nullptr)
219 : {
220 0 : return CE_Failure;
221 : }
222 :
223 : // Create the layer
224 37 : *hLayer = CreateLayer();
225 : }
226 :
227 43 : if (!*hLayer)
228 : {
229 6 : auto hDriver = GDALGetDatasetDriver(*hDstDS);
230 : // Try to load the layer if it already exists
231 6 : if (GDALGetMetadataItem(hDriver, GDAL_DCAP_MULTIPLE_VECTOR_LAYERS,
232 6 : nullptr))
233 : {
234 6 : *hLayer = GDALDatasetGetLayerByName(
235 6 : *hDstDS, psOptions->osNewLayerName.c_str());
236 12 : if (!*hLayer &&
237 6 : GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE_LAYER, nullptr) &&
238 0 : !GDALDatasetTestCapability(*hDstDS, ODsCCreateLayer))
239 : {
240 0 : *hLayer = CreateLayer();
241 : }
242 : }
243 : else
244 : {
245 0 : *hLayer = GDALDatasetGetLayer(*hDstDS, 0);
246 : }
247 : }
248 :
249 43 : if (*hLayer == nullptr)
250 : {
251 0 : return CE_Failure;
252 : }
253 :
254 43 : if (!OGR_L_TestCapability(*hLayer, OLCTransactions))
255 : {
256 39 : psOptions->nGroupTransactions = 0;
257 : }
258 :
259 43 : OGRFieldDefnH hFld = OGR_Fld_Create("ID", OFTInteger);
260 43 : OGR_Fld_SetWidth(hFld, 8);
261 43 : OGR_L_CreateField(*hLayer, hFld, FALSE);
262 43 : OGR_Fld_Destroy(hFld);
263 :
264 43 : if (psOptions->bPolygonize)
265 : {
266 13 : if (!psOptions->osElevAttrib.empty())
267 : {
268 0 : psOptions->osElevAttrib.clear();
269 0 : CPLError(CE_Warning, CPLE_NotSupported,
270 : "-a is ignored in polygonal contouring mode. "
271 : "Use -amin and/or -amax instead");
272 : }
273 : }
274 : else
275 : {
276 58 : if (!psOptions->osElevAttribMin.empty() ||
277 28 : !psOptions->osElevAttribMax.empty())
278 : {
279 2 : psOptions->osElevAttribMin.clear();
280 2 : psOptions->osElevAttribMax.clear();
281 2 : CPLError(CE_Warning, CPLE_NotSupported,
282 : "-amin and/or -amax are ignored in line contouring mode. "
283 : "Use -a instead");
284 : }
285 : }
286 :
287 43 : OGRFeatureDefnH hFeatureDefn = OGR_L_GetLayerDefn(*hLayer);
288 :
289 43 : if (!psOptions->osElevAttrib.empty())
290 : {
291 : // Skip if field already exists
292 24 : if (OGR_FD_GetFieldIndex(hFeatureDefn,
293 48 : psOptions->osElevAttrib.c_str()) == -1)
294 : {
295 24 : if (!CreateElevAttrib(psOptions->osElevAttrib.c_str(), *hLayer))
296 : {
297 0 : CPLError(CE_Failure, CPLE_AppDefined,
298 : "Failed to create elevation field '%s'",
299 0 : psOptions->osElevAttrib.c_str());
300 0 : return CE_Failure;
301 : }
302 : }
303 : }
304 :
305 43 : if (!psOptions->osElevAttribMin.empty())
306 : {
307 : // Skip if field already exists
308 13 : if (OGR_FD_GetFieldIndex(hFeatureDefn,
309 26 : psOptions->osElevAttribMin.c_str()) == -1)
310 : {
311 12 : if (!CreateElevAttrib(psOptions->osElevAttribMin.c_str(), *hLayer))
312 : {
313 0 : CPLError(CE_Failure, CPLE_AppDefined,
314 : "Failed to create elevation min field '%s'",
315 0 : psOptions->osElevAttribMin.c_str());
316 0 : return CE_Failure;
317 : }
318 : }
319 : }
320 :
321 43 : if (!psOptions->osElevAttribMax.empty())
322 : {
323 : // Skip if field already exists
324 13 : if (OGR_FD_GetFieldIndex(hFeatureDefn,
325 26 : psOptions->osElevAttribMax.c_str()) == -1)
326 : {
327 12 : if (!CreateElevAttrib(psOptions->osElevAttribMax.c_str(), *hLayer))
328 : {
329 0 : CPLError(CE_Failure, CPLE_AppDefined,
330 : "Failed to create elevation max field '%s'",
331 0 : psOptions->osElevAttribMax.c_str());
332 0 : return CE_Failure;
333 : }
334 : }
335 : }
336 :
337 : /* -------------------------------------------------------------------- */
338 : /* Invoke. */
339 : /* -------------------------------------------------------------------- */
340 43 : int iIDField = OGR_FD_GetFieldIndex(hFeatureDefn, "ID");
341 43 : int iElevField = (psOptions->osElevAttrib.empty())
342 43 : ? -1
343 24 : : OGR_FD_GetFieldIndex(
344 24 : hFeatureDefn, psOptions->osElevAttrib.c_str());
345 :
346 : int iElevFieldMin =
347 43 : (psOptions->osElevAttribMin.empty())
348 43 : ? -1
349 13 : : OGR_FD_GetFieldIndex(hFeatureDefn,
350 13 : psOptions->osElevAttribMin.c_str());
351 :
352 : int iElevFieldMax =
353 43 : (psOptions->osElevAttribMax.empty())
354 43 : ? -1
355 13 : : OGR_FD_GetFieldIndex(hFeatureDefn,
356 13 : psOptions->osElevAttribMax.c_str());
357 :
358 43 : if (!psOptions->aosFixedLevels.empty())
359 : {
360 17 : std::string values = "FIXED_LEVELS=";
361 62 : for (size_t i = 0; i < psOptions->aosFixedLevels.size(); i++)
362 : {
363 45 : if (i == psOptions->aosFixedLevels.size() - 1)
364 : {
365 17 : values = values + psOptions->aosFixedLevels[i];
366 : }
367 : else
368 : {
369 28 : values = values + psOptions->aosFixedLevels[i] + ",";
370 : }
371 : }
372 17 : *ppapszStringOptions =
373 17 : CSLAddString(*ppapszStringOptions, values.c_str());
374 : }
375 :
376 43 : if (psOptions->dfExpBase != 0.0)
377 : {
378 6 : *ppapszStringOptions = CSLAppendPrintf(
379 : *ppapszStringOptions, "LEVEL_EXP_BASE=%f", psOptions->dfExpBase);
380 : }
381 37 : else if (psOptions->dfInterval != 0.0)
382 : {
383 29 : *ppapszStringOptions = CSLAppendPrintf(
384 : *ppapszStringOptions, "LEVEL_INTERVAL=%f", psOptions->dfInterval);
385 : }
386 :
387 43 : if (psOptions->dfOffset != 0.0)
388 : {
389 4 : *ppapszStringOptions = CSLAppendPrintf(
390 : *ppapszStringOptions, "LEVEL_BASE=%f", psOptions->dfOffset);
391 : }
392 :
393 43 : if (psOptions->bNoDataSet)
394 : {
395 11 : *ppapszStringOptions = CSLAppendPrintf(
396 : *ppapszStringOptions, "NODATA=%.19g", psOptions->dfNoData);
397 : }
398 43 : if (iIDField != -1)
399 : {
400 43 : *ppapszStringOptions =
401 43 : CSLAppendPrintf(*ppapszStringOptions, "ID_FIELD=%d", iIDField);
402 : }
403 43 : if (iElevField != -1)
404 : {
405 24 : *ppapszStringOptions =
406 24 : CSLAppendPrintf(*ppapszStringOptions, "ELEV_FIELD=%d", iElevField);
407 : }
408 43 : if (iElevFieldMin != -1)
409 : {
410 13 : *ppapszStringOptions = CSLAppendPrintf(
411 : *ppapszStringOptions, "ELEV_FIELD_MIN=%d", iElevFieldMin);
412 : }
413 43 : if (iElevFieldMax != -1)
414 : {
415 13 : *ppapszStringOptions = CSLAppendPrintf(
416 : *ppapszStringOptions, "ELEV_FIELD_MAX=%d", iElevFieldMax);
417 : }
418 43 : if (psOptions->bPolygonize)
419 : {
420 13 : *ppapszStringOptions =
421 13 : CSLAppendPrintf(*ppapszStringOptions, "POLYGONIZE=YES");
422 : }
423 43 : if (psOptions->nGroupTransactions)
424 : {
425 3 : *ppapszStringOptions = CSLAppendPrintf(*ppapszStringOptions,
426 : "COMMIT_INTERVAL=" CPL_FRMT_GIB,
427 : psOptions->nGroupTransactions);
428 : }
429 :
430 43 : return CE_None;
431 : }
432 :
433 : ///@endcond
434 :
435 : /************************************************************************/
436 : /* GDALContourAppOptionsGetParser() */
437 : /************************************************************************/
438 :
439 : static std::unique_ptr<GDALArgumentParser>
440 45 : GDALContourAppOptionsGetParser(GDALContourOptions *psOptions,
441 : GDALContourOptionsForBinary *psOptionsForBinary)
442 : {
443 :
444 : auto argParser = std::make_unique<GDALArgumentParser>(
445 45 : "gdal_contour", /* bForBinary=*/psOptionsForBinary != nullptr);
446 :
447 45 : argParser->add_description(_("Creates contour lines from a raster file."));
448 45 : argParser->add_epilog(_(
449 : "For more details, consult the full documentation for the gdal_contour "
450 45 : "utility: http://gdal.org/gdal_contour.html"));
451 :
452 45 : argParser->add_extra_usage_hint(
453 : _("One of -i, -fl or -e must be specified."));
454 :
455 45 : argParser->add_argument("-b")
456 90 : .metavar("<name>")
457 45 : .default_value(1)
458 45 : .nargs(1)
459 45 : .scan<'i', int>()
460 45 : .store_into(psOptions->nBand)
461 45 : .help(_("Select an input band band containing the DEM data."));
462 :
463 45 : argParser->add_argument("-a")
464 90 : .metavar("<name>")
465 45 : .store_into(psOptions->osElevAttrib)
466 : .help(_("Provides a name for the attribute in which to put the "
467 45 : "elevation."));
468 :
469 45 : argParser->add_argument("-amin")
470 90 : .metavar("<name>")
471 45 : .store_into(psOptions->osElevAttribMin)
472 : .help(_("Provides a name for the attribute in which to put the minimum "
473 45 : "elevation."));
474 :
475 45 : argParser->add_argument("-amax")
476 90 : .metavar("<name>")
477 45 : .store_into(psOptions->osElevAttribMax)
478 : .help(_("Provides a name for the attribute in which to put the maximum "
479 45 : "elevation."));
480 :
481 45 : argParser->add_argument("-3d")
482 45 : .flag()
483 45 : .store_into(psOptions->b3D)
484 45 : .help(_("Force production of 3D vectors instead of 2D."));
485 :
486 45 : argParser->add_argument("-inodata")
487 45 : .flag()
488 45 : .store_into(psOptions->bIgnoreNoData)
489 : .help(_("Ignore any nodata value implied in the dataset - treat all "
490 45 : "values as valid."));
491 :
492 45 : argParser->add_argument("-snodata")
493 90 : .metavar("<value>")
494 45 : .scan<'g', double>()
495 : .action(
496 2 : [psOptions](const auto &d)
497 : {
498 1 : psOptions->bNoDataSet = true;
499 1 : psOptions->dfNoData = CPLAtofM(d.c_str());
500 45 : })
501 45 : .help(_("Input pixel value to treat as \"nodata\"."));
502 :
503 45 : auto &group = argParser->add_mutually_exclusive_group();
504 :
505 45 : group.add_argument("-i")
506 90 : .metavar("<interval>")
507 45 : .scan<'g', double>()
508 45 : .store_into(psOptions->dfInterval)
509 45 : .help(_("Elevation interval between contours."));
510 :
511 45 : group.add_argument("-e")
512 90 : .metavar("<base>")
513 45 : .scan<'g', double>()
514 45 : .store_into(psOptions->dfExpBase)
515 : .help(_("Generate levels on an exponential scale: base ^ k, for k an "
516 45 : "integer."));
517 :
518 : // Dealt manually as argparse::nargs_pattern::at_least_one is problematic
519 90 : argParser->add_argument("-fl").metavar("<level>").help(
520 45 : _("Name one or more \"fixed levels\" to extract."));
521 :
522 45 : argParser->add_argument("-off")
523 90 : .metavar("<offset>")
524 45 : .scan<'g', double>()
525 45 : .store_into(psOptions->dfOffset)
526 45 : .help(_("Offset from zero relative to which to interpret intervals."));
527 :
528 45 : argParser->add_argument("-nln")
529 90 : .metavar("<name>")
530 45 : .store_into(psOptions->osNewLayerName)
531 : .help(_("Provide a name for the output vector layer. Defaults to "
532 45 : "\"contour\"."));
533 :
534 45 : argParser->add_argument("-p")
535 45 : .flag()
536 45 : .store_into(psOptions->bPolygonize)
537 45 : .help(_("Generate contour polygons instead of lines."));
538 :
539 45 : argParser->add_argument("-gt")
540 90 : .metavar("<n>|unlimited")
541 : .action(
542 30 : [psOptions](const std::string &s)
543 : {
544 15 : if (EQUAL(s.c_str(), "unlimited"))
545 1 : psOptions->nGroupTransactions = -1;
546 : else
547 14 : psOptions->nGroupTransactions = atoi(s.c_str());
548 45 : })
549 45 : .help(_("Group <n> features per transaction."));
550 :
551 : // Written that way so that in library mode, users can still use the -q
552 : // switch, even if it has no effect
553 : argParser->add_quiet_argument(
554 45 : psOptionsForBinary ? &(psOptionsForBinary->bQuiet) : nullptr);
555 :
556 45 : if (psOptionsForBinary)
557 : {
558 : argParser->add_open_options_argument(
559 32 : psOptionsForBinary->aosOpenOptions);
560 :
561 32 : argParser->add_argument("src_filename")
562 32 : .store_into(psOptions->osSrcDataSource)
563 32 : .help("The source raster file.");
564 :
565 : argParser->add_dataset_creation_options_argument(
566 32 : psOptions->aosOpenOptions);
567 :
568 32 : argParser->add_argument("dst_filename")
569 32 : .store_into(psOptions->osDestDataSource)
570 32 : .help("The destination vector file.");
571 :
572 32 : argParser->add_output_format_argument(psOptions->osFormat);
573 :
574 32 : argParser->add_creation_options_argument(psOptions->aosCreationOptions);
575 :
576 : argParser->add_layer_creation_options_argument(
577 32 : psOptions->aosLayerCreationOptions);
578 : }
579 :
580 45 : return argParser;
581 : }
582 :
583 : /************************************************************************/
584 : /* GDALContourGetParserUsage() */
585 : /************************************************************************/
586 :
587 1 : std::string GDALContourGetParserUsage()
588 : {
589 : try
590 : {
591 2 : GDALContourOptions sOptions;
592 2 : auto argParser = GDALContourAppOptionsGetParser(&sOptions, nullptr);
593 1 : return argParser->usage();
594 : }
595 0 : catch (const std::exception &err)
596 : {
597 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
598 0 : err.what());
599 0 : return std::string();
600 : }
601 : }
602 :
603 : /************************************************************************/
604 : /* GDALContourOptionsNew() */
605 : /************************************************************************/
606 :
607 : /**
608 : * Create a new GDALContourOptions object.
609 : *
610 : * @param papszArgv the command line arguments.
611 : * @param psOptionsForBinary the options for binary.
612 : *
613 : * @return the new GDALContourOptions object.
614 : */
615 : GDALContourOptions *
616 44 : GDALContourOptionsNew(char **papszArgv,
617 : GDALContourOptionsForBinary *psOptionsForBinary)
618 : {
619 :
620 88 : auto psOptions = std::make_unique<GDALContourOptions>();
621 :
622 : /*-------------------------------------------------------------------- */
623 : /* Parse arguments. */
624 : /*-------------------------------------------------------------------- */
625 :
626 88 : CPLStringList aosArgv;
627 :
628 : /* -------------------------------------------------------------------- */
629 : /* Pre-processing for custom syntax that ArgumentParser does not */
630 : /* support. */
631 : /* -------------------------------------------------------------------- */
632 44 : const int argc = CSLCount(papszArgv);
633 :
634 : /* -------------------------------------------------------------------- */
635 : /* Pre-processing for custom syntax that ArgumentParser does not */
636 : /* support. */
637 : /* -------------------------------------------------------------------- */
638 444 : for (int i = 0; i < argc && papszArgv != nullptr && papszArgv[i] != nullptr;
639 : i++)
640 : {
641 : // argparser is confused by arguments that have at_least_one
642 : // cardinality, if they immediately precede positional arguments.
643 400 : if (EQUAL(papszArgv[i], "-fl") && papszArgv[i + 1])
644 : {
645 23 : if (strchr(papszArgv[i + 1], ' '))
646 : {
647 : const CPLStringList aosTokens(
648 0 : CSLTokenizeString(papszArgv[i + 1]));
649 0 : for (const char *pszToken : aosTokens)
650 : {
651 : // Handle min/max special values
652 0 : if (EQUAL(pszToken, "MIN"))
653 : {
654 0 : psOptions->aosFixedLevels.push_back("MIN");
655 : }
656 0 : else if (EQUAL(pszToken, "MAX"))
657 : {
658 0 : psOptions->aosFixedLevels.push_back("MAX");
659 : }
660 : else
661 : {
662 0 : psOptions->aosFixedLevels.push_back(
663 0 : std::to_string(CPLAtof(pszToken)));
664 : }
665 : }
666 0 : i += 1;
667 : }
668 : else
669 : {
670 68 : auto isNumericOrMinMax = [](const char *pszArg) -> bool
671 : {
672 68 : if (EQUAL(pszArg, "MIN") || EQUAL(pszArg, "MAX"))
673 4 : return true;
674 64 : char *pszEnd = nullptr;
675 64 : CPLStrtod(pszArg, &pszEnd);
676 64 : return pszEnd != nullptr && pszEnd[0] == '\0';
677 : };
678 :
679 68 : while (i < argc - 1 && isNumericOrMinMax(papszArgv[i + 1]))
680 : {
681 45 : if (EQUAL(papszArgv[i + 1], "MIN"))
682 : {
683 2 : psOptions->aosFixedLevels.push_back("MIN");
684 : }
685 43 : else if (EQUAL(papszArgv[i + 1], "MAX"))
686 : {
687 2 : psOptions->aosFixedLevels.push_back("MAX");
688 : }
689 : else
690 : {
691 82 : psOptions->aosFixedLevels.push_back(
692 82 : std::to_string(CPLAtof(papszArgv[i + 1])));
693 : }
694 45 : i += 1;
695 : }
696 23 : }
697 : }
698 : else
699 : {
700 377 : aosArgv.AddString(papszArgv[i]);
701 : }
702 : }
703 :
704 : try
705 : {
706 :
707 : auto argParser =
708 88 : GDALContourAppOptionsGetParser(psOptions.get(), psOptionsForBinary);
709 44 : argParser->parse_args_without_binary_name(aosArgv.List());
710 :
711 46 : if (psOptions->dfInterval == 0.0 && psOptions->aosFixedLevels.empty() &&
712 2 : psOptions->dfExpBase == 0.0)
713 : {
714 1 : fprintf(stderr, "%s\n", argParser->usage().c_str());
715 1 : return nullptr;
716 : }
717 :
718 43 : if (psOptions->osSrcDataSource.find("/vsistdout/") !=
719 86 : std::string::npos ||
720 43 : psOptions->osDestDataSource.find("/vsistdout/") !=
721 : std::string::npos)
722 : {
723 0 : psOptions->bQuiet = true;
724 : }
725 :
726 43 : if (psOptionsForBinary)
727 : {
728 31 : psOptionsForBinary->bQuiet = psOptions->bQuiet;
729 31 : psOptionsForBinary->osDestDataSource = psOptions->osDestDataSource;
730 31 : psOptionsForBinary->osSrcDataSource = psOptions->osSrcDataSource;
731 31 : psOptionsForBinary->aosOpenOptions = psOptions->aosOpenOptions;
732 : }
733 :
734 43 : return psOptions.release();
735 : }
736 0 : catch (const std::exception &e)
737 : {
738 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
739 0 : return nullptr;
740 : }
741 : }
742 :
743 : /************************************************************************/
744 : /* GDALContourOptionsFree() */
745 : /************************************************************************/
746 :
747 : /**
748 : * Free a GDALContourOptions object.
749 : *
750 : * @param psOptions the GDALContourOptions object to free.
751 : */
752 43 : void GDALContourOptionsFree(GDALContourOptions *psOptions)
753 : {
754 43 : delete psOptions;
755 43 : }
|