Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL DEM Utilities
4 : * Purpose:
5 : * Authors: Matthew Perry, perrygeo at gmail.com
6 : * Even Rouault, even dot rouault at spatialys.com
7 : * Howard Butler, hobu.inc at gmail.com
8 : * Chris Yesson, chris dot yesson at ioz dot ac dot uk
9 : *
10 : ******************************************************************************
11 : * Copyright (c) 2006, 2009 Matthew Perry
12 : * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
13 : * Portions derived from GRASS 4.1 (public domain) See
14 : * http://trac.osgeo.org/gdal/ticket/2975 for more information regarding
15 : * history of this code
16 : *
17 : * SPDX-License-Identifier: MIT
18 : ****************************************************************************
19 : *
20 : * Slope and aspect calculations based on original method for GRASS GIS 4.1
21 : * by Michael Shapiro, U.S.Army Construction Engineering Research Laboratory
22 : * Olga Waupotitsch, U.S.Army Construction Engineering Research Laboratory
23 : * Marjorie Larson, U.S.Army Construction Engineering Research Laboratory
24 : * as found in GRASS's r.slope.aspect module.
25 : *
26 : * Horn's formula is used to find the first order derivatives in x and y
27 : *directions for slope and aspect calculations: Horn, B. K. P. (1981). "Hill
28 : *Shading and the Reflectance Map", Proceedings of the IEEE, 69(1):14-47.
29 : *
30 : * Other reference :
31 : * Burrough, P.A. and McDonell, R.A., 1998. Principles of Geographical
32 : *Information Systems. p. 190.
33 : *
34 : * Shaded relief based on original method for GRASS GIS 4.1 by Jim Westervelt,
35 : * U.S. Army Construction Engineering Research Laboratory
36 : * as found in GRASS's r.shaded.relief (formerly shade.rel.sh) module.
37 : * ref: "r.mapcalc: An Algebra for GIS and Image Processing",
38 : * by Michael Shapiro and Jim Westervelt, U.S. Army Construction Engineering
39 : * Research Laboratory (March/1991)
40 : *
41 : * Color table of named colors and lookup code derived from
42 : *src/libes/gis/named_colr.c of GRASS 4.1
43 : *
44 : * TRI -
45 : * For bathymetric use cases, implements
46 : * Terrain Ruggedness Index is as described in Wilson et al. (2007)
47 : * this is based on the method of Valentine et al. (2004)
48 : *
49 : * For terrestrial use cases, implements
50 : * Riley, S.J., De Gloria, S.D., Elliot, R. (1999): A Terrain Ruggedness
51 : * that Quantifies Topographic Heterogeneity. Intermountain Journal of Science,
52 : *Vol.5, No.1-4, pp.23-27
53 : *
54 : *
55 : * TPI - Topographic Position Index follows the description in
56 : * Wilson et al. (2007), following Weiss (2001). The radius is fixed
57 : * at 1 cell width/height
58 : *
59 : * Roughness - follows the definition in Wilson et al. (2007), which follows
60 : * Dartnell (2000).
61 : *
62 : * References for TRI/TPI/Roughness:
63 : * Dartnell, P. 2000. Applying Remote Sensing Techniques to map Seafloor
64 : * Geology/Habitat Relationships. Masters Thesis, San Francisco State
65 : * University, pp. 108.
66 : * Valentine, P. C., S. J. Fuller, L. A. Scully. 2004. Terrain Ruggedness
67 : * Analysis and Distribution of Boulder Ridges in the Stellwagen Bank National
68 : * Marine Sanctuary Region (poster). Galway, Ireland: 5th International
69 : * Symposium on Marine Geological and Biological Habitat Mapping (GeoHAB),
70 : * May 2004.
71 : * Weiss, A. D. 2001. Topographic Positions and Landforms Analysis (poster),
72 : * ESRI International User Conference, July 2001. San Diego, CA: ESRI.
73 : * Wilson, M. F. J.; O'Connell, B.; Brown, C.; Guinan, J. C. & Grehan, A. J.
74 : * Multiscale terrain analysis of multibeam bathymetry data for habitat mapping
75 : * on the continental slope Marine Geodesy, 2007, 30, 3-35
76 : ****************************************************************************/
77 :
78 : // Include before others for mingw for VSIStatBufL
79 : #include "cpl_conv.h"
80 :
81 : #include "cpl_port.h"
82 : #include "gdal_utils.h"
83 : #include "gdal_utils_priv.h"
84 : #include "commonutils.h"
85 : #include "gdalargumentparser.h"
86 :
87 : #include <cassert>
88 : #include <cfloat>
89 : #include <cmath>
90 : #include <cstdio>
91 : #include <cstdlib>
92 : #include <cstring>
93 : #if HAVE_SYS_STAT_H
94 : #include <sys/stat.h>
95 : #endif
96 :
97 : #include <algorithm>
98 : #include <cmath>
99 : #include <limits>
100 :
101 : #include "cpl_error.h"
102 : #include "cpl_float.h"
103 : #include "cpl_progress.h"
104 : #include "cpl_string.h"
105 : #include "cpl_vsi.h"
106 : #include "cpl_vsi_virtual.h"
107 : #include "gdal.h"
108 : #include "gdal_priv.h"
109 :
110 : #if defined(__SSE2__) || defined(_M_X64)
111 : #define HAVE_16_SSE_REG
112 : #define HAVE_SSE2
113 : #include "emmintrin.h"
114 : #endif
115 :
116 : static const double kdfDegreesToRadians = M_PI / 180.0;
117 : static const double kdfRadiansToDegrees = 180.0 / M_PI;
118 :
119 : typedef enum
120 : {
121 : COLOR_SELECTION_INTERPOLATE,
122 : COLOR_SELECTION_NEAREST_ENTRY,
123 : COLOR_SELECTION_EXACT_ENTRY
124 : } ColorSelectionMode;
125 :
126 : namespace gdal::GDALDEM
127 : {
128 : enum class GradientAlg
129 : {
130 : HORN,
131 : ZEVENBERGEN_THORNE,
132 : };
133 :
134 : enum class TRIAlg
135 : {
136 : WILSON,
137 : RILEY,
138 : };
139 : } // namespace gdal::GDALDEM
140 :
141 : using namespace gdal::GDALDEM;
142 :
143 : struct GDALDEMProcessingOptions
144 : {
145 : /*! output format. Use the short format name. */
146 : std::string osFormat{};
147 :
148 : /*! the progress function to use */
149 : GDALProgressFunc pfnProgress = nullptr;
150 :
151 : /*! pointer to the progress data variable */
152 : void *pProgressData = nullptr;
153 :
154 : double z = 1.0;
155 : double scale = 1.0;
156 : double az = 315.0;
157 : double alt = 45.0;
158 : bool bSlopeFormatUseDegrees =
159 : true; // false = 'percent' or true = 'degrees'
160 : bool bAddAlpha = false;
161 : bool bZeroForFlat = false;
162 : bool bAngleAsAzimuth = true;
163 : ColorSelectionMode eColorSelectionMode = COLOR_SELECTION_INTERPOLATE;
164 : bool bComputeAtEdges = false;
165 : bool bGradientAlgSpecified = false;
166 : GradientAlg eGradientAlg = GradientAlg::HORN;
167 : bool bTRIAlgSpecified = false;
168 : TRIAlg eTRIAlg = TRIAlg::RILEY;
169 : bool bCombined = false;
170 : bool bIgor = false;
171 : bool bMultiDirectional = false;
172 : CPLStringList aosCreationOptions{};
173 : int nBand = 1;
174 : };
175 :
176 : /************************************************************************/
177 : /* ComputeVal() */
178 : /************************************************************************/
179 :
180 : template <class T> struct GDALGeneric3x3ProcessingAlg
181 : {
182 : typedef float (*type)(const T *pafWindow, float fDstNoDataValue,
183 : void *pData);
184 : };
185 :
186 : template <class T> struct GDALGeneric3x3ProcessingAlg_multisample
187 : {
188 : typedef int (*type)(const T *pafThreeLineWin, int nLine1Off, int nLine2Off,
189 : int nLine3Off, int nXSize, void *pData,
190 : float *pafOutputBuf);
191 : };
192 :
193 : template <class T>
194 : static float ComputeVal(bool bSrcHasNoData, T fSrcNoDataValue,
195 : bool bIsSrcNoDataNan, T *afWin, float fDstNoDataValue,
196 : typename GDALGeneric3x3ProcessingAlg<T>::type pfnAlg,
197 : void *pData, bool bComputeAtEdges);
198 :
199 : template <>
200 42973 : float ComputeVal(bool bSrcHasNoData, float fSrcNoDataValue,
201 : bool bIsSrcNoDataNan, float *afWin, float fDstNoDataValue,
202 : GDALGeneric3x3ProcessingAlg<float>::type pfnAlg, void *pData,
203 : bool bComputeAtEdges)
204 : {
205 85946 : if (bSrcHasNoData &&
206 42973 : ((!bIsSrcNoDataNan && ARE_REAL_EQUAL(afWin[4], fSrcNoDataValue)) ||
207 0 : (bIsSrcNoDataNan && std::isnan(afWin[4]))))
208 : {
209 8 : return fDstNoDataValue;
210 : }
211 42965 : else if (bSrcHasNoData)
212 : {
213 429641 : for (int k = 0; k < 9; k++)
214 : {
215 386677 : if ((!bIsSrcNoDataNan &&
216 773354 : ARE_REAL_EQUAL(afWin[k], fSrcNoDataValue)) ||
217 0 : (bIsSrcNoDataNan && std::isnan(afWin[k])))
218 : {
219 9 : if (bComputeAtEdges)
220 8 : afWin[k] = afWin[4];
221 : else
222 1 : return fDstNoDataValue;
223 : }
224 : }
225 : }
226 :
227 42964 : return pfnAlg(afWin, fDstNoDataValue, pData);
228 : }
229 :
230 : template <>
231 274825 : float ComputeVal(bool bSrcHasNoData, GInt32 fSrcNoDataValue,
232 : bool /* bIsSrcNoDataNan */, GInt32 *afWin,
233 : float fDstNoDataValue,
234 : GDALGeneric3x3ProcessingAlg<GInt32>::type pfnAlg, void *pData,
235 : bool bComputeAtEdges)
236 : {
237 274825 : if (bSrcHasNoData && afWin[4] == fSrcNoDataValue)
238 : {
239 584 : return fDstNoDataValue;
240 : }
241 274241 : else if (bSrcHasNoData)
242 : {
243 444161 : for (int k = 0; k < 9; k++)
244 : {
245 399745 : if (afWin[k] == fSrcNoDataValue)
246 : {
247 9 : if (bComputeAtEdges)
248 8 : afWin[k] = afWin[4];
249 : else
250 1 : return fDstNoDataValue;
251 : }
252 : }
253 : }
254 :
255 274240 : return pfnAlg(afWin, fDstNoDataValue, pData);
256 : }
257 :
258 : /************************************************************************/
259 : /* INTERPOL() */
260 : /************************************************************************/
261 :
262 : template <class T>
263 : static T INTERPOL(T a, T b, int bSrcHasNodata, T fSrcNoDataValue);
264 :
265 : template <>
266 1464 : float INTERPOL(float a, float b, int bSrcHasNoData, float fSrcNoDataValue)
267 : {
268 1464 : return ((bSrcHasNoData && (ARE_REAL_EQUAL(a, fSrcNoDataValue) ||
269 1440 : ARE_REAL_EQUAL(b, fSrcNoDataValue)))
270 1488 : ? fSrcNoDataValue
271 1464 : : 2 * (a) - (b));
272 : }
273 :
274 : template <>
275 10104 : GInt32 INTERPOL(GInt32 a, GInt32 b, int bSrcHasNoData, GInt32 fSrcNoDataValue)
276 : {
277 10104 : if (bSrcHasNoData && ((a == fSrcNoDataValue) || (b == fSrcNoDataValue)))
278 24 : return fSrcNoDataValue;
279 10080 : int nVal = 2 * a - b;
280 10080 : if (bSrcHasNoData && fSrcNoDataValue == nVal)
281 0 : return nVal + 1;
282 10080 : return nVal;
283 : }
284 :
285 : /************************************************************************/
286 : /* GDALGeneric3x3Processing() */
287 : /************************************************************************/
288 :
289 : template <class T>
290 37 : static CPLErr GDALGeneric3x3Processing(
291 : GDALRasterBandH hSrcBand, GDALRasterBandH hDstBand,
292 : typename GDALGeneric3x3ProcessingAlg<T>::type pfnAlg,
293 : typename GDALGeneric3x3ProcessingAlg_multisample<T>::type
294 : pfnAlg_multisample,
295 : void *pData, bool bComputeAtEdges, GDALProgressFunc pfnProgress,
296 : void *pProgressData)
297 : {
298 37 : if (pfnProgress == nullptr)
299 31 : pfnProgress = GDALDummyProgress;
300 :
301 : /* -------------------------------------------------------------------- */
302 : /* Initialize progress counter. */
303 : /* -------------------------------------------------------------------- */
304 37 : if (!pfnProgress(0.0, nullptr, pProgressData))
305 : {
306 0 : CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
307 0 : return CE_Failure;
308 : }
309 :
310 37 : const int nXSize = GDALGetRasterBandXSize(hSrcBand);
311 37 : const int nYSize = GDALGetRasterBandYSize(hSrcBand);
312 :
313 : // 1 line destination buffer.
314 : float *pafOutputBuf =
315 37 : static_cast<float *>(VSI_MALLOC2_VERBOSE(sizeof(float), nXSize));
316 : // 3 line rotating source buffer.
317 : T *pafThreeLineWin =
318 37 : static_cast<T *>(VSI_MALLOC2_VERBOSE(3 * sizeof(T), nXSize + 1));
319 37 : if (pafOutputBuf == nullptr || pafThreeLineWin == nullptr)
320 : {
321 0 : VSIFree(pafOutputBuf);
322 0 : VSIFree(pafThreeLineWin);
323 0 : return CE_Failure;
324 : }
325 :
326 : GDALDataType eReadDT;
327 37 : int bSrcHasNoData = FALSE;
328 : const double dfNoDataValue =
329 37 : GDALGetRasterNoDataValue(hSrcBand, &bSrcHasNoData);
330 :
331 37 : int bIsSrcNoDataNan = FALSE;
332 37 : T fSrcNoDataValue = 0;
333 : if (cpl::NumericLimits<T>::is_integer)
334 : {
335 33 : eReadDT = GDT_Int32;
336 33 : if (bSrcHasNoData)
337 : {
338 31 : GDALDataType eSrcDT = GDALGetRasterDataType(hSrcBand);
339 31 : CPLAssert(eSrcDT == GDT_Byte || eSrcDT == GDT_UInt16 ||
340 : eSrcDT == GDT_Int16);
341 31 : const int nMinVal = (eSrcDT == GDT_Byte) ? 0
342 : : (eSrcDT == GDT_UInt16) ? 0
343 : : -32768;
344 31 : const int nMaxVal = (eSrcDT == GDT_Byte) ? 255
345 : : (eSrcDT == GDT_UInt16) ? 65535
346 : : 32767;
347 :
348 31 : if (fabs(dfNoDataValue - floor(dfNoDataValue + 0.5)) < 1e-2 &&
349 31 : dfNoDataValue >= nMinVal && dfNoDataValue <= nMaxVal)
350 : {
351 31 : fSrcNoDataValue = static_cast<T>(floor(dfNoDataValue + 0.5));
352 : }
353 : else
354 : {
355 0 : bSrcHasNoData = FALSE;
356 : }
357 : }
358 : }
359 : else
360 : {
361 4 : eReadDT = GDT_Float32;
362 4 : fSrcNoDataValue = static_cast<T>(dfNoDataValue);
363 4 : bIsSrcNoDataNan = bSrcHasNoData && std::isnan(dfNoDataValue);
364 : }
365 :
366 37 : int bDstHasNoData = FALSE;
367 37 : float fDstNoDataValue =
368 37 : static_cast<float>(GDALGetRasterNoDataValue(hDstBand, &bDstHasNoData));
369 37 : if (!bDstHasNoData)
370 0 : fDstNoDataValue = 0.0;
371 :
372 37 : int nLine1Off = 0;
373 37 : int nLine2Off = nXSize;
374 37 : int nLine3Off = 2 * nXSize;
375 :
376 : // Move a 3x3 pafWindow over each cell
377 : // (where the cell in question is #4)
378 : //
379 : // 0 1 2
380 : // 3 4 5
381 : // 6 7 8
382 :
383 : /* Preload the first 2 lines */
384 :
385 37 : bool abLineHasNoDataValue[3] = {CPL_TO_BOOL(bSrcHasNoData),
386 37 : CPL_TO_BOOL(bSrcHasNoData),
387 37 : CPL_TO_BOOL(bSrcHasNoData)};
388 :
389 : // Create an extra scope for VC12 to ignore i.
390 : {
391 111 : for (int i = 0; i < 2 && i < nYSize; i++)
392 : {
393 148 : if (GDALRasterIO(hSrcBand, GF_Read, 0, i, nXSize, 1,
394 74 : pafThreeLineWin + i * nXSize, nXSize, 1, eReadDT,
395 74 : 0, 0) != CE_None)
396 : {
397 0 : CPLFree(pafOutputBuf);
398 0 : CPLFree(pafThreeLineWin);
399 :
400 0 : return CE_Failure;
401 : }
402 66 : if (cpl::NumericLimits<T>::is_integer && bSrcHasNoData)
403 : {
404 62 : abLineHasNoDataValue[i] = false;
405 4902 : for (int iX = 0; iX < nXSize; iX++)
406 : {
407 4862 : if (pafThreeLineWin[i * nXSize + iX] == fSrcNoDataValue)
408 : {
409 22 : abLineHasNoDataValue[i] = true;
410 22 : break;
411 : }
412 : }
413 : }
414 : }
415 : } // End extra scope for VC12
416 :
417 37 : CPLErr eErr = CE_None;
418 37 : if (bComputeAtEdges && nXSize >= 2 && nYSize >= 2)
419 : {
420 862 : for (int j = 0; j < nXSize; j++)
421 : {
422 853 : int jmin = (j == 0) ? j : j - 1;
423 853 : int jmax = (j == nXSize - 1) ? j : j + 1;
424 :
425 2559 : T afWin[9] = {
426 853 : INTERPOL(pafThreeLineWin[jmin], pafThreeLineWin[nXSize + jmin],
427 : bSrcHasNoData, fSrcNoDataValue),
428 853 : INTERPOL(pafThreeLineWin[j], pafThreeLineWin[nXSize + j],
429 : bSrcHasNoData, fSrcNoDataValue),
430 853 : INTERPOL(pafThreeLineWin[jmax], pafThreeLineWin[nXSize + jmax],
431 : bSrcHasNoData, fSrcNoDataValue),
432 853 : pafThreeLineWin[jmin],
433 853 : pafThreeLineWin[j],
434 853 : pafThreeLineWin[jmax],
435 853 : pafThreeLineWin[nXSize + jmin],
436 853 : pafThreeLineWin[nXSize + j],
437 853 : pafThreeLineWin[nXSize + jmax]};
438 1706 : pafOutputBuf[j] =
439 853 : ComputeVal(CPL_TO_BOOL(bSrcHasNoData), fSrcNoDataValue,
440 853 : CPL_TO_BOOL(bIsSrcNoDataNan), afWin, fDstNoDataValue,
441 : pfnAlg, pData, bComputeAtEdges);
442 : }
443 9 : eErr = GDALRasterIO(hDstBand, GF_Write, 0, 0, nXSize, 1, pafOutputBuf,
444 9 : nXSize, 1, GDT_Float32, 0, 0);
445 : }
446 : else
447 : {
448 : // Exclude the edges
449 2139 : for (int j = 0; j < nXSize; j++)
450 : {
451 2111 : pafOutputBuf[j] = fDstNoDataValue;
452 : }
453 28 : eErr = GDALRasterIO(hDstBand, GF_Write, 0, 0, nXSize, 1, pafOutputBuf,
454 : nXSize, 1, GDT_Float32, 0, 0);
455 :
456 28 : if (eErr == CE_None && nYSize > 1)
457 : {
458 28 : eErr = GDALRasterIO(hDstBand, GF_Write, 0, nYSize - 1, nXSize, 1,
459 : pafOutputBuf, nXSize, 1, GDT_Float32, 0, 0);
460 : }
461 : }
462 37 : if (eErr != CE_None)
463 : {
464 0 : CPLFree(pafOutputBuf);
465 0 : CPLFree(pafThreeLineWin);
466 :
467 0 : return eErr;
468 : }
469 :
470 37 : int i = 1; // Used after for.
471 2927 : for (; i < nYSize - 1; i++)
472 : {
473 : /* Read third line of the line buffer */
474 : eErr =
475 5780 : GDALRasterIO(hSrcBand, GF_Read, 0, i + 1, nXSize, 1,
476 2890 : pafThreeLineWin + nLine3Off, nXSize, 1, eReadDT, 0, 0);
477 2890 : if (eErr != CE_None)
478 : {
479 0 : CPLFree(pafOutputBuf);
480 0 : CPLFree(pafThreeLineWin);
481 :
482 0 : return eErr;
483 : }
484 :
485 : // In case none of the 3 lines have nodata values, then no need to
486 : // check it in ComputeVal()
487 2890 : bool bOneOfThreeLinesHasNoData = CPL_TO_BOOL(bSrcHasNoData);
488 2650 : if (cpl::NumericLimits<T>::is_integer && bSrcHasNoData)
489 : {
490 2454 : bool bLastLineHasNoDataValue = false;
491 2454 : int iX = 0;
492 73854 : for (; iX + 3 < nXSize; iX += 4)
493 : {
494 71472 : if (pafThreeLineWin[nLine3Off + iX] == fSrcNoDataValue ||
495 71400 : pafThreeLineWin[nLine3Off + iX + 1] == fSrcNoDataValue ||
496 71400 : pafThreeLineWin[nLine3Off + iX + 2] == fSrcNoDataValue ||
497 71400 : pafThreeLineWin[nLine3Off + iX + 3] == fSrcNoDataValue)
498 : {
499 72 : bLastLineHasNoDataValue = true;
500 72 : break;
501 : }
502 : }
503 2454 : if (!bLastLineHasNoDataValue)
504 : {
505 4768 : for (; iX < nXSize; iX++)
506 : {
507 2386 : if (pafThreeLineWin[nLine3Off + iX] == fSrcNoDataValue)
508 : {
509 6 : bLastLineHasNoDataValue = true;
510 : }
511 : }
512 : }
513 2454 : abLineHasNoDataValue[nLine3Off / nXSize] = bLastLineHasNoDataValue;
514 :
515 4834 : bOneOfThreeLinesHasNoData = abLineHasNoDataValue[0] ||
516 4834 : abLineHasNoDataValue[1] ||
517 : abLineHasNoDataValue[2];
518 : }
519 :
520 2890 : if (bComputeAtEdges && nXSize >= 2)
521 : {
522 835 : int j = 0;
523 3340 : T afWin[9] = {INTERPOL(pafThreeLineWin[nLine1Off + j],
524 835 : pafThreeLineWin[nLine1Off + j + 1],
525 : bSrcHasNoData, fSrcNoDataValue),
526 835 : pafThreeLineWin[nLine1Off + j],
527 835 : pafThreeLineWin[nLine1Off + j + 1],
528 835 : INTERPOL(pafThreeLineWin[nLine2Off + j],
529 835 : pafThreeLineWin[nLine2Off + j + 1],
530 : bSrcHasNoData, fSrcNoDataValue),
531 835 : pafThreeLineWin[nLine2Off + j],
532 835 : pafThreeLineWin[nLine2Off + j + 1],
533 835 : INTERPOL(pafThreeLineWin[nLine3Off + j],
534 835 : pafThreeLineWin[nLine3Off + j + 1],
535 : bSrcHasNoData, fSrcNoDataValue),
536 835 : pafThreeLineWin[nLine3Off + j],
537 835 : pafThreeLineWin[nLine3Off + j + 1]};
538 :
539 1670 : pafOutputBuf[j] =
540 835 : ComputeVal(CPL_TO_BOOL(bOneOfThreeLinesHasNoData),
541 835 : fSrcNoDataValue, CPL_TO_BOOL(bIsSrcNoDataNan), afWin,
542 835 : fDstNoDataValue, pfnAlg, pData, bComputeAtEdges);
543 : }
544 : else
545 : {
546 : // Exclude the edges
547 2055 : pafOutputBuf[0] = fDstNoDataValue;
548 : }
549 :
550 2890 : int j = 1;
551 2890 : if (pfnAlg_multisample && !bOneOfThreeLinesHasNoData)
552 : {
553 672 : j = pfnAlg_multisample(pafThreeLineWin, nLine1Off, nLine2Off,
554 : nLine3Off, nXSize, pData, pafOutputBuf);
555 : }
556 :
557 260188 : for (; j < nXSize - 1; j++)
558 : {
559 257298 : T afWin[9] = {pafThreeLineWin[nLine1Off + j - 1],
560 257298 : pafThreeLineWin[nLine1Off + j],
561 257298 : pafThreeLineWin[nLine1Off + j + 1],
562 257298 : pafThreeLineWin[nLine2Off + j - 1],
563 257298 : pafThreeLineWin[nLine2Off + j],
564 257298 : pafThreeLineWin[nLine2Off + j + 1],
565 257298 : pafThreeLineWin[nLine3Off + j - 1],
566 257298 : pafThreeLineWin[nLine3Off + j],
567 257298 : pafThreeLineWin[nLine3Off + j + 1]};
568 :
569 514596 : pafOutputBuf[j] =
570 257298 : ComputeVal(CPL_TO_BOOL(bOneOfThreeLinesHasNoData),
571 257298 : fSrcNoDataValue, CPL_TO_BOOL(bIsSrcNoDataNan), afWin,
572 : fDstNoDataValue, pfnAlg, pData, bComputeAtEdges);
573 : }
574 :
575 2890 : if (bComputeAtEdges && nXSize >= 2)
576 : {
577 835 : j = nXSize - 1;
578 :
579 3340 : T afWin[9] = {pafThreeLineWin[nLine1Off + j - 1],
580 835 : pafThreeLineWin[nLine1Off + j],
581 835 : INTERPOL(pafThreeLineWin[nLine1Off + j],
582 835 : pafThreeLineWin[nLine1Off + j - 1],
583 : bSrcHasNoData, fSrcNoDataValue),
584 835 : pafThreeLineWin[nLine2Off + j - 1],
585 835 : pafThreeLineWin[nLine2Off + j],
586 835 : INTERPOL(pafThreeLineWin[nLine2Off + j],
587 835 : pafThreeLineWin[nLine2Off + j - 1],
588 : bSrcHasNoData, fSrcNoDataValue),
589 835 : pafThreeLineWin[nLine3Off + j - 1],
590 835 : pafThreeLineWin[nLine3Off + j],
591 835 : INTERPOL(pafThreeLineWin[nLine3Off + j],
592 835 : pafThreeLineWin[nLine3Off + j - 1],
593 : bSrcHasNoData, fSrcNoDataValue)};
594 :
595 1670 : pafOutputBuf[j] =
596 835 : ComputeVal(CPL_TO_BOOL(bOneOfThreeLinesHasNoData),
597 835 : fSrcNoDataValue, CPL_TO_BOOL(bIsSrcNoDataNan), afWin,
598 835 : fDstNoDataValue, pfnAlg, pData, bComputeAtEdges);
599 : }
600 : else
601 : {
602 : // Exclude the edges
603 2055 : if (nXSize > 1)
604 2055 : pafOutputBuf[nXSize - 1] = fDstNoDataValue;
605 : }
606 :
607 : /* -----------------------------------------
608 : * Write Line to Raster
609 : */
610 2890 : eErr = GDALRasterIO(hDstBand, GF_Write, 0, i, nXSize, 1, pafOutputBuf,
611 : nXSize, 1, GDT_Float32, 0, 0);
612 2890 : if (eErr != CE_None)
613 : {
614 0 : CPLFree(pafOutputBuf);
615 0 : CPLFree(pafThreeLineWin);
616 :
617 0 : return eErr;
618 : }
619 :
620 2890 : if (!pfnProgress(1.0 * (i + 1) / nYSize, nullptr, pProgressData))
621 : {
622 0 : CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
623 0 : eErr = CE_Failure;
624 :
625 0 : CPLFree(pafOutputBuf);
626 0 : CPLFree(pafThreeLineWin);
627 :
628 0 : return eErr;
629 : }
630 :
631 2890 : const int nTemp = nLine1Off;
632 2890 : nLine1Off = nLine2Off;
633 2890 : nLine2Off = nLine3Off;
634 2890 : nLine3Off = nTemp;
635 : }
636 :
637 37 : if (bComputeAtEdges && nXSize >= 2 && nYSize >= 2)
638 : {
639 862 : for (int j = 0; j < nXSize; j++)
640 : {
641 853 : int jmin = (j == 0) ? j : j - 1;
642 853 : int jmax = (j == nXSize - 1) ? j : j + 1;
643 :
644 3412 : T afWin[9] = {
645 853 : pafThreeLineWin[nLine1Off + jmin],
646 853 : pafThreeLineWin[nLine1Off + j],
647 853 : pafThreeLineWin[nLine1Off + jmax],
648 853 : pafThreeLineWin[nLine2Off + jmin],
649 853 : pafThreeLineWin[nLine2Off + j],
650 853 : pafThreeLineWin[nLine2Off + jmax],
651 853 : INTERPOL(pafThreeLineWin[nLine2Off + jmin],
652 853 : pafThreeLineWin[nLine1Off + jmin], bSrcHasNoData,
653 : fSrcNoDataValue),
654 853 : INTERPOL(pafThreeLineWin[nLine2Off + j],
655 853 : pafThreeLineWin[nLine1Off + j], bSrcHasNoData,
656 : fSrcNoDataValue),
657 853 : INTERPOL(pafThreeLineWin[nLine2Off + jmax],
658 853 : pafThreeLineWin[nLine1Off + jmax], bSrcHasNoData,
659 : fSrcNoDataValue),
660 : };
661 :
662 1706 : pafOutputBuf[j] =
663 853 : ComputeVal(CPL_TO_BOOL(bSrcHasNoData), fSrcNoDataValue,
664 853 : CPL_TO_BOOL(bIsSrcNoDataNan), afWin, fDstNoDataValue,
665 : pfnAlg, pData, bComputeAtEdges);
666 : }
667 9 : eErr = GDALRasterIO(hDstBand, GF_Write, 0, i, nXSize, 1, pafOutputBuf,
668 : nXSize, 1, GDT_Float32, 0, 0);
669 9 : if (eErr != CE_None)
670 : {
671 0 : CPLFree(pafOutputBuf);
672 0 : CPLFree(pafThreeLineWin);
673 :
674 0 : return eErr;
675 : }
676 : }
677 :
678 37 : pfnProgress(1.0, nullptr, pProgressData);
679 37 : eErr = CE_None;
680 :
681 37 : CPLFree(pafOutputBuf);
682 37 : CPLFree(pafThreeLineWin);
683 :
684 37 : return eErr;
685 : }
686 :
687 : /************************************************************************/
688 : /* GradientAlg */
689 : /************************************************************************/
690 :
691 : template <class T, GradientAlg alg> struct Gradient
692 : {
693 : static void inline calc(const T *afWin, double inv_ewres, double inv_nsres,
694 : double &x, double &y);
695 : };
696 :
697 : template <class T> struct Gradient<T, GradientAlg::HORN>
698 : {
699 42963 : static void calc(const T *afWin, double inv_ewres, double inv_nsres,
700 : double &x, double &y)
701 : {
702 42963 : x = ((afWin[0] + afWin[3] + afWin[3] + afWin[6]) -
703 42963 : (afWin[2] + afWin[5] + afWin[5] + afWin[8])) *
704 : inv_ewres;
705 :
706 42963 : y = ((afWin[6] + afWin[7] + afWin[7] + afWin[8]) -
707 42963 : (afWin[0] + afWin[1] + afWin[1] + afWin[2])) *
708 : inv_nsres;
709 42963 : }
710 : };
711 :
712 : template <class T> struct Gradient<T, GradientAlg::ZEVENBERGEN_THORNE>
713 : {
714 42963 : static void calc(const T *afWin, double inv_ewres, double inv_nsres,
715 : double &x, double &y)
716 : {
717 42963 : x = (afWin[3] - afWin[5]) * inv_ewres;
718 42963 : y = (afWin[7] - afWin[1]) * inv_nsres;
719 42963 : }
720 : };
721 :
722 : /************************************************************************/
723 : /* GDALHillshade() */
724 : /************************************************************************/
725 :
726 : typedef struct
727 : {
728 : double inv_nsres;
729 : double inv_ewres;
730 : double sin_altRadians;
731 : double cos_alt_mul_z;
732 : double azRadians;
733 : double cos_az_mul_cos_alt_mul_z;
734 : double sin_az_mul_cos_alt_mul_z;
735 : double square_z;
736 : double sin_altRadians_mul_254;
737 : double cos_az_mul_cos_alt_mul_z_mul_254;
738 : double sin_az_mul_cos_alt_mul_z_mul_254;
739 :
740 : double square_z_mul_square_inv_res;
741 : double cos_az_mul_cos_alt_mul_z_mul_254_mul_inv_res;
742 : double sin_az_mul_cos_alt_mul_z_mul_254_mul_inv_res;
743 : double z_scaled;
744 : } GDALHillshadeAlgData;
745 :
746 : /* Unoptimized formulas are :
747 : x = psData->z*((afWin[0] + afWin[3] + afWin[3] + afWin[6]) -
748 : (afWin[2] + afWin[5] + afWin[5] + afWin[8])) /
749 : (8.0 * psData->ewres * psData->scale);
750 :
751 : y = psData->z*((afWin[6] + afWin[7] + afWin[7] + afWin[8]) -
752 : (afWin[0] + afWin[1] + afWin[1] + afWin[2])) /
753 : (8.0 * psData->nsres * psData->scale);
754 :
755 : slope = atan(sqrt(x*x + y*y));
756 :
757 : aspect = atan2(y,x);
758 :
759 : cang = sin(alt) * cos(slope) +
760 : cos(alt) * sin(slope) *
761 : cos(az - M_PI/2 - aspect);
762 :
763 : We can avoid a lot of trigonometric computations:
764 :
765 : since cos(atan(x)) = 1 / sqrt(1+x^2)
766 : ==> cos(slope) = 1 / sqrt(1+ x*x+y*y)
767 :
768 : and sin(atan(x)) = x / sqrt(1+x^2)
769 : ==> sin(slope) = sqrt(x*x + y*y) / sqrt(1+ x*x+y*y)
770 :
771 : and cos(az - M_PI/2 - aspect)
772 : = cos(-az + M_PI/2 + aspect)
773 : = cos(M_PI/2 - (az - aspect))
774 : = sin(az - aspect)
775 : = -sin(aspect-az)
776 :
777 : ==> cang = (sin(alt) - cos(alt) * sqrt(x*x + y*y) * sin(aspect-as)) /
778 : sqrt(1+ x*x+y*y)
779 :
780 : But:
781 : sin(aspect - az) = sin(aspect)*cos(az) - cos(aspect)*sin(az))
782 :
783 : and as sin(aspect)=sin(atan2(y,x)) = y / sqrt(xx_plus_yy)
784 : and cos(aspect)=cos(atan2(y,x)) = x / sqrt(xx_plus_yy)
785 :
786 : sin(aspect - az) = (y * cos(az) - x * sin(az)) / sqrt(xx_plus_yy)
787 :
788 : so we get a final formula with just one transcendental function
789 : (reciprocal of square root):
790 :
791 : cang = (psData->sin_altRadians -
792 : (y * psData->cos_az_mul_cos_alt_mul_z -
793 : x * psData->sin_az_mul_cos_alt_mul_z)) /
794 : sqrt(1 + psData->square_z * xx_plus_yy);
795 : */
796 :
797 : #ifdef HAVE_SSE2
798 166017 : inline double ApproxADivByInvSqrtB(double a, double b)
799 : {
800 166017 : __m128d regB = _mm_load_sd(&b);
801 332034 : __m128d regB_half = _mm_mul_sd(regB, _mm_set1_pd(0.5));
802 : // Compute rough approximation of 1 / sqrt(b) with _mm_rsqrt_ss
803 : regB =
804 664068 : _mm_cvtss_sd(regB, _mm_rsqrt_ss(_mm_cvtsd_ss(_mm_setzero_ps(), regB)));
805 : // And perform one step of Newton-Raphson approximation to improve it
806 : // approx_inv_sqrt_x = approx_inv_sqrt_x*(1.5 -
807 : // 0.5*x*approx_inv_sqrt_x*approx_inv_sqrt_x);
808 830085 : regB = _mm_mul_sd(
809 : regB, _mm_sub_sd(_mm_set1_pd(1.5),
810 : _mm_mul_sd(regB_half, _mm_mul_sd(regB, regB))));
811 : double dOut;
812 : _mm_store_sd(&dOut, regB);
813 166017 : return a * dOut;
814 : }
815 : #else
816 : inline double ApproxADivByInvSqrtB(double a, double b)
817 : {
818 : return a / sqrt(b);
819 : }
820 : #endif
821 :
822 58564 : static double NormalizeAngle(double angle, double normalizer)
823 : {
824 58564 : angle = std::fmod(angle, normalizer);
825 58564 : if (angle < 0)
826 41924 : angle = normalizer + angle;
827 :
828 58564 : return angle;
829 : }
830 :
831 29282 : static double DifferenceBetweenAngles(double angle1, double angle2,
832 : double normalizer)
833 : {
834 : double diff =
835 29282 : NormalizeAngle(angle1, normalizer) - NormalizeAngle(angle2, normalizer);
836 29282 : diff = std::abs(diff);
837 29282 : if (diff > normalizer / 2)
838 7060 : diff = normalizer - diff;
839 29282 : return diff;
840 : }
841 :
842 : template <class T, GradientAlg alg>
843 29282 : static float GDALHillshadeIgorAlg(const T *afWin, float /*fDstNoDataValue*/,
844 : void *pData)
845 : {
846 29282 : GDALHillshadeAlgData *psData = static_cast<GDALHillshadeAlgData *>(pData);
847 :
848 : double slopeDegrees;
849 : if (alg == GradientAlg::HORN)
850 : {
851 14641 : const double dx = ((afWin[0] + afWin[3] + afWin[3] + afWin[6]) -
852 14641 : (afWin[2] + afWin[5] + afWin[5] + afWin[8])) *
853 14641 : psData->inv_ewres;
854 :
855 14641 : const double dy = ((afWin[6] + afWin[7] + afWin[7] + afWin[8]) -
856 14641 : (afWin[0] + afWin[1] + afWin[1] + afWin[2])) *
857 14641 : psData->inv_nsres;
858 :
859 14641 : const double key = (dx * dx + dy * dy);
860 14641 : slopeDegrees = atan(sqrt(key) * psData->z_scaled) * kdfRadiansToDegrees;
861 : }
862 : else // ZEVENBERGEN_THORNE
863 : {
864 14641 : const double dx = (afWin[3] - afWin[5]) * psData->inv_ewres;
865 14641 : const double dy = (afWin[7] - afWin[1]) * psData->inv_nsres;
866 14641 : const double key = dx * dx + dy * dy;
867 :
868 14641 : slopeDegrees = atan(sqrt(key) * psData->z_scaled) * kdfRadiansToDegrees;
869 : }
870 :
871 : double aspect;
872 : if (alg == GradientAlg::HORN)
873 : {
874 14641 : const double dx = ((afWin[2] + afWin[5] + afWin[5] + afWin[8]) -
875 14641 : (afWin[0] + afWin[3] + afWin[3] + afWin[6]));
876 :
877 14641 : const double dy2 = ((afWin[6] + afWin[7] + afWin[7] + afWin[8]) -
878 14641 : (afWin[0] + afWin[1] + afWin[1] + afWin[2]));
879 :
880 14641 : aspect = atan2(dy2, -dx);
881 : }
882 : else // ZEVENBERGEN_THORNE
883 : {
884 14641 : const double dx = afWin[5] - afWin[3];
885 14641 : const double dy = afWin[7] - afWin[1];
886 14641 : aspect = atan2(dy, -dx);
887 : }
888 :
889 29282 : double slopeStrength = slopeDegrees / 90;
890 :
891 29282 : double aspectDiff = DifferenceBetweenAngles(
892 29282 : aspect, M_PI * 3 / 2 - psData->azRadians, M_PI * 2);
893 :
894 29282 : double aspectStrength = 1 - aspectDiff / M_PI;
895 :
896 29282 : double shadowness = 1.0 - slopeStrength * aspectStrength;
897 :
898 29282 : return static_cast<float>(255.0 * shadowness);
899 : }
900 :
901 : template <class T, GradientAlg alg>
902 14161 : static float GDALHillshadeAlg(const T *afWin, float /*fDstNoDataValue*/,
903 : void *pData)
904 : {
905 14161 : GDALHillshadeAlgData *psData = static_cast<GDALHillshadeAlgData *>(pData);
906 :
907 : // First Slope ...
908 : double x, y;
909 14161 : Gradient<T, alg>::calc(afWin, psData->inv_ewres, psData->inv_nsres, x, y);
910 :
911 14161 : const double xx_plus_yy = x * x + y * y;
912 :
913 : // ... then the shade value
914 : const double cang_mul_254 =
915 28322 : ApproxADivByInvSqrtB(psData->sin_altRadians_mul_254 -
916 14161 : (y * psData->cos_az_mul_cos_alt_mul_z_mul_254 -
917 14161 : x * psData->sin_az_mul_cos_alt_mul_z_mul_254),
918 14161 : 1 + psData->square_z * xx_plus_yy);
919 :
920 14161 : const double cang = cang_mul_254 <= 0.0 ? 1.0 : 1.0 + cang_mul_254;
921 :
922 14161 : return static_cast<float>(cang);
923 : }
924 :
925 : template <class T>
926 88708 : static float GDALHillshadeAlg_same_res(const T *afWin,
927 : float /*fDstNoDataValue*/, void *pData)
928 : {
929 88708 : GDALHillshadeAlgData *psData = static_cast<GDALHillshadeAlgData *>(pData);
930 :
931 : // First Slope ...
932 : /*x = (afWin[0] + afWin[3] + afWin[3] + afWin[6]) -
933 : (afWin[2] + afWin[5] + afWin[5] + afWin[8]);
934 :
935 : y = (afWin[0] + afWin[1] + afWin[1] + afWin[2]) -
936 : (afWin[6] + afWin[7] + afWin[7] + afWin[8]);*/
937 :
938 88708 : T accX = afWin[0] - afWin[8];
939 88708 : const T six_minus_two = afWin[6] - afWin[2];
940 88708 : T accY = accX;
941 88708 : const T three_minus_five = afWin[3] - afWin[5];
942 88708 : const T one_minus_seven = afWin[1] - afWin[7];
943 88708 : accX += three_minus_five;
944 88708 : accY += one_minus_seven;
945 88708 : accX += three_minus_five;
946 88708 : accY += one_minus_seven;
947 88708 : accX += six_minus_two;
948 88708 : accY -= six_minus_two;
949 88708 : const double x = accX;
950 88708 : const double y = accY;
951 :
952 88708 : const double xx_plus_yy = x * x + y * y;
953 :
954 : // ... then the shade value
955 177416 : const double cang_mul_254 = ApproxADivByInvSqrtB(
956 88708 : psData->sin_altRadians_mul_254 +
957 88708 : (x * psData->sin_az_mul_cos_alt_mul_z_mul_254_mul_inv_res +
958 88708 : y * psData->cos_az_mul_cos_alt_mul_z_mul_254_mul_inv_res),
959 88708 : 1 + psData->square_z_mul_square_inv_res * xx_plus_yy);
960 :
961 88708 : const double cang = cang_mul_254 <= 0.0 ? 1.0 : 1.0 + cang_mul_254;
962 :
963 88708 : return static_cast<float>(cang);
964 : }
965 :
966 : #ifdef HAVE_16_SSE_REG
967 : template <class T>
968 : static int
969 672 : GDALHillshadeAlg_same_res_multisample(const T *pafThreeLineWin, int nLine1Off,
970 : int nLine2Off, int nLine3Off, int nXSize,
971 : void *pData, float *pafOutputBuf)
972 : {
973 : // Only valid for T == int
974 :
975 672 : GDALHillshadeAlgData *psData = static_cast<GDALHillshadeAlgData *>(pData);
976 : const __m128d reg_fact_x =
977 672 : _mm_load1_pd(&(psData->sin_az_mul_cos_alt_mul_z_mul_254_mul_inv_res));
978 : const __m128d reg_fact_y =
979 672 : _mm_load1_pd(&(psData->cos_az_mul_cos_alt_mul_z_mul_254_mul_inv_res));
980 : const __m128d reg_constant_num =
981 672 : _mm_load1_pd(&(psData->sin_altRadians_mul_254));
982 : const __m128d reg_constant_denom =
983 1344 : _mm_load1_pd(&(psData->square_z_mul_square_inv_res));
984 672 : const __m128d reg_half = _mm_set1_pd(0.5);
985 672 : const __m128d reg_one = _mm_add_pd(reg_half, reg_half);
986 672 : const __m128 reg_one_float = _mm_set1_ps(1);
987 :
988 672 : int j = 1; // Used after for.
989 19180 : for (; j < nXSize - 4; j += 4)
990 : {
991 18508 : const T *firstLine = pafThreeLineWin + nLine1Off + j - 1;
992 18508 : const T *secondLine = pafThreeLineWin + nLine2Off + j - 1;
993 18508 : const T *thirdLine = pafThreeLineWin + nLine3Off + j - 1;
994 :
995 : __m128i firstLine0 =
996 18508 : _mm_loadu_si128(reinterpret_cast<__m128i const *>(firstLine));
997 : __m128i firstLine1 =
998 18508 : _mm_loadu_si128(reinterpret_cast<__m128i const *>(firstLine + 1));
999 : __m128i firstLine2 =
1000 37016 : _mm_loadu_si128(reinterpret_cast<__m128i const *>(firstLine + 2));
1001 : __m128i thirdLine0 =
1002 18508 : _mm_loadu_si128(reinterpret_cast<__m128i const *>(thirdLine));
1003 : __m128i thirdLine1 =
1004 18508 : _mm_loadu_si128(reinterpret_cast<__m128i const *>(thirdLine + 1));
1005 : __m128i thirdLine2 =
1006 37016 : _mm_loadu_si128(reinterpret_cast<__m128i const *>(thirdLine + 2));
1007 18508 : __m128i accX = _mm_sub_epi32(firstLine0, thirdLine2);
1008 18508 : const __m128i six_minus_two = _mm_sub_epi32(thirdLine0, firstLine2);
1009 18508 : __m128i accY = accX;
1010 55524 : const __m128i three_minus_five = _mm_sub_epi32(
1011 : _mm_loadu_si128(reinterpret_cast<__m128i const *>(secondLine)),
1012 18508 : _mm_loadu_si128(reinterpret_cast<__m128i const *>(secondLine + 2)));
1013 18508 : const __m128i one_minus_seven = _mm_sub_epi32(firstLine1, thirdLine1);
1014 18508 : accX = _mm_add_epi32(accX, three_minus_five);
1015 18508 : accY = _mm_add_epi32(accY, one_minus_seven);
1016 18508 : accX = _mm_add_epi32(accX, three_minus_five);
1017 18508 : accY = _mm_add_epi32(accY, one_minus_seven);
1018 18508 : accX = _mm_add_epi32(accX, six_minus_two);
1019 18508 : accY = _mm_sub_epi32(accY, six_minus_two);
1020 :
1021 18508 : __m128d reg_x0 = _mm_cvtepi32_pd(accX);
1022 37016 : __m128d reg_x1 = _mm_cvtepi32_pd(_mm_srli_si128(accX, 8));
1023 18508 : __m128d reg_y0 = _mm_cvtepi32_pd(accY);
1024 37016 : __m128d reg_y1 = _mm_cvtepi32_pd(_mm_srli_si128(accY, 8));
1025 : __m128d reg_xx_plus_yy0 =
1026 55524 : _mm_add_pd(_mm_mul_pd(reg_x0, reg_x0), _mm_mul_pd(reg_y0, reg_y0));
1027 : __m128d reg_xx_plus_yy1 =
1028 55524 : _mm_add_pd(_mm_mul_pd(reg_x1, reg_x1), _mm_mul_pd(reg_y1, reg_y1));
1029 :
1030 74032 : __m128d reg_numerator0 = _mm_add_pd(
1031 : reg_constant_num, _mm_add_pd(_mm_mul_pd(reg_fact_x, reg_x0),
1032 : _mm_mul_pd(reg_fact_y, reg_y0)));
1033 74032 : __m128d reg_numerator1 = _mm_add_pd(
1034 : reg_constant_num, _mm_add_pd(_mm_mul_pd(reg_fact_x, reg_x1),
1035 : _mm_mul_pd(reg_fact_y, reg_y1)));
1036 37016 : __m128d reg_denominator0 = _mm_add_pd(
1037 : reg_one, _mm_mul_pd(reg_constant_denom, reg_xx_plus_yy0));
1038 18508 : __m128d reg_denominator1 = _mm_add_pd(
1039 : reg_one, _mm_mul_pd(reg_constant_denom, reg_xx_plus_yy1));
1040 :
1041 18508 : __m128d regB0 = reg_denominator0;
1042 18508 : __m128d regB1 = reg_denominator1;
1043 18508 : __m128d regB0_half = _mm_mul_pd(regB0, reg_half);
1044 18508 : __m128d regB1_half = _mm_mul_pd(regB1, reg_half);
1045 : // Compute rough approximation of 1 / sqrt(b) with _mm_rsqrt_ps
1046 55524 : regB0 = _mm_cvtps_pd(_mm_rsqrt_ps(_mm_cvtpd_ps(regB0)));
1047 55524 : regB1 = _mm_cvtps_pd(_mm_rsqrt_ps(_mm_cvtpd_ps(regB1)));
1048 : // And perform one step of Newton-Raphson approximation to improve it
1049 : // approx_inv_sqrt_x = approx_inv_sqrt_x*(1.5 -
1050 : // 0.5*x*approx_inv_sqrt_x*approx_inv_sqrt_x);
1051 18508 : const __m128d reg_one_and_a_half = _mm_add_pd(reg_one, reg_half);
1052 74032 : regB0 = _mm_mul_pd(
1053 : regB0,
1054 : _mm_sub_pd(reg_one_and_a_half,
1055 : _mm_mul_pd(regB0_half, _mm_mul_pd(regB0, regB0))));
1056 74032 : regB1 = _mm_mul_pd(
1057 : regB1,
1058 : _mm_sub_pd(reg_one_and_a_half,
1059 : _mm_mul_pd(regB1_half, _mm_mul_pd(regB1, regB1))));
1060 18508 : reg_numerator0 = _mm_mul_pd(reg_numerator0, regB0);
1061 18508 : reg_numerator1 = _mm_mul_pd(reg_numerator1, regB1);
1062 :
1063 111048 : __m128 res = _mm_castsi128_ps(
1064 : _mm_unpacklo_epi64(_mm_castps_si128(_mm_cvtpd_ps(reg_numerator0)),
1065 : _mm_castps_si128(_mm_cvtpd_ps(reg_numerator1))));
1066 18508 : res = _mm_add_ps(res, reg_one_float);
1067 18508 : res = _mm_max_ps(res, reg_one_float);
1068 :
1069 18508 : _mm_storeu_ps(pafOutputBuf + j, res);
1070 : }
1071 672 : return j;
1072 : }
1073 : #endif
1074 :
1075 : static const double INV_SQUARE_OF_HALF_PI = 1.0 / ((M_PI * M_PI) / 4);
1076 :
1077 : template <class T, GradientAlg alg>
1078 42483 : static float GDALHillshadeCombinedAlg(const T *afWin, float /*fDstNoDataValue*/,
1079 : void *pData)
1080 : {
1081 42483 : GDALHillshadeAlgData *psData = static_cast<GDALHillshadeAlgData *>(pData);
1082 :
1083 : // First Slope ...
1084 : double x, y;
1085 42483 : Gradient<T, alg>::calc(afWin, psData->inv_ewres, psData->inv_nsres, x, y);
1086 :
1087 42483 : const double xx_plus_yy = x * x + y * y;
1088 :
1089 42483 : const double slope = xx_plus_yy * psData->square_z;
1090 :
1091 : // ... then the shade value
1092 84966 : double cang = acos(ApproxADivByInvSqrtB(
1093 42483 : psData->sin_altRadians - (y * psData->cos_az_mul_cos_alt_mul_z -
1094 42483 : x * psData->sin_az_mul_cos_alt_mul_z),
1095 : 1 + slope));
1096 :
1097 : // combined shading
1098 42483 : cang = 1 - cang * atan(sqrt(slope)) * INV_SQUARE_OF_HALF_PI;
1099 :
1100 42483 : const float fcang =
1101 42483 : cang <= 0.0 ? 1.0f : static_cast<float>(1.0 + (254.0 * cang));
1102 :
1103 42483 : return fcang;
1104 : }
1105 :
1106 31 : static void *GDALCreateHillshadeData(double *adfGeoTransform, double z,
1107 : double scale, double alt, double az,
1108 : GradientAlg eAlg)
1109 : {
1110 : GDALHillshadeAlgData *pData = static_cast<GDALHillshadeAlgData *>(
1111 31 : CPLCalloc(1, sizeof(GDALHillshadeAlgData)));
1112 :
1113 31 : pData->inv_nsres = 1.0 / adfGeoTransform[5];
1114 31 : pData->inv_ewres = 1.0 / adfGeoTransform[1];
1115 31 : pData->sin_altRadians = sin(alt * kdfDegreesToRadians);
1116 31 : pData->azRadians = az * kdfDegreesToRadians;
1117 31 : pData->z_scaled =
1118 31 : z / ((eAlg == GradientAlg::ZEVENBERGEN_THORNE ? 2 : 8) * scale);
1119 31 : pData->cos_alt_mul_z = cos(alt * kdfDegreesToRadians) * pData->z_scaled;
1120 31 : pData->cos_az_mul_cos_alt_mul_z =
1121 31 : cos(pData->azRadians) * pData->cos_alt_mul_z;
1122 31 : pData->sin_az_mul_cos_alt_mul_z =
1123 31 : sin(pData->azRadians) * pData->cos_alt_mul_z;
1124 31 : pData->square_z = pData->z_scaled * pData->z_scaled;
1125 :
1126 31 : pData->sin_altRadians_mul_254 = 254.0 * pData->sin_altRadians;
1127 31 : pData->cos_az_mul_cos_alt_mul_z_mul_254 =
1128 31 : 254.0 * pData->cos_az_mul_cos_alt_mul_z;
1129 31 : pData->sin_az_mul_cos_alt_mul_z_mul_254 =
1130 31 : 254.0 * pData->sin_az_mul_cos_alt_mul_z;
1131 :
1132 31 : if (adfGeoTransform[1] == -adfGeoTransform[5])
1133 : {
1134 31 : pData->square_z_mul_square_inv_res =
1135 31 : pData->square_z * pData->inv_ewres * pData->inv_ewres;
1136 31 : pData->cos_az_mul_cos_alt_mul_z_mul_254_mul_inv_res =
1137 31 : pData->cos_az_mul_cos_alt_mul_z_mul_254 * -pData->inv_ewres;
1138 31 : pData->sin_az_mul_cos_alt_mul_z_mul_254_mul_inv_res =
1139 31 : pData->sin_az_mul_cos_alt_mul_z_mul_254 * pData->inv_ewres;
1140 : }
1141 :
1142 31 : return pData;
1143 : }
1144 :
1145 : /************************************************************************/
1146 : /* GDALHillshadeMultiDirectional() */
1147 : /************************************************************************/
1148 :
1149 : typedef struct
1150 : {
1151 : double inv_nsres;
1152 : double inv_ewres;
1153 : double square_z;
1154 : double sin_altRadians_mul_127;
1155 : double sin_altRadians_mul_254;
1156 :
1157 : double cos_alt_mul_z_mul_127;
1158 : double cos225_az_mul_cos_alt_mul_z_mul_127;
1159 :
1160 : } GDALHillshadeMultiDirectionalAlgData;
1161 :
1162 : template <class T, GradientAlg alg>
1163 29282 : static float GDALHillshadeMultiDirectionalAlg(const T *afWin,
1164 : float /*fDstNoDataValue*/,
1165 : void *pData)
1166 : {
1167 29282 : const GDALHillshadeMultiDirectionalAlgData *psData =
1168 : static_cast<const GDALHillshadeMultiDirectionalAlgData *>(pData);
1169 :
1170 : // First Slope ...
1171 : double x, y;
1172 29282 : Gradient<T, alg>::calc(afWin, psData->inv_ewres, psData->inv_nsres, x, y);
1173 :
1174 : // See http://pubs.usgs.gov/of/1992/of92-422/of92-422.pdf
1175 : // W225 = sin^2(aspect - 225) = 0.5 * (1 - 2 * sin(aspect) * cos(aspect))
1176 : // W270 = sin^2(aspect - 270) = cos^2(aspect)
1177 : // W315 = sin^2(aspect - 315) = 0.5 * (1 + 2 * sin(aspect) * cos(aspect))
1178 : // W360 = sin^2(aspect - 360) = sin^2(aspect)
1179 : // hillshade= 0.5 * (W225 * hillshade(az=225) +
1180 : // W270 * hillshade(az=270) +
1181 : // W315 * hillshade(az=315) +
1182 : // W360 * hillshade(az=360))
1183 :
1184 29282 : const double xx = x * x;
1185 29282 : const double yy = y * y;
1186 29282 : const double xx_plus_yy = xx + yy;
1187 29282 : if (xx_plus_yy == 0.0)
1188 8617 : return static_cast<float>(1.0 + psData->sin_altRadians_mul_254);
1189 :
1190 : // ... then the shade value from different azimuth
1191 20665 : double val225_mul_127 =
1192 20665 : psData->sin_altRadians_mul_127 +
1193 20665 : (x - y) * psData->cos225_az_mul_cos_alt_mul_z_mul_127;
1194 20665 : val225_mul_127 = (val225_mul_127 <= 0.0) ? 0.0 : val225_mul_127;
1195 20665 : double val270_mul_127 =
1196 20665 : psData->sin_altRadians_mul_127 - x * psData->cos_alt_mul_z_mul_127;
1197 20665 : val270_mul_127 = (val270_mul_127 <= 0.0) ? 0.0 : val270_mul_127;
1198 20665 : double val315_mul_127 =
1199 20665 : psData->sin_altRadians_mul_127 +
1200 20665 : (x + y) * psData->cos225_az_mul_cos_alt_mul_z_mul_127;
1201 20665 : val315_mul_127 = (val315_mul_127 <= 0.0) ? 0.0 : val315_mul_127;
1202 20665 : double val360_mul_127 =
1203 20665 : psData->sin_altRadians_mul_127 - y * psData->cos_alt_mul_z_mul_127;
1204 20665 : val360_mul_127 = (val360_mul_127 <= 0.0) ? 0.0 : val360_mul_127;
1205 :
1206 : // ... then the weighted shading
1207 20665 : const double weight_225 = 0.5 * xx_plus_yy - x * y;
1208 20665 : const double weight_270 = xx;
1209 20665 : const double weight_315 = xx_plus_yy - weight_225;
1210 20665 : const double weight_360 = yy;
1211 41330 : const double cang_mul_127 = ApproxADivByInvSqrtB(
1212 20665 : (weight_225 * val225_mul_127 + weight_270 * val270_mul_127 +
1213 20665 : weight_315 * val315_mul_127 + weight_360 * val360_mul_127) /
1214 : xx_plus_yy,
1215 20665 : 1 + psData->square_z * xx_plus_yy);
1216 :
1217 20665 : const double cang = 1.0 + cang_mul_127;
1218 :
1219 20665 : return static_cast<float>(cang);
1220 : }
1221 :
1222 2 : static void *GDALCreateHillshadeMultiDirectionalData(double *adfGeoTransform,
1223 : double z, double scale,
1224 : double alt,
1225 : GradientAlg eAlg)
1226 : {
1227 : GDALHillshadeMultiDirectionalAlgData *pData =
1228 : static_cast<GDALHillshadeMultiDirectionalAlgData *>(
1229 2 : CPLCalloc(1, sizeof(GDALHillshadeMultiDirectionalAlgData)));
1230 :
1231 2 : pData->inv_nsres = 1.0 / adfGeoTransform[5];
1232 2 : pData->inv_ewres = 1.0 / adfGeoTransform[1];
1233 2 : const double z_scaled =
1234 2 : z / ((eAlg == GradientAlg::ZEVENBERGEN_THORNE ? 2 : 8) * scale);
1235 2 : const double cos_alt_mul_z = cos(alt * kdfDegreesToRadians) * z_scaled;
1236 2 : pData->square_z = z_scaled * z_scaled;
1237 :
1238 2 : pData->sin_altRadians_mul_127 = 127.0 * sin(alt * kdfDegreesToRadians);
1239 2 : pData->sin_altRadians_mul_254 = 254.0 * sin(alt * kdfDegreesToRadians);
1240 2 : pData->cos_alt_mul_z_mul_127 = 127.0 * cos_alt_mul_z;
1241 2 : pData->cos225_az_mul_cos_alt_mul_z_mul_127 =
1242 2 : 127.0 * cos(225 * kdfDegreesToRadians) * cos_alt_mul_z;
1243 :
1244 2 : return pData;
1245 : }
1246 :
1247 : /************************************************************************/
1248 : /* GDALSlope() */
1249 : /************************************************************************/
1250 :
1251 : typedef struct
1252 : {
1253 : double nsres;
1254 : double ewres;
1255 : double scale;
1256 : int slopeFormat;
1257 : } GDALSlopeAlgData;
1258 :
1259 : template <class T>
1260 14161 : static float GDALSlopeHornAlg(const T *afWin, float /*fDstNoDataValue*/,
1261 : void *pData)
1262 : {
1263 14161 : const GDALSlopeAlgData *psData =
1264 : static_cast<const GDALSlopeAlgData *>(pData);
1265 :
1266 14161 : const double dx = ((afWin[0] + afWin[3] + afWin[3] + afWin[6]) -
1267 14161 : (afWin[2] + afWin[5] + afWin[5] + afWin[8])) /
1268 14161 : psData->ewres;
1269 :
1270 14161 : const double dy = ((afWin[6] + afWin[7] + afWin[7] + afWin[8]) -
1271 14161 : (afWin[0] + afWin[1] + afWin[1] + afWin[2])) /
1272 14161 : psData->nsres;
1273 :
1274 14161 : const double key = (dx * dx + dy * dy);
1275 :
1276 14161 : if (psData->slopeFormat == 1)
1277 14161 : return static_cast<float>(atan(sqrt(key) / (8 * psData->scale)) *
1278 14161 : kdfRadiansToDegrees);
1279 :
1280 0 : return static_cast<float>(100 * (sqrt(key) / (8 * psData->scale)));
1281 : }
1282 :
1283 : template <class T>
1284 14161 : static float GDALSlopeZevenbergenThorneAlg(const T *afWin,
1285 : float /*fDstNoDataValue*/,
1286 : void *pData)
1287 : {
1288 14161 : const GDALSlopeAlgData *psData =
1289 : static_cast<const GDALSlopeAlgData *>(pData);
1290 :
1291 14161 : const double dx = (afWin[3] - afWin[5]) / psData->ewres;
1292 14161 : const double dy = (afWin[7] - afWin[1]) / psData->nsres;
1293 14161 : const double key = dx * dx + dy * dy;
1294 :
1295 14161 : if (psData->slopeFormat == 1)
1296 14161 : return static_cast<float>(atan(sqrt(key) / (2 * psData->scale)) *
1297 14161 : kdfRadiansToDegrees);
1298 :
1299 0 : return static_cast<float>(100 * (sqrt(key) / (2 * psData->scale)));
1300 : }
1301 :
1302 2 : static void *GDALCreateSlopeData(double *adfGeoTransform, double scale,
1303 : int slopeFormat)
1304 : {
1305 : GDALSlopeAlgData *pData =
1306 2 : static_cast<GDALSlopeAlgData *>(CPLMalloc(sizeof(GDALSlopeAlgData)));
1307 :
1308 2 : pData->nsres = adfGeoTransform[5];
1309 2 : pData->ewres = adfGeoTransform[1];
1310 2 : pData->scale = scale;
1311 2 : pData->slopeFormat = slopeFormat;
1312 2 : return pData;
1313 : }
1314 :
1315 : /************************************************************************/
1316 : /* GDALAspect() */
1317 : /************************************************************************/
1318 :
1319 : typedef struct
1320 : {
1321 : bool bAngleAsAzimuth;
1322 : } GDALAspectAlgData;
1323 :
1324 : template <class T>
1325 14161 : static float GDALAspectAlg(const T *afWin, float fDstNoDataValue, void *pData)
1326 : {
1327 14161 : const GDALAspectAlgData *psData =
1328 : static_cast<const GDALAspectAlgData *>(pData);
1329 :
1330 14161 : const double dx = ((afWin[2] + afWin[5] + afWin[5] + afWin[8]) -
1331 14161 : (afWin[0] + afWin[3] + afWin[3] + afWin[6]));
1332 :
1333 14161 : const double dy = ((afWin[6] + afWin[7] + afWin[7] + afWin[8]) -
1334 14161 : (afWin[0] + afWin[1] + afWin[1] + afWin[2]));
1335 :
1336 14161 : float aspect = static_cast<float>(atan2(dy, -dx) / kdfDegreesToRadians);
1337 :
1338 14161 : if (dx == 0 && dy == 0)
1339 : {
1340 : /* Flat area */
1341 4183 : aspect = fDstNoDataValue;
1342 : }
1343 9978 : else if (psData->bAngleAsAzimuth)
1344 : {
1345 9978 : if (aspect > 90.0f)
1346 1243 : aspect = 450.0f - aspect;
1347 : else
1348 8735 : aspect = 90.0f - aspect;
1349 : }
1350 : else
1351 : {
1352 0 : if (aspect < 0)
1353 0 : aspect += 360.0f;
1354 : }
1355 :
1356 14161 : if (aspect == 360.0f)
1357 0 : aspect = 0.0;
1358 :
1359 14161 : return aspect;
1360 : }
1361 :
1362 : template <class T>
1363 14161 : static float GDALAspectZevenbergenThorneAlg(const T *afWin,
1364 : float fDstNoDataValue, void *pData)
1365 : {
1366 14161 : const GDALAspectAlgData *psData =
1367 : static_cast<const GDALAspectAlgData *>(pData);
1368 :
1369 14161 : const double dx = afWin[5] - afWin[3];
1370 14161 : const double dy = afWin[7] - afWin[1];
1371 14161 : float aspect = static_cast<float>(atan2(dy, -dx) / kdfDegreesToRadians);
1372 14161 : if (dx == 0 && dy == 0)
1373 : {
1374 : /* Flat area */
1375 4302 : aspect = fDstNoDataValue;
1376 : }
1377 9859 : else if (psData->bAngleAsAzimuth)
1378 : {
1379 9859 : if (aspect > 90.0f)
1380 1233 : aspect = 450.0f - aspect;
1381 : else
1382 8626 : aspect = 90.0f - aspect;
1383 : }
1384 : else
1385 : {
1386 0 : if (aspect < 0)
1387 0 : aspect += 360.0f;
1388 : }
1389 :
1390 14161 : if (aspect == 360.0f)
1391 0 : aspect = 0.0;
1392 :
1393 14161 : return aspect;
1394 : }
1395 :
1396 2 : static void *GDALCreateAspectData(bool bAngleAsAzimuth)
1397 : {
1398 : GDALAspectAlgData *pData =
1399 2 : static_cast<GDALAspectAlgData *>(CPLMalloc(sizeof(GDALAspectAlgData)));
1400 :
1401 2 : pData->bAngleAsAzimuth = bAngleAsAzimuth;
1402 2 : return pData;
1403 : }
1404 :
1405 : /************************************************************************/
1406 : /* GDALColorRelief() */
1407 : /************************************************************************/
1408 :
1409 : typedef struct
1410 : {
1411 : double dfVal;
1412 : int nR;
1413 : int nG;
1414 : int nB;
1415 : int nA;
1416 : } ColorAssociation;
1417 :
1418 329 : static int GDALColorReliefSortColors(const ColorAssociation &pA,
1419 : const ColorAssociation &pB)
1420 : {
1421 : /* Sort NaN in first position */
1422 657 : return (std::isnan(pA.dfVal) && !std::isnan(pB.dfVal)) ||
1423 657 : pA.dfVal < pB.dfVal;
1424 : }
1425 :
1426 : static void
1427 30 : GDALColorReliefProcessColors(std::vector<ColorAssociation> &asColorAssociation,
1428 : int bSrcHasNoData, double dfSrcNoDataValue,
1429 : ColorSelectionMode eColorSelectionMode)
1430 : {
1431 30 : std::stable_sort(asColorAssociation.begin(), asColorAssociation.end(),
1432 : GDALColorReliefSortColors);
1433 :
1434 30 : size_t nRepeatedEntryIndex = 0;
1435 30 : const size_t nInitialSize = asColorAssociation.size();
1436 159 : for (size_t i = 1; i < nInitialSize; ++i)
1437 : {
1438 129 : const ColorAssociation *pPrevious = &asColorAssociation[i - 1];
1439 129 : const ColorAssociation *pCurrent = &asColorAssociation[i];
1440 :
1441 : // NaN comparison is always false, so it handles itself
1442 129 : if (eColorSelectionMode != COLOR_SELECTION_EXACT_ENTRY &&
1443 111 : bSrcHasNoData && pCurrent->dfVal == dfSrcNoDataValue)
1444 : {
1445 : // Check if there is enough distance between the nodata value and
1446 : // its predecessor.
1447 6 : const double dfNewValue = std::nextafter(
1448 6 : pCurrent->dfVal, -std::numeric_limits<double>::infinity());
1449 6 : if (dfNewValue > pPrevious->dfVal)
1450 : {
1451 : // add one just below the nodata value
1452 6 : ColorAssociation sNew = *pPrevious;
1453 6 : sNew.dfVal = dfNewValue;
1454 6 : asColorAssociation.push_back(std::move(sNew));
1455 6 : }
1456 : }
1457 123 : else if (eColorSelectionMode != COLOR_SELECTION_EXACT_ENTRY &&
1458 105 : bSrcHasNoData && pPrevious->dfVal == dfSrcNoDataValue)
1459 : {
1460 : // Check if there is enough distance between the nodata value and
1461 : // its successor.
1462 7 : const double dfNewValue = std::nextafter(
1463 7 : pPrevious->dfVal, std::numeric_limits<double>::infinity());
1464 7 : if (dfNewValue < pCurrent->dfVal)
1465 : {
1466 : // add one just above the nodata value
1467 7 : ColorAssociation sNew = *pCurrent;
1468 7 : sNew.dfVal = dfNewValue;
1469 7 : asColorAssociation.push_back(std::move(sNew));
1470 7 : }
1471 : }
1472 116 : else if (nRepeatedEntryIndex == 0 &&
1473 114 : pCurrent->dfVal == pPrevious->dfVal)
1474 : {
1475 : // second of a series of equivalent entries
1476 2 : nRepeatedEntryIndex = i;
1477 : }
1478 114 : else if (nRepeatedEntryIndex != 0 &&
1479 2 : pCurrent->dfVal != pPrevious->dfVal)
1480 : {
1481 : // Get the distance between the predecessor and successor of the
1482 : // equivalent entries.
1483 2 : double dfTotalDist = 0.0;
1484 2 : double dfLeftDist = 0.0;
1485 2 : if (nRepeatedEntryIndex >= 2)
1486 : {
1487 : const ColorAssociation *pLower =
1488 2 : &asColorAssociation[nRepeatedEntryIndex - 2];
1489 2 : dfTotalDist = pCurrent->dfVal - pLower->dfVal;
1490 2 : dfLeftDist = pPrevious->dfVal - pLower->dfVal;
1491 : }
1492 : else
1493 : {
1494 0 : dfTotalDist = pCurrent->dfVal - pPrevious->dfVal;
1495 : }
1496 :
1497 : // check if this distance is enough
1498 2 : const size_t nEquivalentCount = i - nRepeatedEntryIndex + 1;
1499 2 : if (dfTotalDist >
1500 2 : std::abs(pPrevious->dfVal) * nEquivalentCount * DBL_EPSILON)
1501 : {
1502 : // balance the alterations
1503 2 : double dfMultiplier =
1504 2 : 0.5 - double(nEquivalentCount) * dfLeftDist / dfTotalDist;
1505 6 : for (auto j = nRepeatedEntryIndex - 1; j < i; ++j)
1506 : {
1507 8 : asColorAssociation[j].dfVal +=
1508 4 : (std::abs(pPrevious->dfVal) * dfMultiplier) *
1509 : DBL_EPSILON;
1510 4 : dfMultiplier += 1.0;
1511 : }
1512 : }
1513 : else
1514 : {
1515 : // Fallback to the old behavior: keep equivalent entries as
1516 : // they are.
1517 : }
1518 :
1519 2 : nRepeatedEntryIndex = 0;
1520 : }
1521 : }
1522 :
1523 30 : if (nInitialSize != asColorAssociation.size())
1524 : {
1525 11 : std::stable_sort(asColorAssociation.begin(), asColorAssociation.end(),
1526 : GDALColorReliefSortColors);
1527 : }
1528 30 : }
1529 :
1530 : static bool
1531 236058 : GDALColorReliefGetRGBA(const std::vector<ColorAssociation> &asColorAssociation,
1532 : double dfVal, ColorSelectionMode eColorSelectionMode,
1533 : int *pnR, int *pnG, int *pnB, int *pnA)
1534 : {
1535 236058 : CPLAssert(!asColorAssociation.empty());
1536 :
1537 236058 : size_t lower = 0;
1538 :
1539 : // Special case for NaN
1540 236058 : if (std::isnan(asColorAssociation[0].dfVal))
1541 : {
1542 4 : if (std::isnan(dfVal))
1543 : {
1544 1 : *pnR = asColorAssociation[0].nR;
1545 1 : *pnG = asColorAssociation[0].nG;
1546 1 : *pnB = asColorAssociation[0].nB;
1547 1 : *pnA = asColorAssociation[0].nA;
1548 1 : return true;
1549 : }
1550 : else
1551 : {
1552 3 : lower = 1;
1553 : }
1554 : }
1555 :
1556 : // Find the index of the first element in the LUT input array that
1557 : // is not smaller than the dfVal value.
1558 236057 : size_t i = 0;
1559 236057 : size_t upper = asColorAssociation.size() - 1;
1560 : while (true)
1561 : {
1562 764367 : const size_t mid = (lower + upper) / 2;
1563 764367 : if (upper - lower <= 1)
1564 : {
1565 236057 : if (dfVal <= asColorAssociation[lower].dfVal)
1566 12 : i = lower;
1567 236045 : else if (dfVal <= asColorAssociation[upper].dfVal)
1568 235035 : i = upper;
1569 : else
1570 1010 : i = upper + 1;
1571 236057 : break;
1572 : }
1573 528310 : else if (asColorAssociation[mid].dfVal >= dfVal)
1574 : {
1575 310726 : upper = mid;
1576 : }
1577 : else
1578 : {
1579 217584 : lower = mid;
1580 : }
1581 528310 : }
1582 :
1583 236057 : if (i == 0)
1584 : {
1585 11 : if (eColorSelectionMode == COLOR_SELECTION_EXACT_ENTRY &&
1586 2 : asColorAssociation[0].dfVal != dfVal)
1587 : {
1588 0 : *pnR = 0;
1589 0 : *pnG = 0;
1590 0 : *pnB = 0;
1591 0 : *pnA = 0;
1592 0 : return false;
1593 : }
1594 : else
1595 : {
1596 9 : *pnR = asColorAssociation[0].nR;
1597 9 : *pnG = asColorAssociation[0].nG;
1598 9 : *pnB = asColorAssociation[0].nB;
1599 9 : *pnA = asColorAssociation[0].nA;
1600 9 : return true;
1601 : }
1602 : }
1603 236048 : else if (i == asColorAssociation.size())
1604 : {
1605 1262 : if (eColorSelectionMode == COLOR_SELECTION_EXACT_ENTRY &&
1606 252 : asColorAssociation[i - 1].dfVal != dfVal)
1607 : {
1608 252 : *pnR = 0;
1609 252 : *pnG = 0;
1610 252 : *pnB = 0;
1611 252 : *pnA = 0;
1612 252 : return false;
1613 : }
1614 : else
1615 : {
1616 758 : *pnR = asColorAssociation[i - 1].nR;
1617 758 : *pnG = asColorAssociation[i - 1].nG;
1618 758 : *pnB = asColorAssociation[i - 1].nB;
1619 758 : *pnA = asColorAssociation[i - 1].nA;
1620 758 : return true;
1621 : }
1622 : }
1623 : else
1624 : {
1625 235038 : if (asColorAssociation[i - 1].dfVal == dfVal)
1626 : {
1627 0 : *pnR = asColorAssociation[i - 1].nR;
1628 0 : *pnG = asColorAssociation[i - 1].nG;
1629 0 : *pnB = asColorAssociation[i - 1].nB;
1630 0 : *pnA = asColorAssociation[i - 1].nA;
1631 0 : return true;
1632 : }
1633 :
1634 235038 : if (asColorAssociation[i].dfVal == dfVal)
1635 : {
1636 74839 : *pnR = asColorAssociation[i].nR;
1637 74839 : *pnG = asColorAssociation[i].nG;
1638 74839 : *pnB = asColorAssociation[i].nB;
1639 74839 : *pnA = asColorAssociation[i].nA;
1640 74839 : return true;
1641 : }
1642 :
1643 160199 : if (eColorSelectionMode == COLOR_SELECTION_EXACT_ENTRY)
1644 : {
1645 10217 : *pnR = 0;
1646 10217 : *pnG = 0;
1647 10217 : *pnB = 0;
1648 10217 : *pnA = 0;
1649 10217 : return false;
1650 : }
1651 :
1652 170164 : if (eColorSelectionMode == COLOR_SELECTION_NEAREST_ENTRY &&
1653 20182 : asColorAssociation[i - 1].dfVal != dfVal)
1654 : {
1655 20182 : const size_t index = (dfVal - asColorAssociation[i - 1].dfVal <
1656 20182 : asColorAssociation[i].dfVal - dfVal)
1657 20182 : ? i - 1
1658 20182 : : i;
1659 20182 : *pnR = asColorAssociation[index].nR;
1660 20182 : *pnG = asColorAssociation[index].nG;
1661 20182 : *pnB = asColorAssociation[index].nB;
1662 20182 : *pnA = asColorAssociation[index].nA;
1663 20182 : return true;
1664 : }
1665 :
1666 129800 : if (std::isnan(asColorAssociation[i - 1].dfVal))
1667 : {
1668 0 : *pnR = asColorAssociation[i].nR;
1669 0 : *pnG = asColorAssociation[i].nG;
1670 0 : *pnB = asColorAssociation[i].nB;
1671 0 : *pnA = asColorAssociation[i].nA;
1672 0 : return true;
1673 : }
1674 :
1675 : const double dfRatio =
1676 129800 : (dfVal - asColorAssociation[i - 1].dfVal) /
1677 129800 : (asColorAssociation[i].dfVal - asColorAssociation[i - 1].dfVal);
1678 519200 : const auto LinearInterpolation = [dfRatio](int nValBefore, int nVal)
1679 : {
1680 1038400 : return std::clamp(static_cast<int>(0.45 + nValBefore +
1681 519200 : dfRatio * (nVal - nValBefore)),
1682 519200 : 0, 255);
1683 129800 : };
1684 :
1685 129800 : *pnR = LinearInterpolation(asColorAssociation[i - 1].nR,
1686 129800 : asColorAssociation[i].nR);
1687 129800 : *pnG = LinearInterpolation(asColorAssociation[i - 1].nG,
1688 129800 : asColorAssociation[i].nG);
1689 129800 : *pnB = LinearInterpolation(asColorAssociation[i - 1].nB,
1690 129800 : asColorAssociation[i].nB);
1691 129800 : *pnA = LinearInterpolation(asColorAssociation[i - 1].nA,
1692 129800 : asColorAssociation[i].nA);
1693 :
1694 129800 : return true;
1695 : }
1696 : }
1697 :
1698 : /* dfPct : percentage between 0 and 1 */
1699 0 : static double GDALColorReliefGetAbsoluteValFromPct(GDALRasterBandH hSrcBand,
1700 : double dfPct)
1701 : {
1702 0 : int bSuccessMin = FALSE;
1703 0 : int bSuccessMax = FALSE;
1704 0 : double dfMin = GDALGetRasterMinimum(hSrcBand, &bSuccessMin);
1705 0 : double dfMax = GDALGetRasterMaximum(hSrcBand, &bSuccessMax);
1706 0 : if (!bSuccessMin || !bSuccessMax)
1707 : {
1708 0 : double dfMean = 0.0;
1709 0 : double dfStdDev = 0.0;
1710 0 : fprintf(stderr, "Computing source raster statistics...\n");
1711 0 : GDALComputeRasterStatistics(hSrcBand, FALSE, &dfMin, &dfMax, &dfMean,
1712 : &dfStdDev, nullptr, nullptr);
1713 : }
1714 0 : return dfMin + dfPct * (dfMax - dfMin);
1715 : }
1716 :
1717 : typedef struct
1718 : {
1719 : const char *name;
1720 : float r, g, b;
1721 : } NamedColor;
1722 :
1723 : static const NamedColor namedColors[] = {
1724 : {"white", 1.00, 1.00, 1.00}, {"black", 0.00, 0.00, 0.00},
1725 : {"red", 1.00, 0.00, 0.00}, {"green", 0.00, 1.00, 0.00},
1726 : {"blue", 0.00, 0.00, 1.00}, {"yellow", 1.00, 1.00, 0.00},
1727 : {"magenta", 1.00, 0.00, 1.00}, {"cyan", 0.00, 1.00, 1.00},
1728 : {"aqua", 0.00, 0.75, 0.75}, {"grey", 0.75, 0.75, 0.75},
1729 : {"gray", 0.75, 0.75, 0.75}, {"orange", 1.00, 0.50, 0.00},
1730 : {"brown", 0.75, 0.50, 0.25}, {"purple", 0.50, 0.00, 1.00},
1731 : {"violet", 0.50, 0.00, 1.00}, {"indigo", 0.00, 0.50, 1.00},
1732 : };
1733 :
1734 0 : static bool GDALColorReliefFindNamedColor(const char *pszColorName, int *pnR,
1735 : int *pnG, int *pnB)
1736 : {
1737 0 : *pnR = 0;
1738 0 : *pnG = 0;
1739 0 : *pnB = 0;
1740 0 : for (const auto &namedColor : namedColors)
1741 : {
1742 0 : if (EQUAL(pszColorName, namedColor.name))
1743 : {
1744 0 : *pnR = static_cast<int>(255.0 * namedColor.r);
1745 0 : *pnG = static_cast<int>(255.0 * namedColor.g);
1746 0 : *pnB = static_cast<int>(255.0 * namedColor.b);
1747 0 : return true;
1748 : }
1749 : }
1750 0 : return false;
1751 : }
1752 :
1753 : static std::vector<ColorAssociation>
1754 31 : GDALColorReliefParseColorFile(GDALRasterBandH hSrcBand,
1755 : const char *pszColorFilename,
1756 : ColorSelectionMode eColorSelectionMode)
1757 : {
1758 : auto fpColorFile =
1759 62 : VSIVirtualHandleUniquePtr(VSIFOpenL(pszColorFilename, "rt"));
1760 31 : if (fpColorFile == nullptr)
1761 : {
1762 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s",
1763 : pszColorFilename);
1764 0 : return {};
1765 : }
1766 :
1767 62 : std::vector<ColorAssociation> asColorAssociation;
1768 :
1769 31 : int bSrcHasNoData = FALSE;
1770 : double dfSrcNoDataValue =
1771 31 : GDALGetRasterNoDataValue(hSrcBand, &bSrcHasNoData);
1772 :
1773 31 : const char *pszLine = nullptr;
1774 31 : bool bIsGMT_CPT = false;
1775 : ColorAssociation sColor;
1776 193 : while ((pszLine = CPLReadLineL(fpColorFile.get())) != nullptr)
1777 : {
1778 162 : if (pszLine[0] == '#' && strstr(pszLine, "COLOR_MODEL"))
1779 : {
1780 1 : if (strstr(pszLine, "COLOR_MODEL = RGB") == nullptr)
1781 : {
1782 0 : CPLError(CE_Failure, CPLE_AppDefined,
1783 : "Only COLOR_MODEL = RGB is supported");
1784 0 : return {};
1785 : }
1786 1 : bIsGMT_CPT = true;
1787 : }
1788 :
1789 : const CPLStringList aosFields(
1790 162 : CSLTokenizeStringComplex(pszLine, " ,\t:", FALSE, FALSE));
1791 : /* Skip comment lines */
1792 162 : const int nTokens = aosFields.size();
1793 162 : if (nTokens >= 1 && (aosFields[0][0] == '#' || aosFields[0][0] == '/'))
1794 : {
1795 3 : continue;
1796 : }
1797 :
1798 159 : if (bIsGMT_CPT && nTokens == 8)
1799 : {
1800 3 : sColor.dfVal = CPLAtof(aosFields[0]);
1801 3 : sColor.nR = atoi(aosFields[1]);
1802 3 : sColor.nG = atoi(aosFields[2]);
1803 3 : sColor.nB = atoi(aosFields[3]);
1804 3 : sColor.nA = 255;
1805 3 : asColorAssociation.push_back(sColor);
1806 :
1807 3 : sColor.dfVal = CPLAtof(aosFields[4]);
1808 3 : sColor.nR = atoi(aosFields[5]);
1809 3 : sColor.nG = atoi(aosFields[6]);
1810 3 : sColor.nB = atoi(aosFields[7]);
1811 3 : sColor.nA = 255;
1812 3 : asColorAssociation.push_back(sColor);
1813 : }
1814 156 : else if (bIsGMT_CPT && nTokens == 4)
1815 : {
1816 : // The first token might be B (background), F (foreground) or N
1817 : // (nodata) Just interested in N.
1818 3 : if (EQUAL(aosFields[0], "N") && bSrcHasNoData)
1819 : {
1820 1 : sColor.dfVal = dfSrcNoDataValue;
1821 1 : sColor.nR = atoi(aosFields[1]);
1822 1 : sColor.nG = atoi(aosFields[2]);
1823 1 : sColor.nB = atoi(aosFields[3]);
1824 1 : sColor.nA = 255;
1825 1 : asColorAssociation.push_back(sColor);
1826 : }
1827 : }
1828 153 : else if (!bIsGMT_CPT && nTokens >= 2)
1829 : {
1830 153 : if (EQUAL(aosFields[0], "nv"))
1831 : {
1832 5 : if (!bSrcHasNoData)
1833 : {
1834 1 : CPLError(CE_Warning, CPLE_AppDefined,
1835 : "Input dataset has no nodata value. "
1836 : "Ignoring 'nv' entry in color palette");
1837 1 : continue;
1838 : }
1839 4 : sColor.dfVal = dfSrcNoDataValue;
1840 : }
1841 245 : else if (strlen(aosFields[0]) > 1 &&
1842 97 : aosFields[0][strlen(aosFields[0]) - 1] == '%')
1843 : {
1844 0 : const double dfPct = CPLAtof(aosFields[0]) / 100.0;
1845 0 : if (dfPct < 0.0 || dfPct > 1.0)
1846 : {
1847 0 : CPLError(CE_Failure, CPLE_AppDefined,
1848 : "Wrong value for a percentage : %s", aosFields[0]);
1849 0 : return {};
1850 : }
1851 0 : sColor.dfVal =
1852 0 : GDALColorReliefGetAbsoluteValFromPct(hSrcBand, dfPct);
1853 : }
1854 : else
1855 : {
1856 148 : sColor.dfVal = CPLAtof(aosFields[0]);
1857 : }
1858 :
1859 152 : if (nTokens >= 4)
1860 : {
1861 152 : sColor.nR = atoi(aosFields[1]);
1862 152 : sColor.nG = atoi(aosFields[2]);
1863 152 : sColor.nB = atoi(aosFields[3]);
1864 152 : sColor.nA =
1865 152 : (CSLCount(aosFields) >= 5) ? atoi(aosFields[4]) : 255;
1866 : }
1867 : else
1868 : {
1869 0 : int nR = 0;
1870 0 : int nG = 0;
1871 0 : int nB = 0;
1872 0 : if (!GDALColorReliefFindNamedColor(aosFields[1], &nR, &nG, &nB))
1873 : {
1874 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unknown color : %s",
1875 : aosFields[1]);
1876 0 : return {};
1877 : }
1878 0 : sColor.nR = nR;
1879 0 : sColor.nG = nG;
1880 0 : sColor.nB = nB;
1881 0 : sColor.nA =
1882 0 : (CSLCount(aosFields) >= 3) ? atoi(aosFields[2]) : 255;
1883 : }
1884 152 : asColorAssociation.push_back(sColor);
1885 : }
1886 : }
1887 :
1888 31 : if (asColorAssociation.empty())
1889 : {
1890 1 : CPLError(CE_Failure, CPLE_AppDefined,
1891 : "No color association found in %s", pszColorFilename);
1892 1 : return {};
1893 : }
1894 :
1895 30 : GDALColorReliefProcessColors(asColorAssociation, bSrcHasNoData,
1896 : dfSrcNoDataValue, eColorSelectionMode);
1897 :
1898 30 : return asColorAssociation;
1899 : }
1900 :
1901 21 : static GByte *GDALColorReliefPrecompute(
1902 : GDALRasterBandH hSrcBand,
1903 : const std::vector<ColorAssociation> &asColorAssociation,
1904 : ColorSelectionMode eColorSelectionMode, int *pnIndexOffset)
1905 : {
1906 21 : const GDALDataType eDT = GDALGetRasterDataType(hSrcBand);
1907 21 : GByte *pabyPrecomputed = nullptr;
1908 21 : const int nIndexOffset = (eDT == GDT_Int16) ? 32768 : 0;
1909 21 : *pnIndexOffset = nIndexOffset;
1910 21 : const int nXSize = GDALGetRasterBandXSize(hSrcBand);
1911 21 : const int nYSize = GDALGetRasterBandYSize(hSrcBand);
1912 21 : if (eDT == GDT_Byte || ((eDT == GDT_Int16 || eDT == GDT_UInt16) &&
1913 10 : static_cast<GIntBig>(nXSize) * nYSize > 65536))
1914 : {
1915 7 : const int iMax = (eDT == GDT_Byte) ? 256 : 65536;
1916 7 : pabyPrecomputed = static_cast<GByte *>(VSI_MALLOC2_VERBOSE(4, iMax));
1917 7 : if (pabyPrecomputed)
1918 : {
1919 1799 : for (int i = 0; i < iMax; i++)
1920 : {
1921 1792 : int nR = 0;
1922 1792 : int nG = 0;
1923 1792 : int nB = 0;
1924 1792 : int nA = 0;
1925 1792 : GDALColorReliefGetRGBA(asColorAssociation, i - nIndexOffset,
1926 : eColorSelectionMode, &nR, &nG, &nB, &nA);
1927 1792 : pabyPrecomputed[4 * i] = static_cast<GByte>(nR);
1928 1792 : pabyPrecomputed[4 * i + 1] = static_cast<GByte>(nG);
1929 1792 : pabyPrecomputed[4 * i + 2] = static_cast<GByte>(nB);
1930 1792 : pabyPrecomputed[4 * i + 3] = static_cast<GByte>(nA);
1931 : }
1932 : }
1933 : }
1934 21 : return pabyPrecomputed;
1935 : }
1936 :
1937 : /************************************************************************/
1938 : /* ==================================================================== */
1939 : /* GDALColorReliefDataset */
1940 : /* ==================================================================== */
1941 : /************************************************************************/
1942 :
1943 : class GDALColorReliefRasterBand;
1944 :
1945 : class GDALColorReliefDataset : public GDALDataset
1946 : {
1947 : friend class GDALColorReliefRasterBand;
1948 :
1949 : GDALDatasetH hSrcDS;
1950 : GDALRasterBandH hSrcBand;
1951 : std::vector<ColorAssociation> asColorAssociation{};
1952 : ColorSelectionMode eColorSelectionMode;
1953 : GByte *pabyPrecomputed;
1954 : int nIndexOffset;
1955 : float *pafSourceBuf;
1956 : int *panSourceBuf;
1957 : int nCurBlockXOff;
1958 : int nCurBlockYOff;
1959 :
1960 : CPL_DISALLOW_COPY_ASSIGN(GDALColorReliefDataset)
1961 :
1962 : public:
1963 : GDALColorReliefDataset(GDALDatasetH hSrcDS, GDALRasterBandH hSrcBand,
1964 : const char *pszColorFilename,
1965 : ColorSelectionMode eColorSelectionMode, int bAlpha);
1966 : ~GDALColorReliefDataset();
1967 :
1968 2 : bool InitOK() const
1969 : {
1970 2 : return pafSourceBuf != nullptr || panSourceBuf != nullptr;
1971 : }
1972 :
1973 : CPLErr GetGeoTransform(double *padfGeoTransform) override;
1974 : const OGRSpatialReference *GetSpatialRef() const override;
1975 : };
1976 :
1977 : /************************************************************************/
1978 : /* ==================================================================== */
1979 : /* GDALColorReliefRasterBand */
1980 : /* ==================================================================== */
1981 : /************************************************************************/
1982 :
1983 : class GDALColorReliefRasterBand : public GDALRasterBand
1984 : {
1985 : friend class GDALColorReliefDataset;
1986 :
1987 : public:
1988 : GDALColorReliefRasterBand(GDALColorReliefDataset *, int);
1989 :
1990 : virtual CPLErr IReadBlock(int, int, void *) override;
1991 : virtual GDALColorInterp GetColorInterpretation() override;
1992 : };
1993 :
1994 2 : GDALColorReliefDataset::GDALColorReliefDataset(
1995 : GDALDatasetH hSrcDSIn, GDALRasterBandH hSrcBandIn,
1996 : const char *pszColorFilename, ColorSelectionMode eColorSelectionModeIn,
1997 2 : int bAlpha)
1998 : : hSrcDS(hSrcDSIn), hSrcBand(hSrcBandIn),
1999 : eColorSelectionMode(eColorSelectionModeIn), pabyPrecomputed(nullptr),
2000 : nIndexOffset(0), pafSourceBuf(nullptr), panSourceBuf(nullptr),
2001 2 : nCurBlockXOff(-1), nCurBlockYOff(-1)
2002 : {
2003 4 : asColorAssociation = GDALColorReliefParseColorFile(
2004 2 : hSrcBand, pszColorFilename, eColorSelectionMode);
2005 :
2006 2 : nRasterXSize = GDALGetRasterXSize(hSrcDS);
2007 2 : nRasterYSize = GDALGetRasterYSize(hSrcDS);
2008 :
2009 2 : int nBlockXSize = 0;
2010 2 : int nBlockYSize = 0;
2011 2 : GDALGetBlockSize(hSrcBand, &nBlockXSize, &nBlockYSize);
2012 :
2013 4 : pabyPrecomputed = GDALColorReliefPrecompute(
2014 2 : hSrcBand, asColorAssociation, eColorSelectionMode, &nIndexOffset);
2015 :
2016 8 : for (int i = 0; i < ((bAlpha) ? 4 : 3); i++)
2017 : {
2018 6 : SetBand(i + 1, new GDALColorReliefRasterBand(this, i + 1));
2019 : }
2020 :
2021 2 : if (pabyPrecomputed)
2022 0 : panSourceBuf = static_cast<int *>(
2023 0 : VSI_MALLOC3_VERBOSE(sizeof(int), nBlockXSize, nBlockYSize));
2024 : else
2025 2 : pafSourceBuf = static_cast<float *>(
2026 2 : VSI_MALLOC3_VERBOSE(sizeof(float), nBlockXSize, nBlockYSize));
2027 2 : }
2028 :
2029 4 : GDALColorReliefDataset::~GDALColorReliefDataset()
2030 : {
2031 2 : CPLFree(pabyPrecomputed);
2032 2 : CPLFree(panSourceBuf);
2033 2 : CPLFree(pafSourceBuf);
2034 4 : }
2035 :
2036 2 : CPLErr GDALColorReliefDataset::GetGeoTransform(double *padfGeoTransform)
2037 : {
2038 2 : return GDALGetGeoTransform(hSrcDS, padfGeoTransform);
2039 : }
2040 :
2041 2 : const OGRSpatialReference *GDALColorReliefDataset::GetSpatialRef() const
2042 : {
2043 2 : return GDALDataset::FromHandle(hSrcDS)->GetSpatialRef();
2044 : }
2045 :
2046 6 : GDALColorReliefRasterBand::GDALColorReliefRasterBand(
2047 6 : GDALColorReliefDataset *poDSIn, int nBandIn)
2048 : {
2049 6 : poDS = poDSIn;
2050 6 : nBand = nBandIn;
2051 6 : eDataType = GDT_Byte;
2052 6 : GDALGetBlockSize(poDSIn->hSrcBand, &nBlockXSize, &nBlockYSize);
2053 6 : }
2054 :
2055 36 : CPLErr GDALColorReliefRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
2056 : void *pImage)
2057 : {
2058 : GDALColorReliefDataset *poGDS =
2059 36 : cpl::down_cast<GDALColorReliefDataset *>(poDS);
2060 72 : const int nReqXSize = (nBlockXOff + 1) * nBlockXSize >= nRasterXSize
2061 36 : ? nRasterXSize - nBlockXOff * nBlockXSize
2062 : : nBlockXSize;
2063 :
2064 72 : const int nReqYSize = (nBlockYOff + 1) * nBlockYSize >= nRasterYSize
2065 36 : ? nRasterYSize - nBlockYOff * nBlockYSize
2066 : : nBlockYSize;
2067 :
2068 36 : if (poGDS->nCurBlockXOff != nBlockXOff ||
2069 34 : poGDS->nCurBlockYOff != nBlockYOff)
2070 : {
2071 12 : poGDS->nCurBlockXOff = nBlockXOff;
2072 12 : poGDS->nCurBlockYOff = nBlockYOff;
2073 :
2074 24 : const CPLErr eErr = GDALRasterIO(
2075 12 : poGDS->hSrcBand, GF_Read, nBlockXOff * nBlockXSize,
2076 12 : nBlockYOff * nBlockYSize, nReqXSize, nReqYSize,
2077 12 : (poGDS->panSourceBuf) ? static_cast<void *>(poGDS->panSourceBuf)
2078 : : static_cast<void *>(poGDS->pafSourceBuf),
2079 : nReqXSize, nReqYSize,
2080 12 : (poGDS->panSourceBuf) ? GDT_Int32 : GDT_Float32, 0, 0);
2081 12 : if (eErr != CE_None)
2082 : {
2083 0 : memset(pImage, 0, static_cast<size_t>(nBlockXSize) * nBlockYSize);
2084 0 : return eErr;
2085 : }
2086 : }
2087 :
2088 36 : int j = 0;
2089 36 : if (poGDS->panSourceBuf)
2090 : {
2091 0 : for (int y = 0; y < nReqYSize; y++)
2092 : {
2093 0 : for (int x = 0; x < nReqXSize; x++)
2094 : {
2095 0 : const int nIndex = poGDS->panSourceBuf[j] + poGDS->nIndexOffset;
2096 0 : static_cast<GByte *>(pImage)[y * nBlockXSize + x] =
2097 0 : poGDS->pabyPrecomputed[4 * nIndex + nBand - 1];
2098 0 : j++;
2099 : }
2100 : }
2101 : }
2102 : else
2103 : {
2104 36 : int anComponents[4] = {0, 0, 0, 0};
2105 762 : for (int y = 0; y < nReqYSize; y++)
2106 : {
2107 88572 : for (int x = 0; x < nReqXSize; x++)
2108 : {
2109 87846 : GDALColorReliefGetRGBA(
2110 87846 : poGDS->asColorAssociation, poGDS->pafSourceBuf[j],
2111 : poGDS->eColorSelectionMode, &anComponents[0],
2112 : &anComponents[1], &anComponents[2], &anComponents[3]);
2113 87846 : static_cast<GByte *>(pImage)[y * nBlockXSize + x] =
2114 87846 : static_cast<GByte>(anComponents[nBand - 1]);
2115 87846 : j++;
2116 : }
2117 : }
2118 : }
2119 :
2120 36 : return CE_None;
2121 : }
2122 :
2123 12 : GDALColorInterp GDALColorReliefRasterBand::GetColorInterpretation()
2124 : {
2125 12 : return static_cast<GDALColorInterp>(GCI_RedBand + nBand - 1);
2126 : }
2127 :
2128 : static CPLErr
2129 20 : GDALColorRelief(GDALRasterBandH hSrcBand, GDALRasterBandH hDstBand1,
2130 : GDALRasterBandH hDstBand2, GDALRasterBandH hDstBand3,
2131 : GDALRasterBandH hDstBand4, const char *pszColorFilename,
2132 : ColorSelectionMode eColorSelectionMode,
2133 : GDALProgressFunc pfnProgress, void *pProgressData)
2134 : {
2135 20 : if (hSrcBand == nullptr || hDstBand1 == nullptr || hDstBand2 == nullptr ||
2136 : hDstBand3 == nullptr)
2137 0 : return CE_Failure;
2138 :
2139 : const auto asColorAssociation = GDALColorReliefParseColorFile(
2140 40 : hSrcBand, pszColorFilename, eColorSelectionMode);
2141 20 : if (asColorAssociation.empty())
2142 1 : return CE_Failure;
2143 :
2144 19 : if (pfnProgress == nullptr)
2145 12 : pfnProgress = GDALDummyProgress;
2146 :
2147 : /* -------------------------------------------------------------------- */
2148 : /* Precompute the map from values to RGBA quadruplets */
2149 : /* for GDT_Byte, GDT_Int16 or GDT_UInt16 */
2150 : /* -------------------------------------------------------------------- */
2151 19 : int nIndexOffset = 0;
2152 : std::unique_ptr<GByte, VSIFreeReleaser> pabyPrecomputed(
2153 : GDALColorReliefPrecompute(hSrcBand, asColorAssociation,
2154 38 : eColorSelectionMode, &nIndexOffset));
2155 :
2156 : /* -------------------------------------------------------------------- */
2157 : /* Initialize progress counter. */
2158 : /* -------------------------------------------------------------------- */
2159 :
2160 19 : const int nXSize = GDALGetRasterBandXSize(hSrcBand);
2161 19 : const int nYSize = GDALGetRasterBandYSize(hSrcBand);
2162 :
2163 19 : std::unique_ptr<float, VSIFreeReleaser> pafSourceBuf;
2164 19 : std::unique_ptr<int, VSIFreeReleaser> panSourceBuf;
2165 19 : if (pabyPrecomputed)
2166 7 : panSourceBuf.reset(
2167 7 : static_cast<int *>(VSI_MALLOC2_VERBOSE(sizeof(int), nXSize)));
2168 : else
2169 12 : pafSourceBuf.reset(
2170 12 : static_cast<float *>(VSI_MALLOC2_VERBOSE(sizeof(float), nXSize)));
2171 : std::unique_ptr<GByte, VSIFreeReleaser> pabyDestBuf(
2172 38 : static_cast<GByte *>(VSI_MALLOC2_VERBOSE(4, nXSize)));
2173 19 : GByte *pabyDestBuf1 = pabyDestBuf.get();
2174 19 : GByte *pabyDestBuf2 = pabyDestBuf1 ? pabyDestBuf1 + nXSize : nullptr;
2175 19 : GByte *pabyDestBuf3 = pabyDestBuf2 ? pabyDestBuf2 + nXSize : nullptr;
2176 19 : GByte *pabyDestBuf4 = pabyDestBuf3 ? pabyDestBuf3 + nXSize : nullptr;
2177 :
2178 45 : if ((pabyPrecomputed != nullptr && panSourceBuf == nullptr) ||
2179 45 : (pabyPrecomputed == nullptr && pafSourceBuf == nullptr) ||
2180 : pabyDestBuf1 == nullptr)
2181 : {
2182 0 : return CE_Failure;
2183 : }
2184 :
2185 19 : if (!pfnProgress(0.0, nullptr, pProgressData))
2186 : {
2187 0 : CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
2188 0 : return CE_Failure;
2189 : }
2190 :
2191 19 : int nR = 0;
2192 19 : int nG = 0;
2193 19 : int nB = 0;
2194 19 : int nA = 0;
2195 :
2196 1241 : for (int i = 0; i < nYSize; i++)
2197 : {
2198 : /* Read source buffer */
2199 3666 : CPLErr eErr = GDALRasterIO(
2200 : hSrcBand, GF_Read, 0, i, nXSize, 1,
2201 7 : panSourceBuf ? static_cast<void *>(panSourceBuf.get())
2202 2437 : : static_cast<void *>(pafSourceBuf.get()),
2203 1222 : nXSize, 1, panSourceBuf ? GDT_Int32 : GDT_Float32, 0, 0);
2204 1222 : if (eErr != CE_None)
2205 : {
2206 0 : return eErr;
2207 : }
2208 :
2209 1222 : if (pabyPrecomputed)
2210 : {
2211 7 : const auto pabyPrecomputedRaw = pabyPrecomputed.get();
2212 7 : const auto panSourceBufRaw = panSourceBuf.get();
2213 32 : for (int j = 0; j < nXSize; j++)
2214 : {
2215 25 : int nIndex = panSourceBufRaw[j] + nIndexOffset;
2216 25 : pabyDestBuf1[j] = pabyPrecomputedRaw[4 * nIndex];
2217 25 : pabyDestBuf2[j] = pabyPrecomputedRaw[4 * nIndex + 1];
2218 25 : pabyDestBuf3[j] = pabyPrecomputedRaw[4 * nIndex + 2];
2219 25 : pabyDestBuf4[j] = pabyPrecomputedRaw[4 * nIndex + 3];
2220 : }
2221 : }
2222 : else
2223 : {
2224 1215 : const auto pafSourceBufRaw = pafSourceBuf.get();
2225 147635 : for (int j = 0; j < nXSize; j++)
2226 : {
2227 146420 : GDALColorReliefGetRGBA(asColorAssociation, pafSourceBufRaw[j],
2228 : eColorSelectionMode, &nR, &nG, &nB, &nA);
2229 146420 : pabyDestBuf1[j] = static_cast<GByte>(nR);
2230 146420 : pabyDestBuf2[j] = static_cast<GByte>(nG);
2231 146420 : pabyDestBuf3[j] = static_cast<GByte>(nB);
2232 146420 : pabyDestBuf4[j] = static_cast<GByte>(nA);
2233 : }
2234 : }
2235 :
2236 : /* -----------------------------------------
2237 : * Write Line to Raster
2238 : */
2239 1222 : eErr = GDALRasterIO(hDstBand1, GF_Write, 0, i, nXSize, 1, pabyDestBuf1,
2240 : nXSize, 1, GDT_Byte, 0, 0);
2241 1222 : if (eErr == CE_None)
2242 : {
2243 1222 : eErr = GDALRasterIO(hDstBand2, GF_Write, 0, i, nXSize, 1,
2244 : pabyDestBuf2, nXSize, 1, GDT_Byte, 0, 0);
2245 : }
2246 1222 : if (eErr == CE_None)
2247 : {
2248 1222 : eErr = GDALRasterIO(hDstBand3, GF_Write, 0, i, nXSize, 1,
2249 : pabyDestBuf3, nXSize, 1, GDT_Byte, 0, 0);
2250 : }
2251 1222 : if (eErr == CE_None && hDstBand4)
2252 : {
2253 121 : eErr = GDALRasterIO(hDstBand4, GF_Write, 0, i, nXSize, 1,
2254 : pabyDestBuf4, nXSize, 1, GDT_Byte, 0, 0);
2255 : }
2256 :
2257 2444 : if (eErr == CE_None &&
2258 1222 : !pfnProgress(1.0 * (i + 1) / nYSize, nullptr, pProgressData))
2259 : {
2260 0 : CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
2261 0 : eErr = CE_Failure;
2262 : }
2263 1222 : if (eErr != CE_None)
2264 : {
2265 0 : return eErr;
2266 : }
2267 : }
2268 :
2269 19 : pfnProgress(1.0, nullptr, pProgressData);
2270 :
2271 19 : return CE_None;
2272 : }
2273 :
2274 : /************************************************************************/
2275 : /* GDALGenerateVRTColorRelief() */
2276 : /************************************************************************/
2277 :
2278 9 : static CPLErr GDALGenerateVRTColorRelief(const char *pszDstFilename,
2279 : GDALDatasetH hSrcDataset,
2280 : GDALRasterBandH hSrcBand,
2281 : const char *pszColorFilename,
2282 : ColorSelectionMode eColorSelectionMode,
2283 : bool bAddAlpha)
2284 : {
2285 : const auto asColorAssociation = GDALColorReliefParseColorFile(
2286 18 : hSrcBand, pszColorFilename, eColorSelectionMode);
2287 9 : if (asColorAssociation.empty())
2288 0 : return CE_Failure;
2289 :
2290 9 : VSILFILE *fp = VSIFOpenL(pszDstFilename, "wt");
2291 9 : if (fp == nullptr)
2292 : {
2293 0 : return CE_Failure;
2294 : }
2295 :
2296 9 : const int nXSize = GDALGetRasterBandXSize(hSrcBand);
2297 9 : const int nYSize = GDALGetRasterBandYSize(hSrcBand);
2298 :
2299 : bool bOK =
2300 9 : VSIFPrintfL(fp, "<VRTDataset rasterXSize=\"%d\" rasterYSize=\"%d\">\n",
2301 9 : nXSize, nYSize) > 0;
2302 9 : const char *pszProjectionRef = GDALGetProjectionRef(hSrcDataset);
2303 9 : if (pszProjectionRef && pszProjectionRef[0] != '\0')
2304 : {
2305 : char *pszEscapedString =
2306 2 : CPLEscapeString(pszProjectionRef, -1, CPLES_XML);
2307 2 : bOK &= VSIFPrintfL(fp, " <SRS>%s</SRS>\n", pszEscapedString) > 0;
2308 2 : VSIFree(pszEscapedString);
2309 : }
2310 9 : double adfGT[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
2311 9 : if (GDALGetGeoTransform(hSrcDataset, adfGT) == CE_None)
2312 : {
2313 3 : bOK &= VSIFPrintfL(fp,
2314 : " <GeoTransform> %.16g, %.16g, %.16g, "
2315 : "%.16g, %.16g, %.16g</GeoTransform>\n",
2316 : adfGT[0], adfGT[1], adfGT[2], adfGT[3], adfGT[4],
2317 3 : adfGT[5]) > 0;
2318 : }
2319 :
2320 9 : int nBlockXSize = 0;
2321 9 : int nBlockYSize = 0;
2322 9 : GDALGetBlockSize(hSrcBand, &nBlockXSize, &nBlockYSize);
2323 :
2324 9 : int bRelativeToVRT = FALSE;
2325 9 : const CPLString osPath = CPLGetPathSafe(pszDstFilename);
2326 9 : char *pszSourceFilename = CPLStrdup(CPLExtractRelativePath(
2327 : osPath.c_str(), GDALGetDescription(hSrcDataset), &bRelativeToVRT));
2328 :
2329 9 : const int nBands = 3 + (bAddAlpha ? 1 : 0);
2330 :
2331 36 : for (int iBand = 0; iBand < nBands; iBand++)
2332 : {
2333 27 : bOK &=
2334 27 : VSIFPrintfL(fp, " <VRTRasterBand dataType=\"Byte\" band=\"%d\">\n",
2335 27 : iBand + 1) > 0;
2336 27 : bOK &= VSIFPrintfL(
2337 : fp, " <ColorInterp>%s</ColorInterp>\n",
2338 : GDALGetColorInterpretationName(
2339 27 : static_cast<GDALColorInterp>(GCI_RedBand + iBand))) > 0;
2340 27 : bOK &= VSIFPrintfL(fp, " <ComplexSource>\n") > 0;
2341 27 : bOK &= VSIFPrintfL(fp,
2342 : " <SourceFilename "
2343 : "relativeToVRT=\"%d\">%s</SourceFilename>\n",
2344 27 : bRelativeToVRT, pszSourceFilename) > 0;
2345 27 : bOK &= VSIFPrintfL(fp, " <SourceBand>%d</SourceBand>\n",
2346 27 : GDALGetBandNumber(hSrcBand)) > 0;
2347 27 : bOK &= VSIFPrintfL(fp,
2348 : " <SourceProperties RasterXSize=\"%d\" "
2349 : "RasterYSize=\"%d\" DataType=\"%s\" "
2350 : "BlockXSize=\"%d\" BlockYSize=\"%d\"/>\n",
2351 : nXSize, nYSize,
2352 : GDALGetDataTypeName(GDALGetRasterDataType(hSrcBand)),
2353 27 : nBlockXSize, nBlockYSize) > 0;
2354 27 : bOK &=
2355 27 : VSIFPrintfL(
2356 : fp,
2357 : " "
2358 : "<SrcRect xOff=\"0\" yOff=\"0\" xSize=\"%d\" ySize=\"%d\"/>\n",
2359 27 : nXSize, nYSize) > 0;
2360 27 : bOK &=
2361 27 : VSIFPrintfL(
2362 : fp,
2363 : " "
2364 : "<DstRect xOff=\"0\" yOff=\"0\" xSize=\"%d\" ySize=\"%d\"/>\n",
2365 27 : nXSize, nYSize) > 0;
2366 :
2367 27 : bOK &= VSIFPrintfL(fp, " <LUT>") > 0;
2368 :
2369 174 : for (size_t iColor = 0; iColor < asColorAssociation.size(); iColor++)
2370 : {
2371 147 : const double dfVal = asColorAssociation[iColor].dfVal;
2372 147 : if (iColor > 0)
2373 120 : bOK &= VSIFPrintfL(fp, ",") > 0;
2374 :
2375 120 : if (iColor > 0 &&
2376 267 : eColorSelectionMode == COLOR_SELECTION_NEAREST_ENTRY &&
2377 : dfVal !=
2378 42 : std::nextafter(asColorAssociation[iColor - 1].dfVal,
2379 : std::numeric_limits<double>::infinity()))
2380 : {
2381 : const double dfMidVal =
2382 36 : (dfVal + asColorAssociation[iColor - 1].dfVal) / 2.0;
2383 36 : bOK &=
2384 108 : VSIFPrintfL(
2385 : fp, "%.18g:%d",
2386 : std::nextafter(
2387 36 : dfMidVal, -std::numeric_limits<double>::infinity()),
2388 60 : (iBand == 0) ? asColorAssociation[iColor - 1].nR
2389 12 : : (iBand == 1) ? asColorAssociation[iColor - 1].nG
2390 12 : : (iBand == 2) ? asColorAssociation[iColor - 1].nB
2391 0 : : asColorAssociation[iColor - 1].nA) > 0;
2392 72 : bOK &= VSIFPrintfL(
2393 : fp, ",%.18g:%d", dfMidVal,
2394 60 : (iBand == 0) ? asColorAssociation[iColor].nR
2395 12 : : (iBand == 1) ? asColorAssociation[iColor].nG
2396 12 : : (iBand == 2) ? asColorAssociation[iColor].nB
2397 36 : : asColorAssociation[iColor].nA) > 0;
2398 : }
2399 : else
2400 : {
2401 111 : if (eColorSelectionMode == COLOR_SELECTION_EXACT_ENTRY)
2402 : {
2403 24 : bOK &=
2404 24 : VSIFPrintfL(
2405 : fp, "%.18g:0,",
2406 : std::nextafter(
2407 : dfVal,
2408 48 : -std::numeric_limits<double>::infinity())) > 0;
2409 : }
2410 111 : if (dfVal != static_cast<double>(static_cast<int>(dfVal)))
2411 18 : bOK &= VSIFPrintfL(fp, "%.18g", dfVal) > 0;
2412 : else
2413 93 : bOK &= VSIFPrintfL(fp, "%d", static_cast<int>(dfVal)) > 0;
2414 222 : bOK &= VSIFPrintfL(
2415 : fp, ":%d",
2416 185 : (iBand == 0) ? asColorAssociation[iColor].nR
2417 37 : : (iBand == 1) ? asColorAssociation[iColor].nG
2418 37 : : (iBand == 2) ? asColorAssociation[iColor].nB
2419 111 : : asColorAssociation[iColor].nA) > 0;
2420 : }
2421 :
2422 147 : if (eColorSelectionMode == COLOR_SELECTION_EXACT_ENTRY)
2423 : {
2424 24 : bOK &= VSIFPrintfL(
2425 : fp, ",%.18g:0",
2426 : std::nextafter(
2427 : dfVal,
2428 24 : std::numeric_limits<double>::infinity())) > 0;
2429 : }
2430 : }
2431 27 : bOK &= VSIFPrintfL(fp, "</LUT>\n") > 0;
2432 :
2433 27 : bOK &= VSIFPrintfL(fp, " </ComplexSource>\n") > 0;
2434 27 : bOK &= VSIFPrintfL(fp, " </VRTRasterBand>\n") > 0;
2435 : }
2436 :
2437 9 : CPLFree(pszSourceFilename);
2438 :
2439 9 : bOK &= VSIFPrintfL(fp, "</VRTDataset>\n") > 0;
2440 :
2441 9 : VSIFCloseL(fp);
2442 :
2443 9 : return (bOK) ? CE_None : CE_Failure;
2444 : }
2445 :
2446 : /************************************************************************/
2447 : /* GDALTRIAlg() */
2448 : /************************************************************************/
2449 :
2450 : template <class T> static T MyAbs(T x);
2451 :
2452 0 : template <> float MyAbs(float x)
2453 : {
2454 0 : return fabsf(x);
2455 : }
2456 :
2457 113288 : template <> int MyAbs(int x)
2458 : {
2459 113288 : return x >= 0 ? x : -x;
2460 : }
2461 :
2462 : // Implements Wilson et al. (2007), for bathymetric use cases
2463 : template <class T>
2464 14161 : static float GDALTRIAlgWilson(const T *afWin, float /*fDstNoDataValue*/,
2465 : void * /*pData*/)
2466 : {
2467 : // Terrain Ruggedness is average difference in height
2468 14161 : return (MyAbs(afWin[0] - afWin[4]) + MyAbs(afWin[1] - afWin[4]) +
2469 14161 : MyAbs(afWin[2] - afWin[4]) + MyAbs(afWin[3] - afWin[4]) +
2470 14161 : MyAbs(afWin[5] - afWin[4]) + MyAbs(afWin[6] - afWin[4]) +
2471 14161 : MyAbs(afWin[7] - afWin[4]) + MyAbs(afWin[8] - afWin[4])) *
2472 14161 : 0.125f;
2473 : }
2474 :
2475 : // Implements Riley, S.J., De Gloria, S.D., Elliot, R. (1999): A Terrain
2476 : // Ruggedness that Quantifies Topographic Heterogeneity. Intermountain Journal
2477 : // of Science, Vol.5, No.1-4, pp.23-27 for terrestrial use cases
2478 : template <class T>
2479 14161 : static float GDALTRIAlgRiley(const T *afWin, float /*fDstNoDataValue*/,
2480 : void * /*pData*/)
2481 : {
2482 113288 : const auto square = [](double x) { return x * x; };
2483 :
2484 : return static_cast<float>(
2485 14161 : std::sqrt(square(afWin[0] - afWin[4]) + square(afWin[1] - afWin[4]) +
2486 14161 : square(afWin[2] - afWin[4]) + square(afWin[3] - afWin[4]) +
2487 14161 : square(afWin[5] - afWin[4]) + square(afWin[6] - afWin[4]) +
2488 14161 : square(afWin[7] - afWin[4]) + square(afWin[8] - afWin[4])));
2489 : }
2490 :
2491 : /************************************************************************/
2492 : /* GDALTPIAlg() */
2493 : /************************************************************************/
2494 :
2495 : template <class T>
2496 14161 : static float GDALTPIAlg(const T *afWin, float /*fDstNoDataValue*/,
2497 : void * /*pData*/)
2498 : {
2499 : // Terrain Position is the difference between
2500 : // The central cell and the mean of the surrounding cells
2501 14161 : return afWin[4] - ((afWin[0] + afWin[1] + afWin[2] + afWin[3] + afWin[5] +
2502 14161 : afWin[6] + afWin[7] + afWin[8]) *
2503 14161 : 0.125f);
2504 : }
2505 :
2506 : /************************************************************************/
2507 : /* GDALRoughnessAlg() */
2508 : /************************************************************************/
2509 :
2510 : template <class T>
2511 14161 : static float GDALRoughnessAlg(const T *afWin, float /*fDstNoDataValue*/,
2512 : void * /*pData*/)
2513 : {
2514 : // Roughness is the largest difference between any two cells
2515 :
2516 14161 : T fRoughnessMin = afWin[0];
2517 14161 : T fRoughnessMax = afWin[0];
2518 :
2519 127449 : for (int k = 1; k < 9; k++)
2520 : {
2521 113288 : if (afWin[k] > fRoughnessMax)
2522 : {
2523 13318 : fRoughnessMax = afWin[k];
2524 : }
2525 113288 : if (afWin[k] < fRoughnessMin)
2526 : {
2527 24495 : fRoughnessMin = afWin[k];
2528 : }
2529 : }
2530 14161 : return static_cast<float>(fRoughnessMax - fRoughnessMin);
2531 : }
2532 :
2533 : /************************************************************************/
2534 : /* ==================================================================== */
2535 : /* GDALGeneric3x3Dataset */
2536 : /* ==================================================================== */
2537 : /************************************************************************/
2538 :
2539 : template <class T> class GDALGeneric3x3RasterBand;
2540 :
2541 : template <class T> class GDALGeneric3x3Dataset : public GDALDataset
2542 : {
2543 : friend class GDALGeneric3x3RasterBand<T>;
2544 :
2545 : typename GDALGeneric3x3ProcessingAlg<T>::type pfnAlg;
2546 : void *pAlgData;
2547 : GDALDatasetH hSrcDS;
2548 : GDALRasterBandH hSrcBand;
2549 : T *apafSourceBuf[3];
2550 : int bDstHasNoData;
2551 : double dfDstNoDataValue;
2552 : int nCurLine;
2553 : bool bComputeAtEdges;
2554 :
2555 : CPL_DISALLOW_COPY_ASSIGN(GDALGeneric3x3Dataset)
2556 :
2557 : public:
2558 : GDALGeneric3x3Dataset(GDALDatasetH hSrcDS, GDALRasterBandH hSrcBand,
2559 : GDALDataType eDstDataType, int bDstHasNoData,
2560 : double dfDstNoDataValue,
2561 : typename GDALGeneric3x3ProcessingAlg<T>::type pfnAlg,
2562 : void *pAlgData, bool bComputeAtEdges);
2563 : ~GDALGeneric3x3Dataset();
2564 :
2565 4 : bool InitOK() const
2566 : {
2567 8 : return apafSourceBuf[0] != nullptr && apafSourceBuf[1] != nullptr &&
2568 8 : apafSourceBuf[2] != nullptr;
2569 : }
2570 :
2571 : CPLErr GetGeoTransform(double *padfGeoTransform) override;
2572 : const OGRSpatialReference *GetSpatialRef() const override;
2573 : };
2574 :
2575 : /************************************************************************/
2576 : /* ==================================================================== */
2577 : /* GDALGeneric3x3RasterBand */
2578 : /* ==================================================================== */
2579 : /************************************************************************/
2580 :
2581 : template <class T> class GDALGeneric3x3RasterBand : public GDALRasterBand
2582 : {
2583 : friend class GDALGeneric3x3Dataset<T>;
2584 : int bSrcHasNoData;
2585 : T fSrcNoDataValue;
2586 : int bIsSrcNoDataNan;
2587 : GDALDataType eReadDT;
2588 :
2589 : void InitWithNoData(void *pImage);
2590 :
2591 : public:
2592 : GDALGeneric3x3RasterBand(GDALGeneric3x3Dataset<T> *poDSIn,
2593 : GDALDataType eDstDataType);
2594 :
2595 : virtual CPLErr IReadBlock(int, int, void *) override;
2596 : virtual double GetNoDataValue(int *pbHasNoData) override;
2597 : };
2598 :
2599 : template <class T>
2600 4 : GDALGeneric3x3Dataset<T>::GDALGeneric3x3Dataset(
2601 : GDALDatasetH hSrcDSIn, GDALRasterBandH hSrcBandIn,
2602 : GDALDataType eDstDataType, int bDstHasNoDataIn, double dfDstNoDataValueIn,
2603 : typename GDALGeneric3x3ProcessingAlg<T>::type pfnAlgIn, void *pAlgDataIn,
2604 : bool bComputeAtEdgesIn)
2605 : : pfnAlg(pfnAlgIn), pAlgData(pAlgDataIn), hSrcDS(hSrcDSIn),
2606 : hSrcBand(hSrcBandIn), bDstHasNoData(bDstHasNoDataIn),
2607 : dfDstNoDataValue(dfDstNoDataValueIn), nCurLine(-1),
2608 4 : bComputeAtEdges(bComputeAtEdgesIn)
2609 : {
2610 4 : CPLAssert(eDstDataType == GDT_Byte || eDstDataType == GDT_Float32);
2611 :
2612 4 : nRasterXSize = GDALGetRasterXSize(hSrcDS);
2613 4 : nRasterYSize = GDALGetRasterYSize(hSrcDS);
2614 :
2615 4 : SetBand(1, new GDALGeneric3x3RasterBand<T>(this, eDstDataType));
2616 :
2617 4 : apafSourceBuf[0] =
2618 4 : static_cast<T *>(VSI_MALLOC2_VERBOSE(sizeof(T), nRasterXSize));
2619 4 : apafSourceBuf[1] =
2620 4 : static_cast<T *>(VSI_MALLOC2_VERBOSE(sizeof(T), nRasterXSize));
2621 4 : apafSourceBuf[2] =
2622 4 : static_cast<T *>(VSI_MALLOC2_VERBOSE(sizeof(T), nRasterXSize));
2623 4 : }
2624 :
2625 8 : template <class T> GDALGeneric3x3Dataset<T>::~GDALGeneric3x3Dataset()
2626 : {
2627 4 : CPLFree(apafSourceBuf[0]);
2628 4 : CPLFree(apafSourceBuf[1]);
2629 4 : CPLFree(apafSourceBuf[2]);
2630 12 : }
2631 :
2632 : template <class T>
2633 6 : CPLErr GDALGeneric3x3Dataset<T>::GetGeoTransform(double *padfGeoTransform)
2634 : {
2635 6 : return GDALGetGeoTransform(hSrcDS, padfGeoTransform);
2636 : }
2637 :
2638 : template <class T>
2639 7 : const OGRSpatialReference *GDALGeneric3x3Dataset<T>::GetSpatialRef() const
2640 : {
2641 7 : return GDALDataset::FromHandle(hSrcDS)->GetSpatialRef();
2642 : }
2643 :
2644 : template <class T>
2645 4 : GDALGeneric3x3RasterBand<T>::GDALGeneric3x3RasterBand(
2646 : GDALGeneric3x3Dataset<T> *poDSIn, GDALDataType eDstDataType)
2647 : : bSrcHasNoData(FALSE), fSrcNoDataValue(0), bIsSrcNoDataNan(FALSE),
2648 4 : eReadDT(GDT_Unknown)
2649 : {
2650 4 : poDS = poDSIn;
2651 4 : nBand = 1;
2652 4 : eDataType = eDstDataType;
2653 4 : nBlockXSize = poDS->GetRasterXSize();
2654 4 : nBlockYSize = 1;
2655 :
2656 : const double dfNoDataValue =
2657 4 : GDALGetRasterNoDataValue(poDSIn->hSrcBand, &bSrcHasNoData);
2658 : if (cpl::NumericLimits<T>::is_integer)
2659 : {
2660 3 : eReadDT = GDT_Int32;
2661 3 : if (bSrcHasNoData)
2662 : {
2663 3 : GDALDataType eSrcDT = GDALGetRasterDataType(poDSIn->hSrcBand);
2664 3 : CPLAssert(eSrcDT == GDT_Byte || eSrcDT == GDT_UInt16 ||
2665 : eSrcDT == GDT_Int16);
2666 3 : const int nMinVal = (eSrcDT == GDT_Byte) ? 0
2667 : : (eSrcDT == GDT_UInt16) ? 0
2668 : : -32768;
2669 3 : const int nMaxVal = (eSrcDT == GDT_Byte) ? 255
2670 : : (eSrcDT == GDT_UInt16) ? 65535
2671 : : 32767;
2672 :
2673 3 : if (fabs(dfNoDataValue - floor(dfNoDataValue + 0.5)) < 1e-2 &&
2674 3 : dfNoDataValue >= nMinVal && dfNoDataValue <= nMaxVal)
2675 : {
2676 3 : fSrcNoDataValue = static_cast<T>(floor(dfNoDataValue + 0.5));
2677 : }
2678 : else
2679 : {
2680 0 : bSrcHasNoData = FALSE;
2681 : }
2682 : }
2683 : }
2684 : else
2685 : {
2686 1 : eReadDT = GDT_Float32;
2687 1 : fSrcNoDataValue = static_cast<T>(dfNoDataValue);
2688 1 : bIsSrcNoDataNan = bSrcHasNoData && std::isnan(dfNoDataValue);
2689 : }
2690 4 : }
2691 :
2692 : template <class T>
2693 6 : void GDALGeneric3x3RasterBand<T>::InitWithNoData(void *pImage)
2694 : {
2695 6 : auto poGDS = cpl::down_cast<GDALGeneric3x3Dataset<T> *>(poDS);
2696 6 : if (eDataType == GDT_Byte)
2697 : {
2698 732 : for (int j = 0; j < nBlockXSize; j++)
2699 726 : static_cast<GByte *>(pImage)[j] =
2700 726 : static_cast<GByte>(poGDS->dfDstNoDataValue);
2701 : }
2702 : else
2703 : {
2704 0 : for (int j = 0; j < nBlockXSize; j++)
2705 0 : static_cast<float *>(pImage)[j] =
2706 0 : static_cast<float>(poGDS->dfDstNoDataValue);
2707 : }
2708 6 : }
2709 :
2710 : template <class T>
2711 484 : CPLErr GDALGeneric3x3RasterBand<T>::IReadBlock(int /*nBlockXOff*/,
2712 : int nBlockYOff, void *pImage)
2713 : {
2714 484 : auto poGDS = cpl::down_cast<GDALGeneric3x3Dataset<T> *>(poDS);
2715 :
2716 484 : if (poGDS->bComputeAtEdges && nRasterXSize >= 2 && nRasterYSize >= 2)
2717 : {
2718 121 : if (nBlockYOff == 0)
2719 : {
2720 3 : for (int i = 0; i < 2; i++)
2721 : {
2722 4 : CPLErr eErr = GDALRasterIO(
2723 : poGDS->hSrcBand, GF_Read, 0, i, nBlockXSize, 1,
2724 2 : poGDS->apafSourceBuf[i + 1], nBlockXSize, 1, eReadDT, 0, 0);
2725 2 : if (eErr != CE_None)
2726 : {
2727 0 : InitWithNoData(pImage);
2728 0 : return eErr;
2729 : }
2730 : }
2731 1 : poGDS->nCurLine = 0;
2732 :
2733 122 : for (int j = 0; j < nRasterXSize; j++)
2734 : {
2735 121 : int jmin = (j == 0) ? j : j - 1;
2736 121 : int jmax = (j == nRasterXSize - 1) ? j : j + 1;
2737 :
2738 484 : T afWin[9] = {INTERPOL(poGDS->apafSourceBuf[1][jmin],
2739 121 : poGDS->apafSourceBuf[2][jmin],
2740 : bSrcHasNoData, fSrcNoDataValue),
2741 121 : INTERPOL(poGDS->apafSourceBuf[1][j],
2742 121 : poGDS->apafSourceBuf[2][j],
2743 : bSrcHasNoData, fSrcNoDataValue),
2744 121 : INTERPOL(poGDS->apafSourceBuf[1][jmax],
2745 121 : poGDS->apafSourceBuf[2][jmax],
2746 : bSrcHasNoData, fSrcNoDataValue),
2747 121 : poGDS->apafSourceBuf[1][jmin],
2748 121 : poGDS->apafSourceBuf[1][j],
2749 121 : poGDS->apafSourceBuf[1][jmax],
2750 121 : poGDS->apafSourceBuf[2][jmin],
2751 121 : poGDS->apafSourceBuf[2][j],
2752 121 : poGDS->apafSourceBuf[2][jmax]};
2753 :
2754 121 : const float fVal = ComputeVal(
2755 121 : CPL_TO_BOOL(bSrcHasNoData), fSrcNoDataValue,
2756 121 : CPL_TO_BOOL(bIsSrcNoDataNan), afWin,
2757 121 : static_cast<float>(poGDS->dfDstNoDataValue), poGDS->pfnAlg,
2758 121 : poGDS->pAlgData, poGDS->bComputeAtEdges);
2759 :
2760 121 : if (eDataType == GDT_Byte)
2761 121 : static_cast<GByte *>(pImage)[j] =
2762 121 : static_cast<GByte>(fVal + 0.5);
2763 : else
2764 0 : static_cast<float *>(pImage)[j] = fVal;
2765 : }
2766 :
2767 1 : return CE_None;
2768 : }
2769 120 : else if (nBlockYOff == nRasterYSize - 1)
2770 : {
2771 1 : if (poGDS->nCurLine != nRasterYSize - 2)
2772 : {
2773 0 : for (int i = 0; i < 2; i++)
2774 : {
2775 0 : CPLErr eErr = GDALRasterIO(
2776 0 : poGDS->hSrcBand, GF_Read, 0, nRasterYSize - 2 + i,
2777 0 : nBlockXSize, 1, poGDS->apafSourceBuf[i + 1],
2778 : nBlockXSize, 1, eReadDT, 0, 0);
2779 0 : if (eErr != CE_None)
2780 : {
2781 0 : InitWithNoData(pImage);
2782 0 : return eErr;
2783 : }
2784 : }
2785 : }
2786 :
2787 122 : for (int j = 0; j < nRasterXSize; j++)
2788 : {
2789 121 : int jmin = (j == 0) ? j : j - 1;
2790 121 : int jmax = (j == nRasterXSize - 1) ? j : j + 1;
2791 :
2792 484 : T afWin[9] = {poGDS->apafSourceBuf[1][jmin],
2793 121 : poGDS->apafSourceBuf[1][j],
2794 121 : poGDS->apafSourceBuf[1][jmax],
2795 121 : poGDS->apafSourceBuf[2][jmin],
2796 121 : poGDS->apafSourceBuf[2][j],
2797 121 : poGDS->apafSourceBuf[2][jmax],
2798 121 : INTERPOL(poGDS->apafSourceBuf[2][jmin],
2799 121 : poGDS->apafSourceBuf[1][jmin],
2800 : bSrcHasNoData, fSrcNoDataValue),
2801 121 : INTERPOL(poGDS->apafSourceBuf[2][j],
2802 121 : poGDS->apafSourceBuf[1][j],
2803 : bSrcHasNoData, fSrcNoDataValue),
2804 121 : INTERPOL(poGDS->apafSourceBuf[2][jmax],
2805 121 : poGDS->apafSourceBuf[1][jmax],
2806 : bSrcHasNoData, fSrcNoDataValue)};
2807 :
2808 121 : const float fVal = ComputeVal(
2809 121 : CPL_TO_BOOL(bSrcHasNoData), fSrcNoDataValue,
2810 121 : CPL_TO_BOOL(bIsSrcNoDataNan), afWin,
2811 121 : static_cast<float>(poGDS->dfDstNoDataValue), poGDS->pfnAlg,
2812 121 : poGDS->pAlgData, poGDS->bComputeAtEdges);
2813 :
2814 121 : if (eDataType == GDT_Byte)
2815 121 : static_cast<GByte *>(pImage)[j] =
2816 121 : static_cast<GByte>(fVal + 0.5);
2817 : else
2818 0 : static_cast<float *>(pImage)[j] = fVal;
2819 : }
2820 :
2821 1 : return CE_None;
2822 119 : }
2823 : }
2824 363 : else if (nBlockYOff == 0 || nBlockYOff == nRasterYSize - 1)
2825 : {
2826 6 : InitWithNoData(pImage);
2827 6 : return CE_None;
2828 : }
2829 :
2830 476 : if (poGDS->nCurLine != nBlockYOff)
2831 : {
2832 476 : if (poGDS->nCurLine + 1 == nBlockYOff)
2833 : {
2834 473 : T *pafTmp = poGDS->apafSourceBuf[0];
2835 473 : poGDS->apafSourceBuf[0] = poGDS->apafSourceBuf[1];
2836 473 : poGDS->apafSourceBuf[1] = poGDS->apafSourceBuf[2];
2837 473 : poGDS->apafSourceBuf[2] = pafTmp;
2838 :
2839 946 : CPLErr eErr = GDALRasterIO(
2840 : poGDS->hSrcBand, GF_Read, 0, nBlockYOff + 1, nBlockXSize, 1,
2841 473 : poGDS->apafSourceBuf[2], nBlockXSize, 1, eReadDT, 0, 0);
2842 :
2843 473 : if (eErr != CE_None)
2844 : {
2845 0 : InitWithNoData(pImage);
2846 0 : return eErr;
2847 : }
2848 : }
2849 : else
2850 : {
2851 12 : for (int i = 0; i < 3; i++)
2852 : {
2853 18 : const CPLErr eErr = GDALRasterIO(
2854 9 : poGDS->hSrcBand, GF_Read, 0, nBlockYOff + i - 1,
2855 9 : nBlockXSize, 1, poGDS->apafSourceBuf[i], nBlockXSize, 1,
2856 : eReadDT, 0, 0);
2857 9 : if (eErr != CE_None)
2858 : {
2859 0 : InitWithNoData(pImage);
2860 0 : return eErr;
2861 : }
2862 : }
2863 : }
2864 :
2865 476 : poGDS->nCurLine = nBlockYOff;
2866 : }
2867 :
2868 476 : if (poGDS->bComputeAtEdges && nRasterXSize >= 2)
2869 : {
2870 119 : int j = 0;
2871 357 : T afWin[9] = {
2872 119 : INTERPOL(poGDS->apafSourceBuf[0][j], poGDS->apafSourceBuf[0][j + 1],
2873 : bSrcHasNoData, fSrcNoDataValue),
2874 119 : poGDS->apafSourceBuf[0][j],
2875 119 : poGDS->apafSourceBuf[0][j + 1],
2876 119 : INTERPOL(poGDS->apafSourceBuf[1][j], poGDS->apafSourceBuf[1][j + 1],
2877 : bSrcHasNoData, fSrcNoDataValue),
2878 119 : poGDS->apafSourceBuf[1][j],
2879 119 : poGDS->apafSourceBuf[1][j + 1],
2880 119 : INTERPOL(poGDS->apafSourceBuf[2][j], poGDS->apafSourceBuf[2][j + 1],
2881 : bSrcHasNoData, fSrcNoDataValue),
2882 119 : poGDS->apafSourceBuf[2][j],
2883 119 : poGDS->apafSourceBuf[2][j + 1]};
2884 :
2885 : {
2886 119 : const float fVal = ComputeVal(
2887 119 : CPL_TO_BOOL(bSrcHasNoData), fSrcNoDataValue,
2888 119 : CPL_TO_BOOL(bIsSrcNoDataNan), afWin,
2889 119 : static_cast<float>(poGDS->dfDstNoDataValue), poGDS->pfnAlg,
2890 119 : poGDS->pAlgData, poGDS->bComputeAtEdges);
2891 :
2892 119 : if (eDataType == GDT_Byte)
2893 119 : static_cast<GByte *>(pImage)[j] =
2894 119 : static_cast<GByte>(fVal + 0.5);
2895 : else
2896 0 : static_cast<float *>(pImage)[j] = fVal;
2897 : }
2898 :
2899 119 : j = nRasterXSize - 1;
2900 :
2901 119 : afWin[0] = poGDS->apafSourceBuf[0][j - 1];
2902 119 : afWin[1] = poGDS->apafSourceBuf[0][j];
2903 119 : afWin[2] =
2904 119 : INTERPOL(poGDS->apafSourceBuf[0][j], poGDS->apafSourceBuf[0][j - 1],
2905 : bSrcHasNoData, fSrcNoDataValue);
2906 119 : afWin[3] = poGDS->apafSourceBuf[1][j - 1];
2907 119 : afWin[4] = poGDS->apafSourceBuf[1][j];
2908 119 : afWin[5] =
2909 119 : INTERPOL(poGDS->apafSourceBuf[1][j], poGDS->apafSourceBuf[1][j - 1],
2910 : bSrcHasNoData, fSrcNoDataValue);
2911 119 : afWin[6] = poGDS->apafSourceBuf[2][j - 1];
2912 119 : afWin[7] = poGDS->apafSourceBuf[2][j];
2913 119 : afWin[8] =
2914 119 : INTERPOL(poGDS->apafSourceBuf[2][j], poGDS->apafSourceBuf[2][j - 1],
2915 : bSrcHasNoData, fSrcNoDataValue);
2916 :
2917 : const float fVal =
2918 119 : ComputeVal(CPL_TO_BOOL(bSrcHasNoData), fSrcNoDataValue,
2919 119 : CPL_TO_BOOL(bIsSrcNoDataNan), afWin,
2920 119 : static_cast<float>(poGDS->dfDstNoDataValue),
2921 119 : poGDS->pfnAlg, poGDS->pAlgData, poGDS->bComputeAtEdges);
2922 :
2923 119 : if (eDataType == GDT_Byte)
2924 119 : static_cast<GByte *>(pImage)[j] = static_cast<GByte>(fVal + 0.5);
2925 : else
2926 119 : static_cast<float *>(pImage)[j] = fVal;
2927 : }
2928 : else
2929 : {
2930 357 : if (eDataType == GDT_Byte)
2931 : {
2932 357 : static_cast<GByte *>(pImage)[0] =
2933 357 : static_cast<GByte>(poGDS->dfDstNoDataValue);
2934 357 : if (nBlockXSize > 1)
2935 357 : static_cast<GByte *>(pImage)[nBlockXSize - 1] =
2936 357 : static_cast<GByte>(poGDS->dfDstNoDataValue);
2937 : }
2938 : else
2939 : {
2940 0 : static_cast<float *>(pImage)[0] =
2941 0 : static_cast<float>(poGDS->dfDstNoDataValue);
2942 0 : if (nBlockXSize > 1)
2943 0 : static_cast<float *>(pImage)[nBlockXSize - 1] =
2944 0 : static_cast<float>(poGDS->dfDstNoDataValue);
2945 : }
2946 : }
2947 :
2948 57120 : for (int j = 1; j < nBlockXSize - 1; j++)
2949 : {
2950 56644 : T afWin[9] = {
2951 56644 : poGDS->apafSourceBuf[0][j - 1], poGDS->apafSourceBuf[0][j],
2952 56644 : poGDS->apafSourceBuf[0][j + 1], poGDS->apafSourceBuf[1][j - 1],
2953 56644 : poGDS->apafSourceBuf[1][j], poGDS->apafSourceBuf[1][j + 1],
2954 56644 : poGDS->apafSourceBuf[2][j - 1], poGDS->apafSourceBuf[2][j],
2955 56644 : poGDS->apafSourceBuf[2][j + 1]};
2956 :
2957 : const float fVal =
2958 56644 : ComputeVal(CPL_TO_BOOL(bSrcHasNoData), fSrcNoDataValue,
2959 56644 : CPL_TO_BOOL(bIsSrcNoDataNan), afWin,
2960 56644 : static_cast<float>(poGDS->dfDstNoDataValue),
2961 56644 : poGDS->pfnAlg, poGDS->pAlgData, poGDS->bComputeAtEdges);
2962 :
2963 56644 : if (eDataType == GDT_Byte)
2964 56644 : static_cast<GByte *>(pImage)[j] = static_cast<GByte>(fVal + 0.5);
2965 : else
2966 0 : static_cast<float *>(pImage)[j] = fVal;
2967 : }
2968 :
2969 476 : return CE_None;
2970 : }
2971 :
2972 : template <class T>
2973 16 : double GDALGeneric3x3RasterBand<T>::GetNoDataValue(int *pbHasNoData)
2974 : {
2975 16 : auto poGDS = cpl::down_cast<GDALGeneric3x3Dataset<T> *>(poDS);
2976 16 : if (pbHasNoData)
2977 12 : *pbHasNoData = poGDS->bDstHasNoData;
2978 16 : return poGDS->dfDstNoDataValue;
2979 : }
2980 :
2981 : /************************************************************************/
2982 : /* GetAlgorithm() */
2983 : /************************************************************************/
2984 :
2985 : typedef enum
2986 : {
2987 : INVALID,
2988 : HILL_SHADE,
2989 : SLOPE,
2990 : ASPECT,
2991 : COLOR_RELIEF,
2992 : TRI,
2993 : TPI,
2994 : ROUGHNESS
2995 : } Algorithm;
2996 :
2997 72 : static Algorithm GetAlgorithm(const char *pszProcessing)
2998 : {
2999 72 : if (EQUAL(pszProcessing, "shade") || EQUAL(pszProcessing, "hillshade"))
3000 : {
3001 33 : return HILL_SHADE;
3002 : }
3003 39 : else if (EQUAL(pszProcessing, "slope"))
3004 : {
3005 2 : return SLOPE;
3006 : }
3007 37 : else if (EQUAL(pszProcessing, "aspect"))
3008 : {
3009 2 : return ASPECT;
3010 : }
3011 35 : else if (EQUAL(pszProcessing, "color-relief"))
3012 : {
3013 31 : return COLOR_RELIEF;
3014 : }
3015 4 : else if (EQUAL(pszProcessing, "TRI"))
3016 : {
3017 2 : return TRI;
3018 : }
3019 2 : else if (EQUAL(pszProcessing, "TPI"))
3020 : {
3021 1 : return TPI;
3022 : }
3023 1 : else if (EQUAL(pszProcessing, "roughness"))
3024 : {
3025 1 : return ROUGHNESS;
3026 : }
3027 : else
3028 : {
3029 0 : return INVALID;
3030 : }
3031 : }
3032 :
3033 : /************************************************************************/
3034 : /* GDALDEMAppOptionsGetParser() */
3035 : /************************************************************************/
3036 :
3037 73 : static std::unique_ptr<GDALArgumentParser> GDALDEMAppOptionsGetParser(
3038 : GDALDEMProcessingOptions *psOptions,
3039 : GDALDEMProcessingOptionsForBinary *psOptionsForBinary)
3040 : {
3041 : auto argParser = std::make_unique<GDALArgumentParser>(
3042 73 : "gdaldem", /* bForBinary=*/psOptionsForBinary != nullptr);
3043 :
3044 73 : argParser->add_description(_("Tools to analyze and visualize DEMs."));
3045 :
3046 73 : argParser->add_epilog(_("For more details, consult "
3047 73 : "https://gdal.org/programs/gdaldem.html"));
3048 :
3049 : // Common options (for all modes)
3050 : auto addCommonOptions =
3051 2709 : [psOptions, psOptionsForBinary](GDALArgumentParser *subParser)
3052 : {
3053 511 : subParser->add_output_format_argument(psOptions->osFormat);
3054 :
3055 511 : subParser->add_argument("-compute_edges")
3056 511 : .flag()
3057 511 : .store_into(psOptions->bComputeAtEdges)
3058 : .help(_(
3059 511 : "Do the computation at raster edges and near nodata values."));
3060 :
3061 511 : auto &bandArg = subParser->add_argument("-b")
3062 1022 : .metavar("<value>")
3063 511 : .scan<'i', int>()
3064 511 : .store_into(psOptions->nBand)
3065 511 : .help(_("Select an input band."));
3066 :
3067 511 : subParser->add_hidden_alias_for(bandArg, "--b");
3068 :
3069 511 : subParser->add_creation_options_argument(psOptions->aosCreationOptions);
3070 :
3071 511 : if (psOptionsForBinary)
3072 : {
3073 154 : subParser->add_quiet_argument(&psOptionsForBinary->bQuiet);
3074 : }
3075 511 : };
3076 :
3077 : // Hillshade
3078 :
3079 73 : auto subCommandHillshade = argParser->add_subparser(
3080 : "hillshade", /* bForBinary=*/psOptionsForBinary != nullptr);
3081 73 : subCommandHillshade->add_description(_("Compute hillshade."));
3082 :
3083 73 : if (psOptionsForBinary)
3084 : {
3085 22 : subCommandHillshade->add_argument("input_dem")
3086 22 : .store_into(psOptionsForBinary->osSrcFilename)
3087 22 : .help(_("The input DEM raster to be processed."));
3088 :
3089 22 : subCommandHillshade->add_argument("output_hillshade")
3090 22 : .store_into(psOptionsForBinary->osDstFilename)
3091 22 : .help(_("The output raster to be produced."));
3092 : }
3093 :
3094 73 : subCommandHillshade->add_argument("-alg")
3095 146 : .metavar("<Horn|ZevenbergenThorne>")
3096 : .action(
3097 13 : [psOptions](const std::string &s)
3098 : {
3099 7 : if (EQUAL(s.c_str(), "ZevenbergenThorne"))
3100 : {
3101 6 : psOptions->bGradientAlgSpecified = true;
3102 6 : psOptions->eGradientAlg = GradientAlg::ZEVENBERGEN_THORNE;
3103 : }
3104 1 : else if (EQUAL(s.c_str(), "Horn"))
3105 : {
3106 0 : psOptions->bGradientAlgSpecified = true;
3107 0 : psOptions->eGradientAlg = GradientAlg::HORN;
3108 : }
3109 : else
3110 : {
3111 : throw std::invalid_argument(
3112 1 : CPLSPrintf("Invalid value for -alg: %s.", s.c_str()));
3113 : }
3114 79 : })
3115 73 : .help(_("Choose between ZevenbergenThorne or Horn algorithms."));
3116 :
3117 73 : subCommandHillshade->add_argument("-z")
3118 146 : .metavar("<factor>")
3119 73 : .scan<'g', double>()
3120 73 : .store_into(psOptions->z)
3121 73 : .help(_("Vertical exaggeration."));
3122 :
3123 : auto &scaleHillshadeArg =
3124 73 : subCommandHillshade->add_argument("-s")
3125 146 : .metavar("<scale>")
3126 73 : .scan<'g', double>()
3127 73 : .store_into(psOptions->scale)
3128 73 : .help(_("Ratio of vertical units to horizontal."));
3129 :
3130 73 : subCommandHillshade->add_hidden_alias_for(scaleHillshadeArg, "--s");
3131 73 : subCommandHillshade->add_hidden_alias_for(scaleHillshadeArg, "-scale");
3132 73 : subCommandHillshade->add_hidden_alias_for(scaleHillshadeArg, "--scale");
3133 :
3134 : auto &azimuthHillshadeArg =
3135 73 : subCommandHillshade->add_argument("-az")
3136 146 : .metavar("<azimuth>")
3137 73 : .scan<'g', double>()
3138 73 : .store_into(psOptions->az)
3139 73 : .help(_("Azimuth of the light, in degrees."));
3140 :
3141 73 : subCommandHillshade->add_hidden_alias_for(azimuthHillshadeArg, "--az");
3142 73 : subCommandHillshade->add_hidden_alias_for(azimuthHillshadeArg, "-azimuth");
3143 73 : subCommandHillshade->add_hidden_alias_for(azimuthHillshadeArg, "--azimuth");
3144 :
3145 : auto &altitudeHillshadeArg =
3146 73 : subCommandHillshade->add_argument("-alt")
3147 146 : .metavar("<altitude>")
3148 73 : .scan<'g', double>()
3149 73 : .store_into(psOptions->alt)
3150 73 : .help(_("Altitude of the light, in degrees."));
3151 :
3152 73 : subCommandHillshade->add_hidden_alias_for(altitudeHillshadeArg, "--alt");
3153 : subCommandHillshade->add_hidden_alias_for(altitudeHillshadeArg,
3154 73 : "--altitude");
3155 : subCommandHillshade->add_hidden_alias_for(altitudeHillshadeArg,
3156 73 : "-altitude");
3157 :
3158 73 : auto &shadingGroup = subCommandHillshade->add_mutually_exclusive_group();
3159 :
3160 73 : auto &combinedHillshadeArg = shadingGroup.add_argument("-combined")
3161 73 : .flag()
3162 73 : .store_into(psOptions->bCombined)
3163 73 : .help(_("Use combined shading."));
3164 :
3165 : subCommandHillshade->add_hidden_alias_for(combinedHillshadeArg,
3166 73 : "--combined");
3167 :
3168 : auto &multidirectionalHillshadeArg =
3169 73 : shadingGroup.add_argument("-multidirectional")
3170 73 : .flag()
3171 73 : .store_into(psOptions->bMultiDirectional)
3172 73 : .help(_("Use multidirectional shading."));
3173 :
3174 : subCommandHillshade->add_hidden_alias_for(multidirectionalHillshadeArg,
3175 73 : "--multidirectional");
3176 :
3177 : auto &igorHillshadeArg =
3178 73 : shadingGroup.add_argument("-igor")
3179 73 : .flag()
3180 73 : .store_into(psOptions->bIgor)
3181 : .help(_("Shading which tries to minimize effects on other map "
3182 73 : "features beneath."));
3183 :
3184 73 : subCommandHillshade->add_hidden_alias_for(igorHillshadeArg, "--igor");
3185 :
3186 73 : addCommonOptions(subCommandHillshade);
3187 :
3188 : // Slope
3189 :
3190 73 : auto subCommandSlope = argParser->add_subparser(
3191 : "slope", /* bForBinary=*/psOptionsForBinary != nullptr);
3192 :
3193 73 : subCommandSlope->add_description(_("Compute slope."));
3194 :
3195 73 : if (psOptionsForBinary)
3196 : {
3197 22 : subCommandSlope->add_argument("input_dem")
3198 22 : .store_into(psOptionsForBinary->osSrcFilename)
3199 22 : .help(_("The input DEM raster to be processed."));
3200 :
3201 22 : subCommandSlope->add_argument("output_slope_map")
3202 22 : .store_into(psOptionsForBinary->osDstFilename)
3203 22 : .help(_("The output raster to be produced."));
3204 : }
3205 :
3206 73 : subCommandSlope->add_argument("-alg")
3207 146 : .metavar("Horn|ZevenbergenThorne")
3208 : .action(
3209 1 : [psOptions](const std::string &s)
3210 : {
3211 1 : if (EQUAL(s.c_str(), "ZevenbergenThorne"))
3212 : {
3213 0 : psOptions->bGradientAlgSpecified = true;
3214 0 : psOptions->eGradientAlg = GradientAlg::ZEVENBERGEN_THORNE;
3215 : }
3216 1 : else if (EQUAL(s.c_str(), "Horn"))
3217 : {
3218 0 : psOptions->bGradientAlgSpecified = true;
3219 0 : psOptions->eGradientAlg = GradientAlg::HORN;
3220 : }
3221 : else
3222 : {
3223 : throw std::invalid_argument(
3224 1 : CPLSPrintf("Invalid value for -alg: %s.", s.c_str()));
3225 : }
3226 73 : })
3227 73 : .help(_("Choose between ZevenbergenThorne or Horn algorithms."));
3228 :
3229 : subCommandSlope->add_inverted_logic_flag(
3230 : "-p", &psOptions->bSlopeFormatUseDegrees,
3231 73 : _("Express slope as a percentage."));
3232 :
3233 73 : subCommandSlope->add_argument("-s")
3234 146 : .metavar("<scale>")
3235 73 : .scan<'g', double>()
3236 73 : .store_into(psOptions->scale)
3237 73 : .help(_("Ratio of vertical units to horizontal."));
3238 :
3239 73 : addCommonOptions(subCommandSlope);
3240 :
3241 : // Aspect
3242 :
3243 73 : auto subCommandAspect = argParser->add_subparser(
3244 : "aspect", /* bForBinary=*/psOptionsForBinary != nullptr);
3245 :
3246 73 : subCommandAspect->add_description(_("Compute aspect."));
3247 :
3248 73 : if (psOptionsForBinary)
3249 : {
3250 22 : subCommandAspect->add_argument("input_dem")
3251 22 : .store_into(psOptionsForBinary->osSrcFilename)
3252 22 : .help(_("The input DEM raster to be processed."));
3253 :
3254 22 : subCommandAspect->add_argument("output_aspect_map")
3255 22 : .store_into(psOptionsForBinary->osDstFilename)
3256 22 : .help(_("The output raster to be produced."));
3257 : }
3258 :
3259 73 : subCommandAspect->add_argument("-alg")
3260 146 : .metavar("Horn|ZevenbergenThorne")
3261 : .action(
3262 1 : [psOptions](const std::string &s)
3263 : {
3264 1 : if (EQUAL(s.c_str(), "ZevenbergenThorne"))
3265 : {
3266 0 : psOptions->bGradientAlgSpecified = true;
3267 0 : psOptions->eGradientAlg = GradientAlg::ZEVENBERGEN_THORNE;
3268 : }
3269 1 : else if (EQUAL(s.c_str(), "Horn"))
3270 : {
3271 0 : psOptions->bGradientAlgSpecified = true;
3272 0 : psOptions->eGradientAlg = GradientAlg::HORN;
3273 : }
3274 : else
3275 : {
3276 : throw std::invalid_argument(
3277 1 : CPLSPrintf("Invalid value for -alg: %s.", s.c_str()));
3278 : }
3279 73 : })
3280 73 : .help(_("Choose between ZevenbergenThorne or Horn algorithms."));
3281 :
3282 : subCommandAspect->add_inverted_logic_flag(
3283 : "-trigonometric", &psOptions->bAngleAsAzimuth,
3284 73 : _("Express aspect in trigonometric format."));
3285 :
3286 73 : subCommandAspect->add_argument("-zero_for_flat")
3287 73 : .flag()
3288 73 : .store_into(psOptions->bZeroForFlat)
3289 73 : .help(_("Return 0 for flat areas with slope=0, instead of -9999."));
3290 :
3291 73 : addCommonOptions(subCommandAspect);
3292 :
3293 : // Color-relief
3294 :
3295 73 : auto subCommandColorRelief = argParser->add_subparser(
3296 : "color-relief", /* bForBinary=*/psOptionsForBinary != nullptr);
3297 :
3298 : subCommandColorRelief->add_description(
3299 : _("Color relief computed from the elevation and a text-based color "
3300 73 : "configuration file."));
3301 :
3302 73 : if (psOptionsForBinary)
3303 : {
3304 22 : subCommandColorRelief->add_argument("input_dem")
3305 22 : .store_into(psOptionsForBinary->osSrcFilename)
3306 22 : .help(_("The input DEM raster to be processed."));
3307 :
3308 22 : subCommandColorRelief->add_argument("color_text_file")
3309 22 : .store_into(psOptionsForBinary->osColorFilename)
3310 22 : .help(_("Text-based color configuration file."));
3311 :
3312 22 : subCommandColorRelief->add_argument("output_color_relief_map")
3313 22 : .store_into(psOptionsForBinary->osDstFilename)
3314 22 : .help(_("The output raster to be produced."));
3315 : }
3316 :
3317 73 : subCommandColorRelief->add_argument("-alpha")
3318 73 : .flag()
3319 73 : .store_into(psOptions->bAddAlpha)
3320 73 : .help(_("Add an alpha channel to the output raster."));
3321 :
3322 73 : subCommandColorRelief->add_argument("-exact_color_entry")
3323 73 : .nargs(0)
3324 : .action(
3325 5 : [psOptions](const auto &)
3326 73 : { psOptions->eColorSelectionMode = COLOR_SELECTION_EXACT_ENTRY; })
3327 : .help(
3328 73 : _("Use strict matching when searching in the configuration file."));
3329 :
3330 73 : subCommandColorRelief->add_argument("-nearest_color_entry")
3331 73 : .nargs(0)
3332 : .action(
3333 7 : [psOptions](const auto &)
3334 73 : { psOptions->eColorSelectionMode = COLOR_SELECTION_NEAREST_ENTRY; })
3335 : .help(_("Use the RGBA corresponding to the closest entry in the "
3336 73 : "configuration file."));
3337 :
3338 73 : addCommonOptions(subCommandColorRelief);
3339 :
3340 : // TRI
3341 :
3342 73 : auto subCommandTRI = argParser->add_subparser(
3343 : "TRI", /* bForBinary=*/psOptionsForBinary != nullptr);
3344 :
3345 73 : subCommandTRI->add_description(_("Compute the Terrain Ruggedness Index."));
3346 :
3347 73 : if (psOptionsForBinary)
3348 : {
3349 :
3350 22 : subCommandTRI->add_argument("input_dem")
3351 22 : .store_into(psOptionsForBinary->osSrcFilename)
3352 22 : .help(_("The input DEM raster to be processed."));
3353 :
3354 22 : subCommandTRI->add_argument("output_TRI_map")
3355 22 : .store_into(psOptionsForBinary->osDstFilename)
3356 22 : .help(_("The output raster to be produced."));
3357 : }
3358 :
3359 73 : subCommandTRI->add_argument("-alg")
3360 146 : .metavar("Wilson|Riley")
3361 : .action(
3362 2 : [psOptions](const std::string &s)
3363 : {
3364 1 : if (EQUAL(s.c_str(), "Wilson"))
3365 : {
3366 1 : psOptions->bTRIAlgSpecified = true;
3367 1 : psOptions->eTRIAlg = TRIAlg::WILSON;
3368 : }
3369 0 : else if (EQUAL(s.c_str(), "Riley"))
3370 : {
3371 0 : psOptions->bTRIAlgSpecified = true;
3372 0 : psOptions->eTRIAlg = TRIAlg::RILEY;
3373 : }
3374 : else
3375 : {
3376 : throw std::invalid_argument(
3377 0 : CPLSPrintf("Invalid value for -alg: %s.", s.c_str()));
3378 : }
3379 74 : })
3380 73 : .help(_("Choose between Wilson or Riley algorithms."));
3381 :
3382 73 : addCommonOptions(subCommandTRI);
3383 :
3384 : // TPI
3385 :
3386 73 : auto subCommandTPI = argParser->add_subparser(
3387 : "TPI", /* bForBinary=*/psOptionsForBinary != nullptr);
3388 :
3389 : subCommandTPI->add_description(
3390 73 : _("Compute the Topographic Position Index."));
3391 :
3392 73 : if (psOptionsForBinary)
3393 : {
3394 22 : subCommandTPI->add_argument("input_dem")
3395 22 : .store_into(psOptionsForBinary->osSrcFilename)
3396 22 : .help(_("The input DEM raster to be processed."));
3397 :
3398 22 : subCommandTPI->add_argument("output_TPI_map")
3399 22 : .store_into(psOptionsForBinary->osDstFilename)
3400 22 : .help(_("The output raster to be produced."));
3401 : }
3402 :
3403 73 : addCommonOptions(subCommandTPI);
3404 :
3405 : // Roughness
3406 :
3407 73 : auto subCommandRoughness = argParser->add_subparser(
3408 : "roughness", /* bForBinary=*/psOptionsForBinary != nullptr);
3409 :
3410 : subCommandRoughness->add_description(
3411 73 : _("Compute the roughness of the DEM."));
3412 :
3413 73 : if (psOptionsForBinary)
3414 : {
3415 22 : subCommandRoughness->add_argument("input_dem")
3416 22 : .store_into(psOptionsForBinary->osSrcFilename)
3417 22 : .help(_("The input DEM raster to be processed."));
3418 :
3419 22 : subCommandRoughness->add_argument("output_roughness_map")
3420 22 : .store_into(psOptionsForBinary->osDstFilename)
3421 22 : .help(_("The output raster to be produced."));
3422 : }
3423 :
3424 73 : addCommonOptions(subCommandRoughness);
3425 :
3426 146 : return argParser;
3427 : }
3428 :
3429 : /************************************************************************/
3430 : /* GDALDEMAppGetParserUsage() */
3431 : /************************************************************************/
3432 :
3433 1 : std::string GDALDEMAppGetParserUsage(const std::string &osProcessingMode)
3434 : {
3435 : try
3436 : {
3437 2 : GDALDEMProcessingOptions sOptions;
3438 2 : GDALDEMProcessingOptionsForBinary sOptionsForBinary;
3439 : auto argParser =
3440 2 : GDALDEMAppOptionsGetParser(&sOptions, &sOptionsForBinary);
3441 1 : if (!osProcessingMode.empty())
3442 : {
3443 0 : const auto subParser = argParser->get_subparser(osProcessingMode);
3444 0 : if (subParser)
3445 : {
3446 0 : return subParser->usage();
3447 : }
3448 : else
3449 : {
3450 0 : CPLError(CE_Failure, CPLE_AppDefined,
3451 : "Invalid processing mode: %s",
3452 : osProcessingMode.c_str());
3453 : }
3454 : }
3455 1 : return argParser->usage();
3456 : }
3457 0 : catch (const std::exception &err)
3458 : {
3459 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
3460 0 : err.what());
3461 0 : return std::string();
3462 : }
3463 : }
3464 :
3465 : /************************************************************************/
3466 : /* GDALDEMProcessing() */
3467 : /************************************************************************/
3468 :
3469 : /**
3470 : * Apply a DEM processing.
3471 : *
3472 : * This is the equivalent of the <a href="/programs/gdaldem.html">gdaldem</a>
3473 : * utility.
3474 : *
3475 : * GDALDEMProcessingOptions* must be allocated and freed with
3476 : * GDALDEMProcessingOptionsNew() and GDALDEMProcessingOptionsFree()
3477 : * respectively.
3478 : *
3479 : * @param pszDest the destination dataset path.
3480 : * @param hSrcDataset the source dataset handle.
3481 : * @param pszProcessing the processing to apply (one of "hillshade", "slope",
3482 : * "aspect", "color-relief", "TRI", "TPI", "Roughness")
3483 : * @param pszColorFilename color file (mandatory for "color-relief" processing,
3484 : * should be NULL otherwise)
3485 : * @param psOptionsIn the options struct returned by
3486 : * GDALDEMProcessingOptionsNew() or NULL.
3487 : * @param pbUsageError pointer to a integer output variable to store if any
3488 : * usage error has occurred or NULL.
3489 : * @return the output dataset (new dataset that must be closed using
3490 : * GDALClose()) or NULL in case of error.
3491 : *
3492 : * @since GDAL 2.1
3493 : */
3494 :
3495 72 : GDALDatasetH GDALDEMProcessing(const char *pszDest, GDALDatasetH hSrcDataset,
3496 : const char *pszProcessing,
3497 : const char *pszColorFilename,
3498 : const GDALDEMProcessingOptions *psOptionsIn,
3499 : int *pbUsageError)
3500 : {
3501 72 : if (hSrcDataset == nullptr)
3502 : {
3503 0 : CPLError(CE_Failure, CPLE_AppDefined, "No source dataset specified.");
3504 :
3505 0 : if (pbUsageError)
3506 0 : *pbUsageError = TRUE;
3507 0 : return nullptr;
3508 : }
3509 72 : if (pszDest == nullptr)
3510 : {
3511 0 : CPLError(CE_Failure, CPLE_AppDefined, "No target dataset specified.");
3512 :
3513 0 : if (pbUsageError)
3514 0 : *pbUsageError = TRUE;
3515 0 : return nullptr;
3516 : }
3517 72 : if (pszProcessing == nullptr)
3518 : {
3519 0 : CPLError(CE_Failure, CPLE_AppDefined, "No target dataset specified.");
3520 :
3521 0 : if (pbUsageError)
3522 0 : *pbUsageError = TRUE;
3523 0 : return nullptr;
3524 : }
3525 :
3526 72 : Algorithm eUtilityMode = GetAlgorithm(pszProcessing);
3527 72 : if (eUtilityMode == INVALID)
3528 : {
3529 0 : CPLError(CE_Failure, CPLE_IllegalArg, "Invalid processing mode: %s",
3530 : pszProcessing);
3531 0 : if (pbUsageError)
3532 0 : *pbUsageError = TRUE;
3533 0 : return nullptr;
3534 : }
3535 :
3536 72 : if (eUtilityMode == COLOR_RELIEF &&
3537 31 : (pszColorFilename == nullptr || strlen(pszColorFilename) == 0))
3538 : {
3539 0 : CPLError(CE_Failure, CPLE_AppDefined, "pszColorFilename == NULL.");
3540 :
3541 0 : if (pbUsageError)
3542 0 : *pbUsageError = TRUE;
3543 0 : return nullptr;
3544 : }
3545 72 : else if (eUtilityMode != COLOR_RELIEF && pszColorFilename != nullptr &&
3546 9 : strlen(pszColorFilename) > 0)
3547 : {
3548 0 : CPLError(CE_Failure, CPLE_AppDefined, "pszColorFilename != NULL.");
3549 :
3550 0 : if (pbUsageError)
3551 0 : *pbUsageError = TRUE;
3552 0 : return nullptr;
3553 : }
3554 :
3555 72 : if (psOptionsIn && psOptionsIn->bCombined && eUtilityMode != HILL_SHADE)
3556 : {
3557 0 : CPLError(CE_Failure, CPLE_NotSupported,
3558 : "-combined can only be used with hillshade");
3559 :
3560 0 : if (pbUsageError)
3561 0 : *pbUsageError = TRUE;
3562 0 : return nullptr;
3563 : }
3564 :
3565 72 : if (psOptionsIn && psOptionsIn->bIgor && eUtilityMode != HILL_SHADE)
3566 : {
3567 0 : CPLError(CE_Failure, CPLE_NotSupported,
3568 : "-igor can only be used with hillshade");
3569 :
3570 0 : if (pbUsageError)
3571 0 : *pbUsageError = TRUE;
3572 0 : return nullptr;
3573 : }
3574 :
3575 72 : if (psOptionsIn && psOptionsIn->bMultiDirectional &&
3576 : eUtilityMode != HILL_SHADE)
3577 : {
3578 0 : CPLError(CE_Failure, CPLE_NotSupported,
3579 : "-multidirectional can only be used with hillshade");
3580 :
3581 0 : if (pbUsageError)
3582 0 : *pbUsageError = TRUE;
3583 0 : return nullptr;
3584 : }
3585 :
3586 72 : GDALDEMProcessingOptions *psOptionsToFree = nullptr;
3587 72 : const GDALDEMProcessingOptions *psOptions = psOptionsIn;
3588 72 : if (!psOptions)
3589 : {
3590 0 : psOptionsToFree = GDALDEMProcessingOptionsNew(nullptr, nullptr);
3591 0 : psOptions = psOptionsToFree;
3592 : }
3593 :
3594 72 : if (psOptions->bGradientAlgSpecified &&
3595 2 : !(eUtilityMode == HILL_SHADE || eUtilityMode == SLOPE ||
3596 : eUtilityMode == ASPECT))
3597 : {
3598 0 : CPLError(CE_Failure, CPLE_IllegalArg,
3599 : "This value of -alg is only value for hillshade, slope or "
3600 : "aspect processing");
3601 0 : GDALDEMProcessingOptionsFree(psOptionsToFree);
3602 0 : return nullptr;
3603 : }
3604 :
3605 72 : if (psOptions->bTRIAlgSpecified && !(eUtilityMode == TRI))
3606 : {
3607 0 : CPLError(CE_Failure, CPLE_IllegalArg,
3608 : "This value of -alg is only value for TRI processing");
3609 0 : GDALDEMProcessingOptionsFree(psOptionsToFree);
3610 0 : return nullptr;
3611 : }
3612 :
3613 72 : double adfGeoTransform[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
3614 :
3615 72 : const int nXSize = GDALGetRasterXSize(hSrcDataset);
3616 72 : const int nYSize = GDALGetRasterYSize(hSrcDataset);
3617 :
3618 144 : if (psOptions->nBand <= 0 ||
3619 72 : psOptions->nBand > GDALGetRasterCount(hSrcDataset))
3620 : {
3621 0 : CPLError(CE_Failure, CPLE_IllegalArg, "Unable to fetch band #%d",
3622 0 : psOptions->nBand);
3623 0 : GDALDEMProcessingOptionsFree(psOptionsToFree);
3624 0 : return nullptr;
3625 : }
3626 72 : GDALRasterBandH hSrcBand = GDALGetRasterBand(hSrcDataset, psOptions->nBand);
3627 :
3628 72 : GDALGetGeoTransform(hSrcDataset, adfGeoTransform);
3629 :
3630 144 : CPLString osFormat;
3631 72 : if (psOptions->osFormat.empty())
3632 : {
3633 14 : osFormat = GetOutputDriverForRaster(pszDest);
3634 14 : if (osFormat.empty())
3635 : {
3636 0 : GDALDEMProcessingOptionsFree(psOptionsToFree);
3637 0 : return nullptr;
3638 : }
3639 : }
3640 : else
3641 : {
3642 58 : osFormat = psOptions->osFormat;
3643 : }
3644 :
3645 72 : GDALDriverH hDriver = GDALGetDriverByName(osFormat);
3646 144 : if (hDriver == nullptr ||
3647 72 : (GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE, nullptr) == nullptr &&
3648 5 : GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATECOPY, nullptr) ==
3649 : nullptr))
3650 : {
3651 0 : CPLError(CE_Failure, CPLE_AppDefined,
3652 : "Output driver `%s' not recognised to have output support.",
3653 : osFormat.c_str());
3654 0 : fprintf(stderr, "The following format drivers are configured\n"
3655 : "and support output:\n");
3656 :
3657 0 : for (int iDr = 0; iDr < GDALGetDriverCount(); iDr++)
3658 : {
3659 0 : hDriver = GDALGetDriver(iDr);
3660 :
3661 0 : if (GDALGetMetadataItem(hDriver, GDAL_DCAP_RASTER, nullptr) !=
3662 0 : nullptr &&
3663 0 : (GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE, nullptr) !=
3664 0 : nullptr ||
3665 0 : GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATECOPY, nullptr) !=
3666 : nullptr))
3667 : {
3668 0 : fprintf(stderr, " %s: %s\n", GDALGetDriverShortName(hDriver),
3669 : GDALGetDriverLongName(hDriver));
3670 : }
3671 : }
3672 0 : GDALDEMProcessingOptionsFree(psOptionsToFree);
3673 0 : return nullptr;
3674 : }
3675 :
3676 72 : double dfDstNoDataValue = 0.0;
3677 72 : bool bDstHasNoData = false;
3678 72 : void *pData = nullptr;
3679 72 : GDALGeneric3x3ProcessingAlg<float>::type pfnAlgFloat = nullptr;
3680 72 : GDALGeneric3x3ProcessingAlg<GInt32>::type pfnAlgInt32 = nullptr;
3681 : GDALGeneric3x3ProcessingAlg_multisample<GInt32>::type
3682 72 : pfnAlgInt32_multisample = nullptr;
3683 :
3684 72 : if (eUtilityMode == HILL_SHADE && psOptions->bMultiDirectional)
3685 : {
3686 2 : dfDstNoDataValue = 0;
3687 2 : bDstHasNoData = true;
3688 4 : pData = GDALCreateHillshadeMultiDirectionalData(
3689 2 : adfGeoTransform, psOptions->z, psOptions->scale, psOptions->alt,
3690 2 : psOptions->eGradientAlg);
3691 2 : if (psOptions->eGradientAlg == GradientAlg::ZEVENBERGEN_THORNE)
3692 : {
3693 1 : pfnAlgFloat = GDALHillshadeMultiDirectionalAlg<
3694 : float, GradientAlg::ZEVENBERGEN_THORNE>;
3695 1 : pfnAlgInt32 = GDALHillshadeMultiDirectionalAlg<
3696 : GInt32, GradientAlg::ZEVENBERGEN_THORNE>;
3697 : }
3698 : else
3699 : {
3700 1 : pfnAlgFloat =
3701 : GDALHillshadeMultiDirectionalAlg<float, GradientAlg::HORN>;
3702 1 : pfnAlgInt32 =
3703 : GDALHillshadeMultiDirectionalAlg<GInt32, GradientAlg::HORN>;
3704 : }
3705 : }
3706 70 : else if (eUtilityMode == HILL_SHADE)
3707 : {
3708 31 : dfDstNoDataValue = 0;
3709 31 : bDstHasNoData = true;
3710 62 : pData = GDALCreateHillshadeData(adfGeoTransform, psOptions->z,
3711 31 : psOptions->scale, psOptions->alt,
3712 31 : psOptions->az, psOptions->eGradientAlg);
3713 31 : if (psOptions->eGradientAlg == GradientAlg::ZEVENBERGEN_THORNE)
3714 : {
3715 3 : if (psOptions->bCombined)
3716 : {
3717 1 : pfnAlgFloat =
3718 : GDALHillshadeCombinedAlg<float,
3719 : GradientAlg::ZEVENBERGEN_THORNE>;
3720 1 : pfnAlgInt32 =
3721 : GDALHillshadeCombinedAlg<GInt32,
3722 : GradientAlg::ZEVENBERGEN_THORNE>;
3723 : }
3724 2 : else if (psOptions->bIgor)
3725 : {
3726 1 : pfnAlgFloat =
3727 : GDALHillshadeIgorAlg<float,
3728 : GradientAlg::ZEVENBERGEN_THORNE>;
3729 1 : pfnAlgInt32 =
3730 : GDALHillshadeIgorAlg<GInt32,
3731 : GradientAlg::ZEVENBERGEN_THORNE>;
3732 : }
3733 : else
3734 : {
3735 1 : pfnAlgFloat =
3736 : GDALHillshadeAlg<float, GradientAlg::ZEVENBERGEN_THORNE>;
3737 1 : pfnAlgInt32 =
3738 : GDALHillshadeAlg<GInt32, GradientAlg::ZEVENBERGEN_THORNE>;
3739 : }
3740 : }
3741 : else
3742 : {
3743 28 : if (psOptions->bCombined)
3744 : {
3745 2 : pfnAlgFloat =
3746 : GDALHillshadeCombinedAlg<float, GradientAlg::HORN>;
3747 2 : pfnAlgInt32 =
3748 : GDALHillshadeCombinedAlg<GInt32, GradientAlg::HORN>;
3749 : }
3750 26 : else if (psOptions->bIgor)
3751 : {
3752 1 : pfnAlgFloat = GDALHillshadeIgorAlg<float, GradientAlg::HORN>;
3753 1 : pfnAlgInt32 = GDALHillshadeIgorAlg<GInt32, GradientAlg::HORN>;
3754 : }
3755 : else
3756 : {
3757 25 : if (adfGeoTransform[1] == -adfGeoTransform[5])
3758 : {
3759 25 : pfnAlgFloat = GDALHillshadeAlg_same_res<float>;
3760 25 : pfnAlgInt32 = GDALHillshadeAlg_same_res<GInt32>;
3761 : #ifdef HAVE_16_SSE_REG
3762 25 : pfnAlgInt32_multisample =
3763 : GDALHillshadeAlg_same_res_multisample<GInt32>;
3764 : #endif
3765 : }
3766 : else
3767 : {
3768 0 : pfnAlgFloat = GDALHillshadeAlg<float, GradientAlg::HORN>;
3769 0 : pfnAlgInt32 = GDALHillshadeAlg<GInt32, GradientAlg::HORN>;
3770 : }
3771 : }
3772 : }
3773 : }
3774 39 : else if (eUtilityMode == SLOPE)
3775 : {
3776 2 : dfDstNoDataValue = -9999;
3777 2 : bDstHasNoData = true;
3778 :
3779 4 : pData = GDALCreateSlopeData(adfGeoTransform, psOptions->scale,
3780 2 : psOptions->bSlopeFormatUseDegrees);
3781 2 : if (psOptions->eGradientAlg == GradientAlg::ZEVENBERGEN_THORNE)
3782 : {
3783 1 : pfnAlgFloat = GDALSlopeZevenbergenThorneAlg<float>;
3784 1 : pfnAlgInt32 = GDALSlopeZevenbergenThorneAlg<GInt32>;
3785 : }
3786 : else
3787 : {
3788 1 : pfnAlgFloat = GDALSlopeHornAlg<float>;
3789 1 : pfnAlgInt32 = GDALSlopeHornAlg<GInt32>;
3790 : }
3791 : }
3792 :
3793 37 : else if (eUtilityMode == ASPECT)
3794 : {
3795 2 : if (!psOptions->bZeroForFlat)
3796 : {
3797 2 : dfDstNoDataValue = -9999;
3798 2 : bDstHasNoData = true;
3799 : }
3800 :
3801 2 : pData = GDALCreateAspectData(psOptions->bAngleAsAzimuth);
3802 2 : if (psOptions->eGradientAlg == GradientAlg::ZEVENBERGEN_THORNE)
3803 : {
3804 1 : pfnAlgFloat = GDALAspectZevenbergenThorneAlg<float>;
3805 1 : pfnAlgInt32 = GDALAspectZevenbergenThorneAlg<GInt32>;
3806 : }
3807 : else
3808 : {
3809 1 : pfnAlgFloat = GDALAspectAlg<float>;
3810 1 : pfnAlgInt32 = GDALAspectAlg<GInt32>;
3811 : }
3812 : }
3813 35 : else if (eUtilityMode == TRI)
3814 : {
3815 2 : dfDstNoDataValue = -9999;
3816 2 : bDstHasNoData = true;
3817 2 : if (psOptions->eTRIAlg == TRIAlg::WILSON)
3818 : {
3819 1 : pfnAlgFloat = GDALTRIAlgWilson<float>;
3820 1 : pfnAlgInt32 = GDALTRIAlgWilson<GInt32>;
3821 : }
3822 : else
3823 : {
3824 1 : pfnAlgFloat = GDALTRIAlgRiley<float>;
3825 1 : pfnAlgInt32 = GDALTRIAlgRiley<GInt32>;
3826 : }
3827 : }
3828 33 : else if (eUtilityMode == TPI)
3829 : {
3830 1 : dfDstNoDataValue = -9999;
3831 1 : bDstHasNoData = true;
3832 1 : pfnAlgFloat = GDALTPIAlg<float>;
3833 1 : pfnAlgInt32 = GDALTPIAlg<GInt32>;
3834 : }
3835 32 : else if (eUtilityMode == ROUGHNESS)
3836 : {
3837 1 : dfDstNoDataValue = -9999;
3838 1 : bDstHasNoData = true;
3839 1 : pfnAlgFloat = GDALRoughnessAlg<float>;
3840 1 : pfnAlgInt32 = GDALRoughnessAlg<GInt32>;
3841 : }
3842 :
3843 72 : const GDALDataType eDstDataType =
3844 39 : (eUtilityMode == HILL_SHADE || eUtilityMode == COLOR_RELIEF)
3845 111 : ? GDT_Byte
3846 : : GDT_Float32;
3847 :
3848 72 : if (EQUAL(osFormat, "VRT"))
3849 : {
3850 9 : if (eUtilityMode == COLOR_RELIEF)
3851 : {
3852 9 : GDALGenerateVRTColorRelief(
3853 : pszDest, hSrcDataset, hSrcBand, pszColorFilename,
3854 9 : psOptions->eColorSelectionMode, psOptions->bAddAlpha);
3855 :
3856 9 : CPLFree(pData);
3857 :
3858 9 : GDALDEMProcessingOptionsFree(psOptionsToFree);
3859 9 : return GDALOpen(pszDest, GA_Update);
3860 : }
3861 : else
3862 : {
3863 0 : CPLError(CE_Failure, CPLE_NotSupported,
3864 : "VRT driver can only be used with color-relief utility.");
3865 0 : GDALDEMProcessingOptionsFree(psOptionsToFree);
3866 0 : CPLFree(pData);
3867 0 : return nullptr;
3868 : }
3869 : }
3870 :
3871 : // We might actually want to always go through the intermediate dataset
3872 63 : bool bForceUseIntermediateDataset = false;
3873 :
3874 63 : GDALProgressFunc pfnProgress = psOptions->pfnProgress;
3875 63 : void *pProgressData = psOptions->pProgressData;
3876 :
3877 63 : if (EQUAL(osFormat, "GTiff"))
3878 : {
3879 14 : if (!EQUAL(psOptions->aosCreationOptions.FetchNameValueDef("COMPRESS",
3880 : "NONE"),
3881 16 : "NONE") &&
3882 2 : CPLTestBool(
3883 : psOptions->aosCreationOptions.FetchNameValueDef("TILED", "NO")))
3884 : {
3885 1 : bForceUseIntermediateDataset = true;
3886 : }
3887 13 : else if (strcmp(pszDest, "/vsistdout/") == 0)
3888 : {
3889 0 : bForceUseIntermediateDataset = true;
3890 0 : pfnProgress = GDALDummyProgress;
3891 0 : pProgressData = nullptr;
3892 : }
3893 : #ifdef S_ISFIFO
3894 : else
3895 : {
3896 : VSIStatBufL sStat;
3897 13 : if (VSIStatExL(pszDest, &sStat,
3898 13 : VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG) == 0 &&
3899 0 : S_ISFIFO(sStat.st_mode))
3900 : {
3901 0 : bForceUseIntermediateDataset = true;
3902 : }
3903 : }
3904 : #endif
3905 : }
3906 :
3907 63 : const GDALDataType eSrcDT = GDALGetRasterDataType(hSrcBand);
3908 :
3909 126 : if (GDALGetMetadataItem(hDriver, GDAL_DCAP_RASTER, nullptr) != nullptr &&
3910 62 : ((bForceUseIntermediateDataset ||
3911 62 : GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE, nullptr) == nullptr) &&
3912 6 : GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATECOPY, nullptr) !=
3913 : nullptr))
3914 : {
3915 6 : GDALDatasetH hIntermediateDataset = nullptr;
3916 :
3917 6 : if (eUtilityMode == COLOR_RELIEF)
3918 : {
3919 : GDALColorReliefDataset *poDS = new GDALColorReliefDataset(
3920 : hSrcDataset, hSrcBand, pszColorFilename,
3921 2 : psOptions->eColorSelectionMode, psOptions->bAddAlpha);
3922 2 : if (!(poDS->InitOK()))
3923 : {
3924 0 : delete poDS;
3925 0 : CPLFree(pData);
3926 0 : GDALDEMProcessingOptionsFree(psOptionsToFree);
3927 0 : return nullptr;
3928 : }
3929 2 : hIntermediateDataset = static_cast<GDALDatasetH>(poDS);
3930 : }
3931 : else
3932 : {
3933 4 : if (eSrcDT == GDT_Byte || eSrcDT == GDT_Int16 ||
3934 : eSrcDT == GDT_UInt16)
3935 : {
3936 : GDALGeneric3x3Dataset<GInt32> *poDS =
3937 : new GDALGeneric3x3Dataset<GInt32>(
3938 : hSrcDataset, hSrcBand, eDstDataType, bDstHasNoData,
3939 : dfDstNoDataValue, pfnAlgInt32, pData,
3940 3 : psOptions->bComputeAtEdges);
3941 :
3942 3 : if (!(poDS->InitOK()))
3943 : {
3944 0 : delete poDS;
3945 0 : CPLFree(pData);
3946 0 : GDALDEMProcessingOptionsFree(psOptionsToFree);
3947 0 : return nullptr;
3948 : }
3949 3 : hIntermediateDataset = static_cast<GDALDatasetH>(poDS);
3950 : }
3951 : else
3952 : {
3953 : GDALGeneric3x3Dataset<float> *poDS =
3954 : new GDALGeneric3x3Dataset<float>(
3955 : hSrcDataset, hSrcBand, eDstDataType, bDstHasNoData,
3956 : dfDstNoDataValue, pfnAlgFloat, pData,
3957 1 : psOptions->bComputeAtEdges);
3958 :
3959 1 : if (!(poDS->InitOK()))
3960 : {
3961 0 : delete poDS;
3962 0 : CPLFree(pData);
3963 0 : GDALDEMProcessingOptionsFree(psOptionsToFree);
3964 0 : return nullptr;
3965 : }
3966 1 : hIntermediateDataset = static_cast<GDALDatasetH>(poDS);
3967 : }
3968 : }
3969 :
3970 6 : GDALDatasetH hOutDS = GDALCreateCopy(
3971 : hDriver, pszDest, hIntermediateDataset, TRUE,
3972 : psOptions->aosCreationOptions.List(), pfnProgress, pProgressData);
3973 :
3974 6 : GDALClose(hIntermediateDataset);
3975 :
3976 6 : CPLFree(pData);
3977 :
3978 6 : GDALDEMProcessingOptionsFree(psOptionsToFree);
3979 6 : return hOutDS;
3980 : }
3981 :
3982 57 : const int nDstBands =
3983 57 : eUtilityMode == COLOR_RELIEF ? ((psOptions->bAddAlpha) ? 4 : 3) : 1;
3984 :
3985 : GDALDatasetH hDstDataset =
3986 57 : GDALCreate(hDriver, pszDest, nXSize, nYSize, nDstBands, eDstDataType,
3987 : psOptions->aosCreationOptions.List());
3988 :
3989 57 : if (hDstDataset == nullptr)
3990 : {
3991 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unable to create dataset %s",
3992 : pszDest);
3993 0 : GDALDEMProcessingOptionsFree(psOptionsToFree);
3994 0 : CPLFree(pData);
3995 0 : return nullptr;
3996 : }
3997 :
3998 57 : GDALRasterBandH hDstBand = GDALGetRasterBand(hDstDataset, 1);
3999 :
4000 57 : GDALSetGeoTransform(hDstDataset, adfGeoTransform);
4001 57 : GDALSetProjection(hDstDataset, GDALGetProjectionRef(hSrcDataset));
4002 :
4003 57 : if (eUtilityMode == COLOR_RELIEF)
4004 : {
4005 20 : GDALColorRelief(hSrcBand, GDALGetRasterBand(hDstDataset, 1),
4006 : GDALGetRasterBand(hDstDataset, 2),
4007 : GDALGetRasterBand(hDstDataset, 3),
4008 1 : psOptions->bAddAlpha ? GDALGetRasterBand(hDstDataset, 4)
4009 : : nullptr,
4010 20 : pszColorFilename, psOptions->eColorSelectionMode,
4011 : pfnProgress, pProgressData);
4012 : }
4013 : else
4014 : {
4015 37 : if (bDstHasNoData)
4016 37 : GDALSetRasterNoDataValue(hDstBand, dfDstNoDataValue);
4017 :
4018 37 : if (eSrcDT == GDT_Byte || eSrcDT == GDT_Int16 || eSrcDT == GDT_UInt16)
4019 : {
4020 33 : GDALGeneric3x3Processing<GInt32>(
4021 : hSrcBand, hDstBand, pfnAlgInt32, pfnAlgInt32_multisample, pData,
4022 33 : psOptions->bComputeAtEdges, pfnProgress, pProgressData);
4023 : }
4024 : else
4025 : {
4026 4 : GDALGeneric3x3Processing<float>(
4027 : hSrcBand, hDstBand, pfnAlgFloat, nullptr, pData,
4028 4 : psOptions->bComputeAtEdges, pfnProgress, pProgressData);
4029 : }
4030 : }
4031 :
4032 57 : CPLFree(pData);
4033 :
4034 57 : GDALDEMProcessingOptionsFree(psOptionsToFree);
4035 57 : return hDstDataset;
4036 : }
4037 :
4038 : /************************************************************************/
4039 : /* GDALDEMProcessingOptionsNew() */
4040 : /************************************************************************/
4041 :
4042 : /**
4043 : * Allocates a GDALDEMProcessingOptions struct.
4044 : *
4045 : * @param papszArgv NULL terminated list of options (potentially including
4046 : * filename and open options too), or NULL. The accepted options are the ones of
4047 : * the <a href="/programs/gdaldem.html">gdaldem</a> utility.
4048 : * @param psOptionsForBinary (output) may be NULL (and should generally be
4049 : * NULL), otherwise (gdal_translate_bin.cpp use case) must be allocated with
4050 : * GDALDEMProcessingOptionsForBinaryNew() prior to
4051 : * this function. Will be filled with potentially present filename, open
4052 : * options,...
4053 : * @return pointer to the allocated GDALDEMProcessingOptions struct. Must be
4054 : * freed with GDALDEMProcessingOptionsFree().
4055 : *
4056 : * @since GDAL 2.1
4057 : */
4058 :
4059 72 : GDALDEMProcessingOptions *GDALDEMProcessingOptionsNew(
4060 : char **papszArgv, GDALDEMProcessingOptionsForBinary *psOptionsForBinary)
4061 : {
4062 :
4063 144 : auto psOptions = std::make_unique<GDALDEMProcessingOptions>();
4064 : /* -------------------------------------------------------------------- */
4065 : /* Handle command line arguments. */
4066 : /* -------------------------------------------------------------------- */
4067 144 : CPLStringList aosArgv;
4068 :
4069 72 : if (papszArgv)
4070 : {
4071 72 : const int nArgc = CSLCount(papszArgv);
4072 509 : for (int i = 0; i < nArgc; i++)
4073 437 : aosArgv.AddString(papszArgv[i]);
4074 : }
4075 :
4076 : // Ugly hack: papszArgv may not contain the processing mode if coming from SWIG
4077 72 : const bool bNoProcessingMode(aosArgv.size() > 0 && aosArgv[0][0] == '-');
4078 :
4079 : auto argParser =
4080 144 : GDALDEMAppOptionsGetParser(psOptions.get(), psOptionsForBinary);
4081 :
4082 160 : auto tryHandleArgv = [&]()
4083 : {
4084 160 : argParser->parse_args_without_binary_name(aosArgv);
4085 : // Validate the parsed options
4086 :
4087 72 : if (psOptions->nBand <= 0)
4088 : {
4089 0 : throw std::invalid_argument("Invalid value for -b");
4090 : }
4091 :
4092 72 : if (psOptions->z <= 0)
4093 : {
4094 0 : throw std::invalid_argument("Invalid value for -z");
4095 : }
4096 :
4097 72 : if (psOptions->scale <= 0)
4098 : {
4099 0 : throw std::invalid_argument("Invalid value for -s");
4100 : }
4101 :
4102 72 : if (psOptions->alt <= 0)
4103 : {
4104 0 : throw std::invalid_argument("Invalid value for -alt");
4105 : }
4106 :
4107 72 : if (psOptions->az <= 0)
4108 : {
4109 0 : throw std::invalid_argument("Invalid value for -az");
4110 : }
4111 :
4112 72 : if (psOptions->bMultiDirectional && argParser->is_used_globally("-az"))
4113 : {
4114 : throw std::invalid_argument(
4115 0 : "-multidirectional and -az cannot be used together");
4116 : }
4117 :
4118 72 : if (psOptions->bIgor && argParser->is_used_globally("-alt"))
4119 : {
4120 : throw std::invalid_argument(
4121 0 : "-igor and -alt cannot be used together");
4122 : }
4123 :
4124 72 : if (psOptionsForBinary && aosArgv.size() > 1)
4125 : {
4126 21 : psOptionsForBinary->osProcessing = aosArgv[0];
4127 : }
4128 72 : };
4129 :
4130 : try
4131 : {
4132 :
4133 : // Ugly hack: papszArgv may not contain the processing mode if coming from
4134 : // SWIG we have not other option than to check them all
4135 : const static std::list<std::string> processingModes{
4136 : {"hillshade", "slope", "aspect", "color-relief", "TRI", "TPI",
4137 226 : "roughness"}};
4138 :
4139 72 : if (bNoProcessingMode)
4140 : {
4141 : try
4142 : {
4143 51 : tryHandleArgv();
4144 : }
4145 102 : catch (std::exception &)
4146 : {
4147 102 : CPLStringList aosArgvCopy{aosArgv};
4148 51 : bool bSuccess = false;
4149 88 : for (const auto &processingMode : processingModes)
4150 : {
4151 88 : aosArgv = aosArgvCopy;
4152 88 : aosArgv.InsertString(0, processingMode.c_str());
4153 : try
4154 : {
4155 88 : tryHandleArgv();
4156 51 : bSuccess = true;
4157 51 : break;
4158 : }
4159 68 : catch (std::runtime_error &)
4160 : {
4161 34 : continue;
4162 : }
4163 6 : catch (std::invalid_argument &)
4164 : {
4165 3 : continue;
4166 : }
4167 0 : catch (std::logic_error &)
4168 : {
4169 0 : continue;
4170 : }
4171 : }
4172 :
4173 51 : if (!bSuccess)
4174 : {
4175 : throw std::invalid_argument(
4176 0 : "Argument(s) are not valid with any processing mode.");
4177 : }
4178 : }
4179 : }
4180 : else
4181 : {
4182 21 : tryHandleArgv();
4183 : }
4184 : }
4185 0 : catch (const std::exception &err)
4186 : {
4187 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
4188 0 : err.what());
4189 0 : return nullptr;
4190 : }
4191 :
4192 72 : return psOptions.release();
4193 : }
4194 :
4195 : /************************************************************************/
4196 : /* GDALDEMProcessingOptionsFree() */
4197 : /************************************************************************/
4198 :
4199 : /**
4200 : * Frees the GDALDEMProcessingOptions struct.
4201 : *
4202 : * @param psOptions the options struct for GDALDEMProcessing().
4203 : *
4204 : * @since GDAL 2.1
4205 : */
4206 :
4207 144 : void GDALDEMProcessingOptionsFree(GDALDEMProcessingOptions *psOptions)
4208 : {
4209 144 : delete psOptions;
4210 144 : }
4211 :
4212 : /************************************************************************/
4213 : /* GDALDEMProcessingOptionsSetProgress() */
4214 : /************************************************************************/
4215 :
4216 : /**
4217 : * Set a progress function.
4218 : *
4219 : * @param psOptions the options struct for GDALDEMProcessing().
4220 : * @param pfnProgress the progress callback.
4221 : * @param pProgressData the user data for the progress callback.
4222 : *
4223 : * @since GDAL 2.1
4224 : */
4225 :
4226 21 : void GDALDEMProcessingOptionsSetProgress(GDALDEMProcessingOptions *psOptions,
4227 : GDALProgressFunc pfnProgress,
4228 : void *pProgressData)
4229 : {
4230 21 : psOptions->pfnProgress = pfnProgress;
4231 21 : psOptions->pProgressData = pProgressData;
4232 21 : }
|