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