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