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 
19 namespace 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;
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 
82  void Hillshade::setAzimuth(Angle azimuth) {
83  delete m_azimuth;
84  m_azimuth = NULL;
85 
86  if (azimuth.isValid()) {
87  m_azimuth = new Angle(azimuth);
88  }
89  }
90 
91 
99  void Hillshade::setZenith(Angle zenith) {
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 
196  if (*m_zenith < Angle(0, Angle::Degrees) || *m_zenith > Angle(90, Angle::Degrees)) {
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 
284  result = numerator / denominator;
285  }
286 
287  return result;
288  }
289 
290 
294  void Hillshade::swap(Hillshade &other) {
295  std::swap(m_azimuth, other.m_azimuth);
296  std::swap(m_zenith, other.m_zenith);
297  std::swap(m_pixelResolution, other.m_pixelResolution);
298  }
299 
300 
305  Hillshade copy(rhs);
306  swap(copy);
307  return *this;
308  }
309 
310 
314  QDebug operator<<(QDebug debug, const Hillshade &hillshade) {
315  QString resolution = "Null";
316 
317  if (!IsSpecial(hillshade.resolution()))
318  resolution = toString(hillshade.resolution());
319 
320  debug << "Hillshade[ azimuth =" << hillshade.azimuth().toString().toLatin1().data()
321  << "zenith =" << hillshade.zenith().toString().toLatin1().data()
322  << "resolution =" << resolution.toLatin1().data() << "]";
323 
324  return debug;
325  }
326 }
Isis::Angle::Degrees
@ Degrees
Degrees are generally considered more human readable, 0-360 is one circle, however most math does not...
Definition: Angle.h:56
Isis::Hillshade::Hillshade
Hillshade()
Create a default-constructed Hillshade.
Definition: Hillshade.cpp:24
Isis::Angle::toString
virtual QString toString(bool includeUnits=true) const
Get the angle in human-readable form.
Definition: Angle.cpp:243
Isis::Buffer::SampleDimension
int SampleDimension() const
Returns the number of samples in the shape buffer.
Definition: Buffer.h:70
Isis::operator<<
QDebug operator<<(QDebug debug, const Hillshade &hillshade)
Print this class out to a QDebug object.
Definition: Hillshade.cpp:314
Isis::IException::Unknown
@ Unknown
A type of error that cannot be classified as any of the other error types.
Definition: IException.h:118
Isis::Hillshade::setResolution
void setResolution(double resolution)
The resolution is the meters per pixel of the input to shadedValue().
Definition: Hillshade.cpp:115
Isis::Hillshade::zenith
Angle zenith() const
Get the current zenith angle.
Definition: Hillshade.cpp:140
Isis::Hillshade::setAzimuth
void setAzimuth(Angle azimuth)
The azimuth is the direction of the light.
Definition: Hillshade.cpp:82
Isis::Hillshade::swap
void swap(Hillshade &other)
Swap class data with other; this cannot throw an exception.
Definition: Hillshade.cpp:294
Isis::Hillshade::m_zenith
Angle * m_zenith
This is the altitide of the light, with 0 directly overhead and 90 at the horizon.
Definition: Hillshade.h:63
Isis::toString
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
Definition: IString.cpp:211
Isis::IsSpecial
bool IsSpecial(const double d)
Returns if the input pixel is special.
Definition: SpecialPixel.h:197
Isis::Hillshade::m_pixelResolution
double m_pixelResolution
meters per pixel
Definition: Hillshade.h:65
Isis::Buffer
Buffer for reading and writing cube data.
Definition: Buffer.h:53
Isis::Hillshade::m_azimuth
Angle * m_azimuth
This is direction of the light, with 0 at north.
Definition: Hillshade.h:61
Isis::Hillshade::azimuth
Angle azimuth() const
Get the current azimuth angle.
Definition: Hillshade.cpp:125
Isis::Hillshade::setZenith
void setZenith(Angle zenith)
The zenith is the altitude/solar elevation of the light.
Definition: Hillshade.cpp:99
Isis::Buffer::LineDimension
int LineDimension() const
Returns the number of lines in the shape buffer.
Definition: Buffer.h:79
Isis::Hillshade::resolution
double resolution() const
Get the current resolution (meters per pixel).
Definition: Hillshade.cpp:155
Isis::IException
Isis exception class.
Definition: IException.h:91
Isis::Angle
Defines an angle and provides unit conversions.
Definition: Angle.h:45
Isis::Null
const double Null
Value for an Isis Null pixel.
Definition: SpecialPixel.h:95
Isis::Hillshade::operator=
Hillshade & operator=(const Hillshade &rhs)
Assignment operator.
Definition: Hillshade.cpp:304
Isis::Hillshade::shadedValue
double shadedValue(Buffer &input) const
Calculate the shaded value from a 3x3x1 window.
Definition: Hillshade.cpp:163
Isis::IException::Programmer
@ Programmer
This error is for when a programmer made an API call that was illegal.
Definition: IException.h:146
Isis::Angle::isValid
bool isValid() const
This indicates whether we have a legitimate angle stored or are in an unset, or invalid,...
Definition: Angle.cpp:95
Isis::Hillshade
Calculate light intensity reflected off a local slope of DEM.
Definition: Hillshade.h:37
Isis::Angle::fullRotation
static Angle fullRotation()
Makes an angle to represent a full rotation (0-360 or 0-2pi).
Definition: Angle.cpp:106
Isis::Buffer::size
int size() const
Returns the total number of pixels in the shape buffer.
Definition: Buffer.h:97
Isis::Buffer::BandDimension
int BandDimension() const
Returns the number of bands in the shape buffer.
Definition: Buffer.h:88
Isis
This is free and unencumbered software released into the public domain.
Definition: Apollo.h:16
Isis::Angle::radians
double radians() const
Convert an angle to a double.
Definition: Angle.h:226