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