USGS

Isis 3.0 Object Programmers' Reference

Home

SunShadowTool.cpp
1 #include "IsisDebug.h"
2 #include "SunShadowTool.h"
3 
4 #include <QApplication>
5 #include <QFileDialog>
6 #include <QHBoxLayout>
7 #include <QLineEdit>
8 #include <QMenu>
9 #include <QMenuBar>
10 #include <QMessageBox>
11 #include <QStatusBar>
12 
13 #include "Angle.h"
14 #include "Camera.h"
15 #include "Distance.h"
16 #include "FileName.h"
17 #include "Latitude.h"
18 #include "Longitude.h"
19 #include "MdiCubeViewport.h"
20 #include "Projection.h"
21 #include "SurfacePoint.h"
22 #include "ToolPad.h"
23 
24 namespace Isis {
31  m_enabled = false;
32  m_tracking = false;
33  m_shadowHeight = NULL;
34  m_shadowLength = NULL;
35  m_trackingAngle = NULL;
36  m_drawInSunDirection = NULL;
37  m_startSurfacePoint = NULL;
38  m_endSurfacePoint = NULL;
39  m_incidenceAngle = NULL;
40 
41  m_tableWin = new TableMainWindow("Sun Shadow Measurements", parent);
43  m_tableWin->installEventFilter(this);
44 
45  m_tableWin->addToTable(false, "Feature\nName", "Feature Name");
46  m_tableWin->addToTable(false, "Feature\nType", "Feature Type");
47  m_tableWin->addToTable(true,
48  "Start\nLatitude:Start\nLongitude:End\nLatitude:End\nLongitude",
49  "Ground Range", -1, Qt::Horizontal,
50  "Start Latitude/Longitude to End Latitude/Longitude");
51  m_tableWin->addToTable(false,
52  "Start\nSample:Start\nLine:End\nSample:End\nLine",
53  "Pixel Range", -1, Qt::Horizontal,
54  "Start Sample/Line to End Sample/Line");
55  m_tableWin->addToTable(true, "Shadow Length\n(km)", "Shadow Length (km)");
56  m_tableWin->addToTable(true, "Shadow Length\n(m)", "Shadow Length (m)");
57  m_tableWin->addToTable(true, "Shadow Height\n(km)", "Shadow Height (km)");
58  m_tableWin->addToTable(true, "Shadow Height\n(m)", "Shadow Height (m)");
59  m_tableWin->addToTable(true, "Incidence Angle\n(degrees)",
60  "Incidence Angle (degrees)");
61  m_tableWin->addToTable(true, "Incidence Angle\n(radians)",
62  "Incidence Angle (radians)");
63  m_tableWin->addToTable(false, "Path", "Path");
64  m_tableWin->addToTable(false, "FileName", "FileName");
65  m_tableWin->addToTable(false, "Notes", "Notes");
66 
67  m_tableWin->setStatusMessage("Click, Drag, and Release to Measure a Line");
68 
69  connect(this, SIGNAL(viewportChanged()),
70  this, SLOT(reinitialize()));
71 
74  m_trackingAngle = new Angle;
77  m_incidenceAngle = new Angle;
78  }
79 
80 
88  QAction *action = new QAction(toolpad);
89  action->setIcon(QPixmap(toolIconDir() + "/sunshadow.png"));
90  action->setToolTip("Sun Shadow (U)");
91  action->setShortcut(Qt::Key_U);
92 
93  QString text =
94  "<b>Function:</b> Calculate heights or depths of features in the active "
95  "viewport given the measurement of a shadow. The shadow measurement "
96  "should originate from the top of the feature and end when the shadow "
97  "ends.\n"
98  "<p><b>Shortcut:</b> U</p> ";
99  action->setWhatsThis(text);
100 
101  return action;
102  }
103 
104 
113  QWidget *SunShadowTool::createToolBarWidget(QStackedWidget *parent) {
114  QWidget *hbox = new QWidget(parent);
115  QToolButton *showTableButton = new QToolButton(hbox);
116  showTableButton->setText("Table");
117  showTableButton->setToolTip("Record Measurement Data in Table");
118  QString text =
119  "<b>Function:</b> This button will bring up a table that will record "
120  "the starting and ending points of the line, along with the calculated "
121  "values for the two points on the image. To measure a shadow, "
122  "click on the first point and releasing the mouse at the second point."
123  "\n<p><b>Shortcut:</b> CTRL+M</p>";
124  showTableButton->setWhatsThis(text);
125  showTableButton->setShortcut(Qt::CTRL + Qt::Key_M);
126  connect(showTableButton, SIGNAL(clicked()), m_tableWin, SLOT(showTable()));
127  connect(showTableButton, SIGNAL(clicked()), m_tableWin, SLOT(syncColumns()));
128  connect(showTableButton, SIGNAL(clicked()), m_tableWin, SLOT(raise()));
129  showTableButton->setEnabled(true);
130 
131  m_shadowHeightLineEdit = new QLineEdit(hbox);
132  m_shadowHeightLineEdit->setText("");
133  m_shadowHeightLineEdit->setMaxLength(12);
134  m_shadowHeightLineEdit->setToolTip("Shadow Height");
135  text = "<b>Function: </b> Shows the height of the shadow drawn on "
136  "the image.";
137  m_shadowHeightLineEdit->setWhatsThis(text);
138  m_shadowHeightLineEdit->setReadOnly(true);
139 
140  m_unitsComboBox = new QComboBox(hbox);
141  m_unitsComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
142  m_unitsComboBox->addItem("Meters", Distance::Meters);
143  m_unitsComboBox->addItem("Kilometers", Distance::Kilometers);
144 
145  connect(m_unitsComboBox, SIGNAL(activated(int)),
146  this, SLOT(updateShadowHeightEdit()));
147 
148  m_drawInSunDirection = new QCheckBox("Draw in Sun Direction");
149  m_drawInSunDirection->setChecked(true);
150 
151  QHBoxLayout *layout = new QHBoxLayout(hbox);
152  layout->setMargin(0);
153  layout->addWidget(m_drawInSunDirection);
154  layout->addWidget(m_shadowHeightLineEdit);
155  layout->addWidget(m_unitsComboBox);
156  layout->addWidget(showTableButton);
157  layout->addStretch(1);
158  hbox->setLayout(layout);
159  return hbox;
160  }
161 
162 
169  void SunShadowTool::addTo(QMenu *menu) {
170  }
171 
172 
180  void SunShadowTool::paintViewport(MdiCubeViewport *vp, QPainter *painter) {
181  if (vp == cubeViewport()) {
182  if (m_startSamp != Null && m_endSamp != Null &&
183  m_startLine != Null && m_endLine != Null) {
184  int vpStartX;
185  int vpStartY;
187  vpStartX, vpStartY);
188 
189  int vpEndX;
190  int vpEndY;
192  vpEndX, vpEndY);
193 
194  painter->setPen(QPen(Qt::red));
195  painter->drawLine(QPoint(vpStartX, vpStartY), QPoint(vpEndX, vpEndY));
196  }
197  }
198  }
199 
200 
207  void SunShadowTool::mouseMove(QPoint p) {
208  if (m_tracking && m_trackingAngle->isValid()) {
209  cubeViewport()->viewportToCube(p.x(), p.y(),
211 
212  if (m_drawInSunDirection->isChecked()) {
213  // Recalculate the end line based on our drawing angle...
214  // y = x * tan(angle) for the right triangle created from the
215  // user drawing a line.
216  //
217  // E
218  // / |
219  // / |
220  // / | L
221  // / |
222  // /A |
223  // S-------|
224  // S = Mouse Start Pos
225  // A = sun angle
226  // E = mouse end pos
227  // L = Line height of the triangle
228  // (needs calculated, L = m_endLine - m_startLine)
229  //
230  Angle verticalDown(90, Angle::Degrees);
231  Angle verticalUp(270, Angle::Degrees);
232  bool adjustLine = true;
233 
234  if (*m_trackingAngle == verticalDown ||
235  *m_trackingAngle == verticalUp) {
237 
238  adjustLine = false;
239  }
240 
241  if (adjustLine) {
244  }
245  }
246 
248 
249  if (m_tableWin->table()->rowCount()) {
250  updateRow(m_tableWin->table()->rowCount() - 1);
251  }
252 
253  cubeViewport()->viewport()->update();
254  }
255  }
256 
257 
264  void SunShadowTool::mouseButtonPress(QPoint p, Qt::MouseButton s) {
265  if (m_enabled && s == Qt::LeftButton) {
266  if (m_tableWin->isVisible())
267  addRow();
268 
269  reinitialize();
270 
271  cubeViewport()->viewportToCube(p.x(), p.y(),
273 
274  Camera *cam = cubeViewport()->cube()->camera();
275 
276  if (cam->SetImage(m_startSamp, m_startLine)) {
277  m_tracking = true;
279  }
280  else {
281  m_tracking = false;
282  m_startSamp = Null;
283  m_startLine = Null;
284  }
285  cubeViewport()->viewport()->update();
286  }
287  }
288 
289 
296  void SunShadowTool::mouseButtonRelease(QPoint p, Qt::MouseButton s) {
297  if (s == Qt::LeftButton && m_tracking) {
298  mouseMove(p);
299  }
300 
301  m_tracking = false;
302  }
303 
304 
305 
313  void SunShadowTool::updateRow(int row) {
314  ASSERT(row < m_tableWin->table()->rowCount());
315 
316  if (row >= m_tableWin->table()->rowCount() || !
317  m_tableWin->isVisible()) {
318  return;
319  }
320 
321  // Blank out the row to remove stuff left over from previous cvps
322  for (int c = 0; c < m_tableWin->table()->columnCount(); c++) {
323  m_tableWin->table()->item(row, c)->setText("");
324  }
325 
326  // Write all the new info to the current row
327  if (m_startSurfacePoint->Valid()) {
328  m_tableWin->table()->item(row, StartLatIndex)->setText(
329  QString::number(m_startSurfacePoint->GetLatitude().degrees()));
330  m_tableWin->table()->item(row, StartLonIndex)->setText(
331  QString::number(m_startSurfacePoint->GetLongitude().degrees()));
332  }
333  else {
334  m_tableWin->table()->item(row, StartLatIndex)->setText("N/A");
335  m_tableWin->table()->item(row, StartLonIndex)->setText("N/A");
336  }
337 
338  if (m_endSurfacePoint->Valid()) {
339  m_tableWin->table()->item(row, EndLatIndex)->setText(
340  QString::number(m_endSurfacePoint->GetLatitude().degrees()));
341  m_tableWin->table()->item(row, EndLonIndex)->setText(
342  QString::number(m_endSurfacePoint->GetLongitude().degrees()));
343  }
344  else {
345  m_tableWin->table()->item(row, EndLatIndex)->setText("N/A");
346  m_tableWin->table()->item(row, EndLonIndex)->setText("N/A");
347  }
348 
349  if (m_startSamp != Null && m_startLine != Null) {
350  m_tableWin->table()->item(row, StartSampIndex)->setText(
351  QString::number(m_startSamp));
352  m_tableWin->table()->item(row, StartLineIndex)->setText(
353  QString::number(m_startLine));
354  }
355  else {
356  m_tableWin->table()->item(row, StartSampIndex)->setText("N/A");
357  m_tableWin->table()->item(row, StartLineIndex)->setText("N/A");
358  }
359 
360  if (m_endSamp != Null && m_endLine != Null) {
361  m_tableWin->table()->item(row, EndSampIndex)->setText(
362  QString::number(m_endSamp));
363  m_tableWin->table()->item(row, EndLineIndex)->setText(
364  QString::number(m_endLine));
365  }
366  else {
367  m_tableWin->table()->item(row, EndSampIndex)->setText("N/A");
368  m_tableWin->table()->item(row, EndLineIndex)->setText("N/A");
369  }
370 
371  if (m_shadowLength->isValid()) {
372  m_tableWin->table()->item(row, ShadowLengthKmIndex)->setText(
373  QString::number(m_shadowLength->kilometers()));
374  m_tableWin->table()->item(row, ShadowLengthMIndex)->setText(
375  QString::number(m_shadowLength->meters()));
376  }
377  else {
378  m_tableWin->table()->item(row, ShadowLengthKmIndex)->setText("N/A");
379  m_tableWin->table()->item(row, ShadowLengthMIndex)->setText("N/A");
380  }
381 
382  if (m_shadowHeight->isValid()) {
383  m_tableWin->table()->item(row, ShadowHeightKmIndex)->setText(
384  QString::number(m_shadowHeight->kilometers()));
385  m_tableWin->table()->item(row, ShadowHeightMIndex)->setText(
386  QString::number(m_shadowHeight->meters()));
387  }
388  else {
389  m_tableWin->table()->item(row, ShadowHeightKmIndex)->setText("N/A");
390  m_tableWin->table()->item(row, ShadowHeightMIndex)->setText("N/A");
391  }
392 
393  if (m_incidenceAngle->isValid()) {
394  m_tableWin->table()->item(row, IncidenceAngleDegreesIndex)->setText(
395  QString::number(m_incidenceAngle->degrees()));
396  m_tableWin->table()->item(row, IncidenceAngleRadiansIndex)->setText(
397  QString::number(m_incidenceAngle->radians()));
398  }
399  else {
400  m_tableWin->table()->item(row, IncidenceAngleDegreesIndex)->setText("N/A");
401  m_tableWin->table()->item(row, IncidenceAngleRadiansIndex)->setText("N/A");
402  }
403 
404  m_tableWin->table()->item(row, PathIndex)->setText(m_path);
405  m_tableWin->table()->item(row, FileNameIndex)->setText(m_fileName);
406  }
407 
408 
413  m_startSamp = Null;
414  m_endSamp = Null;
415  m_startLine = Null;
416  m_endLine = Null;
417 
422  *m_incidenceAngle = Angle();
423 
425  }
426 
427 
432  int newRowPos = m_tableWin->table()->rowCount();
433  m_tableWin->table()->insertRow(newRowPos);
434  for (int c = 0; c < m_tableWin->table()->columnCount(); c++) {
435  QTableWidgetItem *item = new QTableWidgetItem("");
436  m_tableWin->table()->setItem(newRowPos, c, item);
437  }
438  m_tableWin->table()->scrollToItem(m_tableWin->table()->item(newRowPos, 0),
439  QAbstractItemView::PositionAtBottom);
440  }
441 
442 
448  try {
449  if (m_startSamp != Null && m_startLine != Null &&
450  m_endSamp != Null && m_endLine != Null) {
451  m_path = FileName(cubeViewport()->cube()->fileName()).path();
452  m_fileName = FileName(cubeViewport()->cube()->fileName()).name();
453 
454  /* |
455  * \ _ /
456  * -= (_) =- THE SUN
457  * / \ -
458  * | - <--- vector from the sun that intersects P1 and P2
459  * -
460  * -_ |
461  * /^\- |
462  * / | \ - |
463  * / H| \ - |
464  * ________/ | \__T_-|_________
465  * P1 ^ P2
466  * Shadow
467  *
468  * T: Angle from the horizon to the sun
469  * H: Difference in planetary radius between P1 and P2
470  * L : length(Shadow)
471  * H = L * tan(T)
472  *
473  * We do not want the local incidence angle for T.
474  *
475  * Equation to variable mapping:
476  * T: theta
477  * H: m_shadowHeight
478  * L: m_shadowLength
479  * P1: m_startSurfacePoint
480  * P2: m_endSurfacePoint
481  */
482 
483  bool success = true;
484  Camera *cam = cubeViewport()->cube()->camera();
485  success = cam->SetImage(m_startSamp, m_startLine);
486 
487  // Vector is in meters
488  QVector3D sunDirection;
489 
490  if (success) {
492  double sunPosition[3];
493  cam->sunPosition(sunPosition);
494 
495  Distance targetRadii[3];
496  cam->radii(targetRadii);
497 
498  double origin[3] = {0.0, 0.0, 0.0};
499  SpiceBoolean surfptSuccess;
500  // Vector is in kilometers
501  double naifVectorFromSunToP1[3] = {0.0, 0.0, 0.0};
502 
503  surfpt_c(origin, sunPosition, targetRadii[0].kilometers(),
504  targetRadii[1].kilometers(), targetRadii[2].kilometers(),
505  naifVectorFromSunToP1, &surfptSuccess);
506  success = surfptSuccess;
507 
508  if (success) {
509  sunDirection = QVector3D(
510  naifVectorFromSunToP1[0] * 1000.0,
511  naifVectorFromSunToP1[1] * 1000.0,
512  naifVectorFromSunToP1[2] * 1000.0).normalized();
513  }
514  }
515 
516  if (success) {
517  success = cam->SetImage(m_endSamp, m_endLine);
518  }
519 
520  if (success) {
522 
524  Angle theta = Angle(90.0, Angle::Degrees) - *m_incidenceAngle;
525 
526  Displacement deltaX = m_startSurfacePoint->GetX() - m_endSurfacePoint->GetX();
527 
528  Displacement deltaY = m_startSurfacePoint->GetY() - m_endSurfacePoint->GetY();
529 
530  Displacement deltaZ = m_startSurfacePoint->GetZ() - m_endSurfacePoint->GetZ();
531 
532  *m_shadowLength = Distance(sqrt( deltaX.meters() * deltaX.meters() +
533  deltaY.meters() * deltaY.meters() +
534  deltaZ.meters() * deltaZ.meters() ),
536 
537  *m_shadowHeight = Distance(m_shadowLength->meters() * tan( theta.radians() ),
539  }
540  }
541  }
542  catch (IException &) {
543  reinitialize();
544  }
545 
547  }
548 
549 
552  if (m_shadowHeight->isValid()) {
553  Distance::Units displayUnits =
554  (Distance::Units)m_unitsComboBox->itemData(
555  m_unitsComboBox->currentIndex()).toInt();
556 
557  switch (displayUnits) {
558  case Distance::Meters:
559  m_shadowHeightLineEdit->setText(
561  break;
563  m_shadowHeightLineEdit->setText(
565  break;
567  case Distance::Pixels:
568  m_shadowHeightLineEdit->setText("Not Supported");
569  break;
570  }
571 
572  }
573  else {
574  m_shadowHeightLineEdit->setText("");
575  }
576  }
577 
578 
584  MdiCubeViewport *activeViewport = cubeViewport();
585 
586  bool hasCamera = true;
587  try {
588  hasCamera = activeViewport &&
589  (activeViewport->cube()->camera() != NULL);
590  }
591  catch (IException &) {
592  hasCamera = false;
593  }
594 
595  m_shadowHeightLineEdit->setEnabled(hasCamera);
596  m_unitsComboBox->setEnabled(hasCamera);
597  m_enabled = hasCamera;
598 
600  }
601 
602 }
603