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 : #include "gdalalg_raster_write.h"
15 :
16 : #include "gdal_priv.h"
17 : #include "gdal_utils.h"
18 :
19 : #include <cmath>
20 :
21 : //! @cond Doxygen_Suppress
22 :
23 : #ifndef _
24 : #define _(x) (x)
25 : #endif
26 :
27 : /************************************************************************/
28 : /* GDALRasterHillshadeAlgorithm::GDALRasterHillshadeAlgorithm() */
29 : /************************************************************************/
30 :
31 47 : GDALRasterHillshadeAlgorithm::GDALRasterHillshadeAlgorithm(bool standaloneStep)
32 : : GDALRasterPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
33 47 : standaloneStep)
34 : {
35 47 : SetOutputVRTCompatible(false);
36 :
37 47 : AddBandArg(&m_band).SetDefault(m_band);
38 : AddArg("zfactor", 'z',
39 : _("Vertical exaggeration used to pre-multiply the elevations"),
40 94 : &m_zfactor)
41 47 : .SetMinValueExcluded(0);
42 : AddArg("xscale", 0, _("Ratio of vertical units to horizontal X axis units"),
43 94 : &m_xscale)
44 47 : .SetMinValueExcluded(0);
45 : AddArg("yscale", 0, _("Ratio of vertical units to horizontal Y axis units"),
46 94 : &m_yscale)
47 47 : .SetMinValueExcluded(0);
48 94 : AddArg("azimuth", 0, _("Azimuth of the light, in degrees"), &m_azimuth)
49 47 : .SetDefault(m_azimuth);
50 94 : AddArg("altitude", 0, _("Altitude of the light, in degrees"), &m_altitude)
51 47 : .SetDefault(m_altitude)
52 47 : .SetMinValueIncluded(0)
53 47 : .SetMaxValueIncluded(90);
54 : AddArg("gradient-alg", 0, _("Algorithm used to compute terrain gradient"),
55 94 : &m_gradientAlg)
56 47 : .SetChoices("Horn", "ZevenbergenThorne")
57 47 : .SetDefault(m_gradientAlg);
58 94 : AddArg("variant", 0, _("Variant of the hillshading algorithm"), &m_variant)
59 47 : .SetChoices("regular", "combined", "multidirectional", "Igor")
60 47 : .SetDefault(m_variant);
61 : AddArg("no-edges", 0,
62 : _("Do not try to interpolate values at dataset edges or close to "
63 : "nodata values"),
64 47 : &m_noEdges);
65 47 : }
66 :
67 : /************************************************************************/
68 : /* GDALRasterHillshadeAlgorithm::CanHandleNextStep() */
69 : /************************************************************************/
70 :
71 28 : bool GDALRasterHillshadeAlgorithm::CanHandleNextStep(
72 : GDALPipelineStepAlgorithm *poNextStep) const
73 : {
74 56 : return poNextStep->GetName() == GDALRasterWriteAlgorithm::NAME &&
75 56 : poNextStep->GetOutputFormat() != "stream";
76 : }
77 :
78 : /************************************************************************/
79 : /* GDALRasterHillshadeAlgorithm::RunStep() */
80 : /************************************************************************/
81 :
82 18 : bool GDALRasterHillshadeAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
83 : {
84 18 : auto poSrcDS = m_inputDataset[0].GetDatasetRef();
85 18 : CPLAssert(poSrcDS);
86 18 : CPLAssert(m_outputDataset.GetName().empty());
87 18 : CPLAssert(!m_outputDataset.GetDatasetRef());
88 :
89 36 : CPLStringList aosOptions;
90 36 : std::string outputFilename;
91 18 : if (ctxt.m_poNextUsableStep)
92 : {
93 14 : CPLAssert(CanHandleNextStep(ctxt.m_poNextUsableStep));
94 14 : outputFilename = ctxt.m_poNextUsableStep->GetOutputDataset().GetName();
95 14 : const auto &format = ctxt.m_poNextUsableStep->GetOutputFormat();
96 14 : if (!format.empty())
97 : {
98 14 : aosOptions.AddString("-of");
99 14 : aosOptions.AddString(format.c_str());
100 : }
101 :
102 0 : for (const std::string &co :
103 14 : ctxt.m_poNextUsableStep->GetCreationOptions())
104 : {
105 0 : aosOptions.AddString("-co");
106 0 : aosOptions.AddString(co.c_str());
107 : }
108 : }
109 : else
110 : {
111 4 : aosOptions.AddString("-of");
112 4 : aosOptions.AddString("stream");
113 : }
114 :
115 18 : aosOptions.AddString("-b");
116 18 : aosOptions.AddString(CPLSPrintf("%d", m_band));
117 18 : aosOptions.AddString("-z");
118 18 : aosOptions.AddString(CPLSPrintf("%.17g", m_zfactor));
119 18 : if (!std::isnan(m_xscale))
120 : {
121 2 : aosOptions.AddString("-xscale");
122 2 : aosOptions.AddString(CPLSPrintf("%.17g", m_xscale));
123 : }
124 18 : if (!std::isnan(m_yscale))
125 : {
126 2 : aosOptions.AddString("-yscale");
127 2 : aosOptions.AddString(CPLSPrintf("%.17g", m_yscale));
128 : }
129 18 : if (m_variant == "multidirectional")
130 : {
131 2 : if (GetArg("azimuth")->IsExplicitlySet())
132 : {
133 1 : CPLError(CE_Failure, CPLE_AppDefined,
134 : "'azimuth' argument cannot be used with multidirectional "
135 : "variant");
136 1 : return false;
137 : }
138 : }
139 : else
140 : {
141 16 : aosOptions.AddString("-az");
142 16 : aosOptions.AddString(CPLSPrintf("%.17g", m_azimuth));
143 : }
144 17 : if (m_variant == "Igor")
145 : {
146 2 : if (GetArg("altitude")->IsExplicitlySet())
147 : {
148 1 : CPLError(CE_Failure, CPLE_AppDefined,
149 : "'altitude' argument cannot be used with Igor variant");
150 1 : return false;
151 : }
152 : }
153 : else
154 : {
155 15 : aosOptions.AddString("-alt");
156 15 : aosOptions.AddString(CPLSPrintf("%.17g", m_altitude));
157 : }
158 16 : aosOptions.AddString("-alg");
159 16 : aosOptions.AddString(m_gradientAlg.c_str());
160 :
161 16 : if (m_variant == "combined")
162 1 : aosOptions.AddString("-combined");
163 15 : else if (m_variant == "multidirectional")
164 1 : aosOptions.AddString("-multidirectional");
165 14 : else if (m_variant == "Igor")
166 1 : aosOptions.AddString("-igor");
167 :
168 16 : if (!m_noEdges)
169 15 : aosOptions.AddString("-compute_edges");
170 :
171 : GDALDEMProcessingOptions *psOptions =
172 16 : GDALDEMProcessingOptionsNew(aosOptions.List(), nullptr);
173 16 : bool bOK = false;
174 16 : if (psOptions)
175 : {
176 16 : if (ctxt.m_poNextUsableStep)
177 : {
178 12 : GDALDEMProcessingOptionsSetProgress(psOptions, ctxt.m_pfnProgress,
179 : ctxt.m_pProgressData);
180 : }
181 : auto poOutDS = std::unique_ptr<GDALDataset>(GDALDataset::FromHandle(
182 : GDALDEMProcessing(outputFilename.c_str(),
183 : GDALDataset::ToHandle(poSrcDS), "hillshade",
184 32 : nullptr, psOptions, nullptr)));
185 16 : GDALDEMProcessingOptionsFree(psOptions);
186 16 : bOK = poOutDS != nullptr;
187 16 : if (poOutDS)
188 : {
189 16 : m_outputDataset.Set(std::move(poOutDS));
190 : }
191 : }
192 :
193 16 : return bOK;
194 : }
195 :
196 : GDALRasterHillshadeAlgorithmStandalone::
197 : ~GDALRasterHillshadeAlgorithmStandalone() = default;
198 :
199 : //! @endcond
|