Coverage Report

Created: 2026-02-14 06:52

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