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