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