USGS

Isis 3.0 Object Programmers' Reference

Home

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 
37  setAzimuth(azimuth);
38  setZenith(zenith);
39  setResolution(resolution);
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().toAscii().data()
315  << "zenith =" << hillshade.zenith().toString().toAscii().data()
316  << "resolution =" << resolution.toAscii().data() << "]";
317 
318  return debug;
319  }
320 }