Isis 3 Programmer Reference
Hillshade.cpp
1 #include "Hillshade.h"
2 
3 #include <algorithm>
4 
5 #include <QDebug>
6 #include <QObject>
7 #include <QString>
8 
9 #include "Angle.h"
10 #include "Buffer.h"
11 #include "SpecialPixel.h"
12 
13 namespace Isis {
19  m_azimuth = NULL;
20  m_zenith = NULL;
22  }
23 
24 
32  Hillshade::Hillshade(Angle azimuth, Angle zenith, double resolution) {
33  m_azimuth = NULL;
34  m_zenith = NULL;
36 
40  }
41 
42 
47  m_azimuth = NULL;
48  m_zenith = NULL;
50 
51  if (other.m_azimuth)
52  m_azimuth = new Angle(*other.m_azimuth);
53 
54  if (other.m_zenith)
55  m_zenith = new Angle(*other.m_zenith);
56  }
57 
58 
59  Hillshade::~Hillshade() {
60  delete m_azimuth;
61  m_azimuth = NULL;
62 
63  delete m_zenith;
64  m_zenith = NULL;
65 
67  }
68 
69 
76  void Hillshade::setAzimuth(Angle azimuth) {
77  delete m_azimuth;
78  m_azimuth = NULL;
79 
80  if (azimuth.isValid()) {
81  m_azimuth = new Angle(azimuth);
82  }
83  }
84 
85 
93  void Hillshade::setZenith(Angle zenith) {
94  delete m_zenith;
95  m_zenith = NULL;
96 
97  if (zenith.isValid()) {
98  m_zenith = new Angle(zenith);
99  }
100  }
101 
102 
109  void Hillshade::setResolution(double resolution) {
111  }
112 
113 
120  Angle result;
121 
122  if (m_azimuth)
123  result = *m_azimuth;
124 
125  return result;
126  }
127 
128 
135  Angle result;
136 
137  if (m_zenith)
138  result = *m_zenith;
139 
140  return result;
141  }
142 
143 
149  double Hillshade::resolution() const {
150  return m_pixelResolution;
151  }
152 
153 
157  double Hillshade::shadedValue(Buffer &input) const {
158  if (input.SampleDimension() != 3 ||
159  input.LineDimension() != 3 ||
160  input.BandDimension() != 1) {
162  QObject::tr("Hillshade requires a 3x3x1 portal of data, but a %1x%2x%3 "
163  "portal of data was provided instead")
164  .arg(input.SampleDimension()).arg(input.LineDimension())
165  .arg(input.BandDimension()),
166  _FILEINFO_);
167  }
168 
169  if (!m_azimuth) {
171  QObject::tr("Hillshade requires a valid azimuth angle (sun direction) to "
172  "operate"),
173  _FILEINFO_);
174  }
175 
178  QObject::tr("Hillshade azimuth angle [%1] must be between 0 and 360 degrees")
179  .arg(m_azimuth->toString()),
180  _FILEINFO_);
181  }
182 
183  if (!m_zenith) {
185  QObject::tr("Hillshade requires a valid zenith angle (solar elevation) to "
186  "operate"),
187  _FILEINFO_);
188  }
189 
190  if (*m_zenith < Angle(0, Angle::Degrees) || *m_zenith > Angle(90, Angle::Degrees)) {
192  QObject::tr("Hillshade zenith angle [%1] must be between 0 and 90 degrees")
193  .arg(m_zenith->toString()),
194  _FILEINFO_);
195  }
196 
199  QObject::tr("Hillshade requires a pixel resolution (meters/pixel) to "
200  "operate"),
201  _FILEINFO_);
202  }
203 
204  if (qFuzzyCompare(0.0, m_pixelResolution)) {
206  QObject::tr("Hillshade requires a non-zero pixel resolution (meters/pixel) "
207  "to operate"),
208  _FILEINFO_);
209  }
210 
211 
212  // This parameter as used by the algorithm is 0-360 with 0 at 3 o'clock,
213  // increasing in the clockwise direction. The value taken in from the user is
214  // 0-360 with 0 at 12 o'clock increasing in the clockwise direction.
215  Angle azimuthFromThree = *m_azimuth + Angle(270, Angle::Degrees);
216 
217  if (azimuthFromThree > Angle::fullRotation()) {
218  azimuthFromThree -= Angle::fullRotation();
219  }
220 
221  double result = Null;
222 
223  // first we just check to make sure that we don't have any special pixels
224  bool anySpecialPixels = false;
225  for(int i = 0; i < input.size(); ++i) {
226  if(IsSpecial(input[i])) {
227  anySpecialPixels = true;
228  }
229  }
230 
231  // if we have any special pixels we bail out (well ok just set to null)
232  if (!anySpecialPixels) {
233  /* if we have a 3x3 section that doesn't have an special pixels we hit that 3x3
234  / against two gradients
235 
236  [-1 0 1 ] [-1 -1 -1 ]
237  [-1 0 1 ] [ 0 0 0 ]
238  [-1 0 1 ] [ 1 1 1 ]
239 
240  These two gradients are not special in any way aside from that they are
241  are orthogonal. they can be replaced if someone has reason as long as they
242  stay orthogonal to eachoher. (for those that don't know orthoginal means
243  for matrix A: A * transpose(A) = I where I is the identity matrix).
244 
245  */
246 
247  double p = ( (-1) * input[0] + (0) * input[1] + (1) * input[2]
248  + (-1) * input[3] + (0) * input[4] + (1) * input[5]
249  + (-1) * input[6] + (0) * input[7] + (1) * input[8]) / (3.0 * m_pixelResolution);
250 
251  double q = ( (-1) * input[0] + (-1) * input[1] + (-1) * input[2]
252  + (0) * input[3] + (0) * input[4] + (0) * input[5]
253  + (1) * input[6] + (1) * input[7] + (1) * input[8]) / (3.0 * m_pixelResolution);
254 
255  /* after we hit them by the gradients the formula in order to make the shade is
256 
257  1 + p0*p + q0*q
258  shade = -------------------------------------------------
259  sqrt(1 + p*p + q*q) * sqrt(1 + p0*p0 + q0*q0)
260 
261  where p0 = -cos( sun azimuth ) * tan( solar elevation ) and
262  q0 = -sin( sun azimuth ) * tan( solar elevation )
263 
264  and p and q are the two orthogonal gradients of the data (calculated above)
265 
266  this equation comes from
267  Horn, B.K.P. (1982). "Hill shading and the reflectance map". Geo-processing, v. 2, no. 1, p. 65-146.
268 
269  It is avaliable online at http://people.csail.mit.edu/bkph/papers/Hill-Shading.pdf
270  */
271  double p0 = -cos(azimuthFromThree.radians()) * tan(m_zenith->radians());
272  double q0 = -sin(azimuthFromThree.radians()) * tan(m_zenith->radians());
273 
274  double numerator = 1.0 + p0 * p + q0 * q;
275 
276  double denominator = sqrt(1 + p * p + q * q) * sqrt(1 + p0 * p0 + q0 * q0);
277 
278  result = numerator / denominator;
279  }
280 
281  return result;
282  }
283 
284 
288  void Hillshade::swap(Hillshade &other) {
289  std::swap(m_azimuth, other.m_azimuth);
290  std::swap(m_zenith, other.m_zenith);
291  std::swap(m_pixelResolution, other.m_pixelResolution);
292  }
293 
294 
299  Hillshade copy(rhs);
300  swap(copy);
301  return *this;
302  }
303 
304 
308  QDebug operator<<(QDebug debug, const Hillshade &hillshade) {
309  QString resolution = "Null";
310 
311  if (!IsSpecial(hillshade.resolution()))
312  resolution = toString(hillshade.resolution());
313 
314  debug << "Hillshade[ azimuth =" << hillshade.azimuth().toString().toLatin1().data()
315  << "zenith =" << hillshade.zenith().toString().toLatin1().data()
316  << "resolution =" << resolution.toLatin1().data() << "]";
317 
318  return debug;
319  }
320 }
Buffer for reading and writing cube data.
Definition: Buffer.h:69
Angle * m_azimuth
This is direction of the light, with 0 at north.
Definition: Hillshade.h:55
double shadedValue(Buffer &input) const
Calculate the shaded value from a 3x3x1 window.
Definition: Hillshade.cpp:157
const double Null
Value for an Isis Null pixel.
Definition: SpecialPixel.h:110
void setAzimuth(Angle azimuth)
The azimuth is the direction of the light.
Definition: Hillshade.cpp:76
Hillshade & operator=(const Hillshade &rhs)
Assignment operator.
Definition: Hillshade.cpp:298
Angle azimuth() const
Get the current azimuth angle.
Definition: Hillshade.cpp:119
double radians() const
Convert an angle to a double.
Definition: Angle.h:243
Angle * m_zenith
This is the altitide of the light, with 0 directly overhead and 90 at the horizon.
Definition: Hillshade.h:57
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
Definition: IString.cpp:226
int LineDimension() const
Returns the number of lines in the shape buffer.
Definition: Buffer.h:95
This error is for when a programmer made an API call that was illegal.
Definition: IException.h:162
void setZenith(Angle zenith)
The zenith is the altitude/solar elevation of the light.
Definition: Hillshade.cpp:93
Hillshade()
Create a default-constructed Hillshade.
Definition: Hillshade.cpp:18
int size() const
Returns the total number of pixels in the shape buffer.
Definition: Buffer.h:113
Degrees are generally considered more human readable, 0-360 is one circle, however most math does not...
Definition: Angle.h:73
int BandDimension() const
Returns the number of bands in the shape buffer.
Definition: Buffer.h:104
virtual QString toString(bool includeUnits=true) const
Get the angle in human-readable form.
Definition: Angle.cpp:258
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:40
Calculate light intensity reflected off a local slope of DEM.
Definition: Hillshade.h:31
A type of error that cannot be classified as any of the other error types.
Definition: IException.h:134
bool IsSpecial(const double d)
Returns if the input pixel is special.
Definition: SpecialPixel.h:212
static Angle fullRotation()
Makes an angle to represent a full rotation (0-360 or 0-2pi).
Definition: Angle.cpp:121
void setResolution(double resolution)
The resolution is the meters per pixel of the input to shadedValue().
Definition: Hillshade.cpp:109
int SampleDimension() const
Returns the number of samples in the shape buffer.
Definition: Buffer.h:86
Defines an angle and provides unit conversions.
Definition: Angle.h:62
void swap(Hillshade &other)
Swap class data with other; this cannot throw an exception.
Definition: Hillshade.cpp:288
double resolution() const
Get the current resolution (meters per pixel).
Definition: Hillshade.cpp:149
Isis exception class.
Definition: IException.h:107
Namespace for ISIS/Bullet specific routines.
Definition: Apollo.h:31
bool isValid() const
This indicates whether we have a legitimate angle stored or are in an unset, or invalid, state.
Definition: Angle.cpp:110
QDebug operator<<(QDebug debug, const Hillshade &hillshade)
Print this class out to a QDebug object.
Definition: Hillshade.cpp:308
double m_pixelResolution
meters per pixel
Definition: Hillshade.h:59
Angle zenith() const
Get the current zenith angle.
Definition: Hillshade.cpp:134