Isis 3 Programmer Reference
Hillshade.cpp
1
6/* SPDX-License-Identifier: CC0-1.0 */
7#include "Hillshade.h"
8
9#include <algorithm>
10
11#include <QDebug>
12#include <QObject>
13#include <QString>
14
15#include "Angle.h"
16#include "Buffer.h"
17#include "SpecialPixel.h"
18
19namespace Isis {
25 m_azimuth = NULL;
26 m_zenith = NULL;
28 }
29
30
38 Hillshade::Hillshade(Angle azimuth, Angle zenith, double resolution) {
39 m_azimuth = NULL;
40 m_zenith = NULL;
42
46 }
47
48
53 m_azimuth = NULL;
54 m_zenith = NULL;
55 m_pixelResolution = other.m_pixelResolution;
56
57 if (other.m_azimuth)
58 m_azimuth = new Angle(*other.m_azimuth);
59
60 if (other.m_zenith)
61 m_zenith = new Angle(*other.m_zenith);
62 }
63
64
65 Hillshade::~Hillshade() {
66 delete m_azimuth;
67 m_azimuth = NULL;
68
69 delete m_zenith;
70 m_zenith = NULL;
71
73 }
74
75
83 delete m_azimuth;
84 m_azimuth = NULL;
85
86 if (azimuth.isValid()) {
87 m_azimuth = new Angle(azimuth);
88 }
89 }
90
91
100 delete m_zenith;
101 m_zenith = NULL;
102
103 if (zenith.isValid()) {
104 m_zenith = new Angle(zenith);
105 }
106 }
107
108
115 void Hillshade::setResolution(double resolution) {
117 }
118
119
126 Angle result;
127
128 if (m_azimuth)
129 result = *m_azimuth;
130
131 return result;
132 }
133
134
141 Angle result;
142
143 if (m_zenith)
144 result = *m_zenith;
145
146 return result;
147 }
148
149
155 double Hillshade::resolution() const {
156 return m_pixelResolution;
157 }
158
159
163 double Hillshade::shadedValue(Buffer &input) const {
164 if (input.SampleDimension() != 3 ||
165 input.LineDimension() != 3 ||
166 input.BandDimension() != 1) {
168 QObject::tr("Hillshade requires a 3x3x1 portal of data, but a %1x%2x%3 "
169 "portal of data was provided instead")
170 .arg(input.SampleDimension()).arg(input.LineDimension())
171 .arg(input.BandDimension()),
172 _FILEINFO_);
173 }
174
175 if (!m_azimuth) {
177 QObject::tr("Hillshade requires a valid azimuth angle (sun direction) to "
178 "operate"),
179 _FILEINFO_);
180 }
181
184 QObject::tr("Hillshade azimuth angle [%1] must be between 0 and 360 degrees")
185 .arg(m_azimuth->toString()),
186 _FILEINFO_);
187 }
188
189 if (!m_zenith) {
191 QObject::tr("Hillshade requires a valid zenith angle (solar elevation) to "
192 "operate"),
193 _FILEINFO_);
194 }
195
198 QObject::tr("Hillshade zenith angle [%1] must be between 0 and 90 degrees")
199 .arg(m_zenith->toString()),
200 _FILEINFO_);
201 }
202
205 QObject::tr("Hillshade requires a pixel resolution (meters/pixel) to "
206 "operate"),
207 _FILEINFO_);
208 }
209
210 if (qFuzzyCompare(0.0, m_pixelResolution)) {
212 QObject::tr("Hillshade requires a non-zero pixel resolution (meters/pixel) "
213 "to operate"),
214 _FILEINFO_);
215 }
216
217
218 // This parameter as used by the algorithm is 0-360 with 0 at 3 o'clock,
219 // increasing in the clockwise direction. The value taken in from the user is
220 // 0-360 with 0 at 12 o'clock increasing in the clockwise direction.
221 Angle azimuthFromThree = *m_azimuth + Angle(270, Angle::Degrees);
222
223 if (azimuthFromThree > Angle::fullRotation()) {
224 azimuthFromThree -= Angle::fullRotation();
225 }
226
227 double result = Null;
228
229 // first we just check to make sure that we don't have any special pixels
230 bool anySpecialPixels = false;
231 for(int i = 0; i < input.size(); ++i) {
232 if(IsSpecial(input[i])) {
233 anySpecialPixels = true;
234 }
235 }
236
237 // if we have any special pixels we bail out (well ok just set to null)
238 if (!anySpecialPixels) {
239 /* if we have a 3x3 section that doesn't have an special pixels we hit that 3x3
240 / against two gradients
241
242 [-1 0 1 ] [-1 -1 -1 ]
243 [-1 0 1 ] [ 0 0 0 ]
244 [-1 0 1 ] [ 1 1 1 ]
245
246 These two gradients are not special in any way aside from that they are
247 are orthogonal. they can be replaced if someone has reason as long as they
248 stay orthogonal to eachoher. (for those that don't know orthoginal means
249 for matrix A: A * transpose(A) = I where I is the identity matrix).
250
251 */
252
253 double p = ( (-1) * input[0] + (0) * input[1] + (1) * input[2]
254 + (-1) * input[3] + (0) * input[4] + (1) * input[5]
255 + (-1) * input[6] + (0) * input[7] + (1) * input[8]) / (3.0 * m_pixelResolution);
256
257 double q = ( (-1) * input[0] + (-1) * input[1] + (-1) * input[2]
258 + (0) * input[3] + (0) * input[4] + (0) * input[5]
259 + (1) * input[6] + (1) * input[7] + (1) * input[8]) / (3.0 * m_pixelResolution);
260
261 /* after we hit them by the gradients the formula in order to make the shade is
262
263 1 + p0*p + q0*q
264 shade = -------------------------------------------------
265 sqrt(1 + p*p + q*q) * sqrt(1 + p0*p0 + q0*q0)
266
267 where p0 = -cos( sun azimuth ) * tan( solar elevation ) and
268 q0 = -sin( sun azimuth ) * tan( solar elevation )
269
270 and p and q are the two orthogonal gradients of the data (calculated above)
271
272 this equation comes from
273 Horn, B.K.P. (1982). "Hill shading and the reflectance map". Geo-processing, v. 2, no. 1, p. 65-146.
274
275 It is avaliable online at http://people.csail.mit.edu/bkph/papers/Hill-Shading.pdf
276 */
277 double p0 = -cos(azimuthFromThree.radians()) * tan(m_zenith->radians());
278 double q0 = -sin(azimuthFromThree.radians()) * tan(m_zenith->radians());
279
280 double numerator = 1.0 + p0 * p + q0 * q;
281
282 double denominator = sqrt(1 + p * p + q * q) * sqrt(1 + p0 * p0 + q0 * q0);
283 result = numerator / denominator;
284 }
285
286 return result;
287 }
288
289
294 std::swap(m_azimuth, other.m_azimuth);
295 std::swap(m_zenith, other.m_zenith);
296 std::swap(m_pixelResolution, other.m_pixelResolution);
297 }
298
299
304 Hillshade copy(rhs);
305 swap(copy);
306 return *this;
307 }
308
309
313 QDebug operator<<(QDebug debug, const Hillshade &hillshade) {
314 QString resolution = "Null";
315
316 if (!IsSpecial(hillshade.resolution()))
317 resolution = toString(hillshade.resolution());
318
319 debug << "Hillshade[ azimuth =" << hillshade.azimuth().toString().toLatin1().data()
320 << "zenith =" << hillshade.zenith().toString().toLatin1().data()
321 << "resolution =" << resolution.toLatin1().data() << "]";
322
323 return debug;
324 }
325}
Defines an angle and provides unit conversions.
Definition Angle.h:45
bool isValid() const
This indicates whether we have a legitimate angle stored or are in an unset, or invalid,...
Definition Angle.cpp:95
double radians() const
Convert an angle to a double.
Definition Angle.h:226
static Angle fullRotation()
Makes an angle to represent a full rotation (0-360 or 0-2pi).
Definition Angle.cpp:106
@ Degrees
Degrees are generally considered more human readable, 0-360 is one circle, however most math does not...
Definition Angle.h:56
virtual QString toString(bool includeUnits=true) const
Get the angle in human-readable form.
Definition Angle.cpp:243
Buffer for reading and writing cube data.
Definition Buffer.h:53
Calculate light intensity reflected off a local slope of DEM.
Definition Hillshade.h:37
Hillshade & operator=(const Hillshade &rhs)
Assignment operator.
Angle zenith() const
Get the current zenith angle.
void setResolution(double resolution)
The resolution is the meters per pixel of the input to shadedValue().
void setAzimuth(Angle azimuth)
The azimuth is the direction of the light.
Definition Hillshade.cpp:82
Angle * m_zenith
This is the altitide of the light, with 0 directly overhead and 90 at the horizon.
Definition Hillshade.h:63
void swap(Hillshade &other)
Swap class data with other; this cannot throw an exception.
Angle * m_azimuth
This is direction of the light, with 0 at north.
Definition Hillshade.h:61
Hillshade()
Create a default-constructed Hillshade.
Definition Hillshade.cpp:24
double m_pixelResolution
meters per pixel
Definition Hillshade.h:65
Angle azimuth() const
Get the current azimuth angle.
double resolution() const
Get the current resolution (meters per pixel).
void setZenith(Angle zenith)
The zenith is the altitude/solar elevation of the light.
Definition Hillshade.cpp:99
double shadedValue(Buffer &input) const
Calculate the shaded value from a 3x3x1 window.
Isis exception class.
Definition IException.h:91
@ Unknown
A type of error that cannot be classified as any of the other error types.
Definition IException.h:118
@ Programmer
This error is for when a programmer made an API call that was illegal.
Definition IException.h:146
This is free and unencumbered software released into the public domain.
Definition Apollo.h:16
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
Definition IString.cpp:211
const double Null
Value for an Isis Null pixel.
bool IsSpecial(const double d)
Returns if the input pixel is special.
QDebug operator<<(QDebug debug, const Hillshade &hillshade)
Print this class out to a QDebug object.