Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: "hillshade" step of "raster pipeline"
5 : * Author: Even Rouault <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "gdalalg_raster_hillshade.h"
14 :
15 : #include "gdal_priv.h"
16 : #include "gdal_utils.h"
17 :
18 : #include <cmath>
19 :
20 : //! @cond Doxygen_Suppress
21 :
22 : #ifndef _
23 : #define _(x) (x)
24 : #endif
25 :
26 : /************************************************************************/
27 : /* GDALRasterHillshadeAlgorithm::GDALRasterHillshadeAlgorithm() */
28 : /************************************************************************/
29 :
30 28 : GDALRasterHillshadeAlgorithm::GDALRasterHillshadeAlgorithm(bool standaloneStep)
31 : : GDALRasterPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
32 28 : standaloneStep)
33 : {
34 28 : SetOutputVRTCompatible(false);
35 :
36 28 : AddBandArg(&m_band).SetDefault(m_band);
37 : AddArg("zfactor", 'z',
38 : _("Vertical exaggeration used to pre-multiply the elevations"),
39 56 : &m_zfactor)
40 28 : .SetMinValueExcluded(0);
41 : AddArg("xscale", 0, _("Ratio of vertical units to horizontal X axis units"),
42 56 : &m_xscale)
43 28 : .SetMinValueExcluded(0);
44 : AddArg("yscale", 0, _("Ratio of vertical units to horizontal Y axis units"),
45 56 : &m_yscale)
46 28 : .SetMinValueExcluded(0);
47 56 : AddArg("azimuth", 0, _("Azimuth of the light, in degrees"), &m_azimuth)
48 28 : .SetDefault(m_azimuth);
49 56 : AddArg("altitude", 0, _("Altitude of the light, in degrees"), &m_altitude)
50 28 : .SetDefault(m_altitude)
51 28 : .SetMinValueIncluded(0)
52 28 : .SetMaxValueIncluded(90);
53 : AddArg("gradient-alg", 0, _("Algorithm used to compute terrain gradient"),
54 56 : &m_gradientAlg)
55 28 : .SetChoices("Horn", "ZevenbergenThorne")
56 28 : .SetDefault(m_gradientAlg);
57 56 : AddArg("variant", 0, _("Variant of the hillshading algorithm"), &m_variant)
58 28 : .SetChoices("regular", "combined", "multidirectional", "Igor")
59 28 : .SetDefault(m_variant);
60 : AddArg("no-edges", 0,
61 : _("Do not try to interpolate values at dataset edges or close to "
62 : "nodata values"),
63 28 : &m_noEdges);
64 28 : }
65 :
66 : /************************************************************************/
67 : /* GDALRasterHillshadeAlgorithm::RunStep() */
68 : /************************************************************************/
69 :
70 15 : bool GDALRasterHillshadeAlgorithm::RunStep(GDALProgressFunc, void *)
71 : {
72 15 : CPLAssert(m_inputDataset.GetDatasetRef());
73 15 : CPLAssert(m_outputDataset.GetName().empty());
74 15 : CPLAssert(!m_outputDataset.GetDatasetRef());
75 :
76 30 : CPLStringList aosOptions;
77 15 : aosOptions.AddString("-of");
78 15 : aosOptions.AddString("stream");
79 15 : aosOptions.AddString("-b");
80 15 : aosOptions.AddString(CPLSPrintf("%d", m_band));
81 15 : aosOptions.AddString("-z");
82 15 : aosOptions.AddString(CPLSPrintf("%.17g", m_zfactor));
83 15 : if (!std::isnan(m_xscale))
84 : {
85 2 : aosOptions.AddString("-xscale");
86 2 : aosOptions.AddString(CPLSPrintf("%.17g", m_xscale));
87 : }
88 15 : if (!std::isnan(m_yscale))
89 : {
90 2 : aosOptions.AddString("-yscale");
91 2 : aosOptions.AddString(CPLSPrintf("%.17g", m_yscale));
92 : }
93 15 : if (m_variant == "multidirectional")
94 : {
95 2 : if (GetArg("azimuth")->IsExplicitlySet())
96 : {
97 1 : CPLError(CE_Failure, CPLE_AppDefined,
98 : "'azimuth' argument cannot be used with multidirectional "
99 : "variant");
100 1 : return false;
101 : }
102 : }
103 : else
104 : {
105 13 : aosOptions.AddString("-az");
106 13 : aosOptions.AddString(CPLSPrintf("%.17g", m_azimuth));
107 : }
108 14 : if (m_variant == "Igor")
109 : {
110 2 : if (GetArg("altitude")->IsExplicitlySet())
111 : {
112 1 : CPLError(CE_Failure, CPLE_AppDefined,
113 : "'altitude' argument cannot be used with Igor variant");
114 1 : return false;
115 : }
116 : }
117 : else
118 : {
119 12 : aosOptions.AddString("-alt");
120 12 : aosOptions.AddString(CPLSPrintf("%.17g", m_altitude));
121 : }
122 13 : aosOptions.AddString("-alg");
123 13 : aosOptions.AddString(m_gradientAlg.c_str());
124 :
125 13 : if (m_variant == "combined")
126 1 : aosOptions.AddString("-combined");
127 12 : else if (m_variant == "multidirectional")
128 1 : aosOptions.AddString("-multidirectional");
129 11 : else if (m_variant == "Igor")
130 1 : aosOptions.AddString("-igor");
131 :
132 13 : if (!m_noEdges)
133 12 : aosOptions.AddString("-compute_edges");
134 :
135 : GDALDEMProcessingOptions *psOptions =
136 13 : GDALDEMProcessingOptionsNew(aosOptions.List(), nullptr);
137 :
138 : auto poOutDS =
139 : std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(GDALDEMProcessing(
140 : "", GDALDataset::ToHandle(m_inputDataset.GetDatasetRef()),
141 13 : "hillshade", nullptr, psOptions, nullptr)));
142 13 : GDALDEMProcessingOptionsFree(psOptions);
143 13 : const bool bRet = poOutDS != nullptr;
144 13 : if (poOutDS)
145 : {
146 13 : m_outputDataset.Set(std::move(poOutDS));
147 : }
148 :
149 13 : return bRet;
150 : }
151 :
152 : //! @endcond
|