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