Isis 3 Programmer Reference
SunShadowTool.cpp
1#include "SunShadowTool.h"
2
3#include <QApplication>
4#include <QComboBox>
5#include <QCheckBox>
6#include <QFileDialog>
7#include <QHBoxLayout>
8#include <QLineEdit>
9#include <QMenu>
10#include <QMenuBar>
11#include <QMessageBox>
12#include <QStatusBar>
13#include <QStackedWidget>
14#include <QTableWidget>
15#include <QToolButton>
16
17#include "Angle.h"
18#include "Camera.h"
19#include "Distance.h"
20#include "FileName.h"
21#include "Latitude.h"
22#include "Longitude.h"
23#include "MdiCubeViewport.h"
24#include "Projection.h"
25#include "SurfacePoint.h"
26#include "ToolPad.h"
27
28namespace Isis {
35 m_enabled = false;
36 m_tracking = false;
37 m_shadowHeight = NULL;
38 m_shadowLength = NULL;
39 m_trackingAngle = NULL;
42 m_endSurfacePoint = NULL;
43 m_incidenceAngle = NULL;
44
45 m_tableWin = new TableMainWindow("Sun Shadow Measurements", parent);
47 m_tableWin->installEventFilter(this);
48
49 m_tableWin->addToTable(false, "Feature\nName", "Feature Name");
50 m_tableWin->addToTable(false, "Feature\nType", "Feature Type");
52 "Start\nLatitude:Start\nLongitude:End\nLatitude:End\nLongitude",
53 "Ground Range", -1, Qt::Horizontal,
54 "Start Latitude/Longitude to End Latitude/Longitude");
56 "Start\nSample:Start\nLine:End\nSample:End\nLine",
57 "Pixel Range", -1, Qt::Horizontal,
58 "Start Sample/Line to End Sample/Line");
59 m_tableWin->addToTable(true, "Shadow Length\n(km)", "Shadow Length (km)");
60 m_tableWin->addToTable(true, "Shadow Length\n(m)", "Shadow Length (m)");
61 m_tableWin->addToTable(true, "Shadow Height\n(km)", "Shadow Height (km)");
62 m_tableWin->addToTable(true, "Shadow Height\n(m)", "Shadow Height (m)");
63 m_tableWin->addToTable(true, "Incidence Angle\n(degrees)",
64 "Incidence Angle (degrees)");
65 m_tableWin->addToTable(true, "Incidence Angle\n(radians)",
66 "Incidence Angle (radians)");
67 m_tableWin->addToTable(false, "Path", "Path");
68 m_tableWin->addToTable(false, "FileName", "FileName");
69 m_tableWin->addToTable(false, "Notes", "Notes");
70
71 m_tableWin->setStatusMessage("Click, Drag, and Release to Measure a Line");
72
73 connect(this, SIGNAL(viewportChanged()),
74 this, SLOT(reinitialize()));
75
82 }
83
84
92 QAction *action = new QAction(toolpad);
93 action->setIcon(QPixmap(toolIconDir() + "/sunshadow.png"));
94 action->setToolTip("Sun Shadow (U)");
95 action->setShortcut(Qt::Key_U);
96
97 QString text =
98 "<b>Function:</b> Calculate heights or depths of features in the active "
99 "viewport given the measurement of a shadow. The shadow measurement "
100 "should originate from the top of the feature and end when the shadow "
101 "ends.\n"
102 "<p><b>Shortcut:</b> U</p> ";
103 action->setWhatsThis(text);
104
105 return action;
106 }
107
108
118 QWidget *hbox = new QWidget(parent);
119 QToolButton *showTableButton = new QToolButton(hbox);
120 showTableButton->setText("Table");
121 showTableButton->setToolTip("Record Measurement Data in Table");
122 QString text =
123 "<b>Function:</b> This button will bring up a table that will record "
124 "the starting and ending points of the line, along with the calculated "
125 "values for the two points on the image. To measure a shadow, "
126 "click on the first point and releasing the mouse at the second point."
127 "\n<p><b>Shortcut:</b> CTRL+M</p>";
128 showTableButton->setWhatsThis(text);
129 showTableButton->setShortcut(Qt::CTRL + Qt::Key_M);
130 connect(showTableButton, SIGNAL(clicked()), m_tableWin, SLOT(showTable()));
131 connect(showTableButton, SIGNAL(clicked()), m_tableWin, SLOT(syncColumns()));
132 connect(showTableButton, SIGNAL(clicked()), m_tableWin, SLOT(raise()));
133 showTableButton->setEnabled(true);
134
135 m_shadowHeightLineEdit = new QLineEdit(hbox);
136 m_shadowHeightLineEdit->setText("");
137 m_shadowHeightLineEdit->setMaxLength(12);
138 m_shadowHeightLineEdit->setToolTip("Shadow Height");
139 text = "<b>Function: </b> Shows the height of the shadow drawn on "
140 "the image.";
141 m_shadowHeightLineEdit->setWhatsThis(text);
142 m_shadowHeightLineEdit->setReadOnly(true);
143
144 m_unitsComboBox = new QComboBox(hbox);
145 m_unitsComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
146 m_unitsComboBox->addItem("Meters", Distance::Meters);
147 m_unitsComboBox->addItem("Kilometers", Distance::Kilometers);
148
149 connect(m_unitsComboBox, SIGNAL(activated(int)),
150 this, SLOT(updateShadowHeightEdit()));
151
152 m_drawInSunDirection = new QCheckBox("Draw in Sun Direction");
153 m_drawInSunDirection->setChecked(true);
154
155 QHBoxLayout *layout = new QHBoxLayout(hbox);
156 layout->setMargin(0);
157 layout->addWidget(m_drawInSunDirection);
158 layout->addWidget(m_shadowHeightLineEdit);
159 layout->addWidget(m_unitsComboBox);
160 layout->addWidget(showTableButton);
161 layout->addStretch(1);
162 hbox->setLayout(layout);
163 return hbox;
164 }
165
166
174 }
175
176
184 void SunShadowTool::paintViewport(MdiCubeViewport *vp, QPainter *painter) {
185 if (vp == cubeViewport()) {
186 if (m_startSamp != Null && m_endSamp != Null &&
187 m_startLine != Null && m_endLine != Null) {
188 int vpStartX;
189 int vpStartY;
190 vp->cubeToViewport(m_startSamp, m_startLine,
191 vpStartX, vpStartY);
192
193 int vpEndX;
194 int vpEndY;
195 vp->cubeToViewport(m_endSamp, m_endLine,
196 vpEndX, vpEndY);
197
198 painter->setPen(QPen(Qt::red));
199 painter->drawLine(QPoint(vpStartX, vpStartY), QPoint(vpEndX, vpEndY));
200 }
201 }
202 }
203
204
213 cubeViewport()->viewportToCube(p.x(), p.y(),
215
216 if (m_drawInSunDirection->isChecked()) {
217 // Recalculate the end line based on our drawing angle...
218 // y = x * tan(angle) for the right triangle created from the
219 // user drawing a line.
220 //
221 // E
222 // / |
223 // / |
224 // / | L
225 // / |
226 // /A |
227 // S-------|
228 // S = Mouse Start Pos
229 // A = sun angle
230 // E = mouse end pos
231 // L = Line height of the triangle
232 // (needs calculated, L = m_endLine - m_startLine)
233 //
234 Angle verticalDown(90, Angle::Degrees);
235 Angle verticalUp(270, Angle::Degrees);
236 bool adjustLine = true;
237
238 if (*m_trackingAngle == verticalDown ||
239 *m_trackingAngle == verticalUp) {
241
242 adjustLine = false;
243 }
244
245 if (adjustLine) {
248 }
249 }
250
252
253 if (m_tableWin->table()->rowCount()) {
254 updateRow(m_tableWin->table()->rowCount() - 1);
255 }
256
257 cubeViewport()->viewport()->update();
258 }
259 }
260
261
268 void SunShadowTool::mouseButtonPress(QPoint p, Qt::MouseButton s) {
269 if (m_enabled && s == Qt::LeftButton) {
270 if (m_tableWin->isVisible())
271 addRow();
272
273 reinitialize();
274
275 cubeViewport()->viewportToCube(p.x(), p.y(),
277
278 Camera *cam = cubeViewport()->cube()->camera();
279
280 if (cam->SetImage(m_startSamp, m_startLine)) {
281 m_tracking = true;
282 *m_trackingAngle = Angle(cam->SunAzimuth(), Angle::Degrees);
283 }
284 else {
285 m_tracking = false;
288 }
289 cubeViewport()->viewport()->update();
290 }
291 }
292
293
300 void SunShadowTool::mouseButtonRelease(QPoint p, Qt::MouseButton s) {
301 if (s == Qt::LeftButton && m_tracking) {
302 mouseMove(p);
303 }
304
305 m_tracking = false;
306 }
307
308
309
318
319 if (row >= m_tableWin->table()->rowCount() || !
320 m_tableWin->isVisible()) {
321 return;
322 }
323
324 // Blank out the row to remove stuff left over from previous cvps
325 for (int c = 0; c < m_tableWin->table()->columnCount(); c++) {
326 m_tableWin->table()->item(row, c)->setText("");
327 }
328
329 // Write all the new info to the current row
330 if (m_startSurfacePoint->Valid()) {
331 m_tableWin->table()->item(row, StartLatIndex)->setText(
332 QString::number(m_startSurfacePoint->GetLatitude().degrees()));
333 m_tableWin->table()->item(row, StartLonIndex)->setText(
334 QString::number(m_startSurfacePoint->GetLongitude().degrees()));
335 }
336 else {
337 m_tableWin->table()->item(row, StartLatIndex)->setText("N/A");
338 m_tableWin->table()->item(row, StartLonIndex)->setText("N/A");
339 }
340
341 if (m_endSurfacePoint->Valid()) {
342 m_tableWin->table()->item(row, EndLatIndex)->setText(
343 QString::number(m_endSurfacePoint->GetLatitude().degrees()));
344 m_tableWin->table()->item(row, EndLonIndex)->setText(
345 QString::number(m_endSurfacePoint->GetLongitude().degrees()));
346 }
347 else {
348 m_tableWin->table()->item(row, EndLatIndex)->setText("N/A");
349 m_tableWin->table()->item(row, EndLonIndex)->setText("N/A");
350 }
351
352 if (m_startSamp != Null && m_startLine != Null) {
353 m_tableWin->table()->item(row, StartSampIndex)->setText(
354 QString::number(m_startSamp));
355 m_tableWin->table()->item(row, StartLineIndex)->setText(
356 QString::number(m_startLine));
357 }
358 else {
359 m_tableWin->table()->item(row, StartSampIndex)->setText("N/A");
360 m_tableWin->table()->item(row, StartLineIndex)->setText("N/A");
361 }
362
363 if (m_endSamp != Null && m_endLine != Null) {
364 m_tableWin->table()->item(row, EndSampIndex)->setText(
365 QString::number(m_endSamp));
366 m_tableWin->table()->item(row, EndLineIndex)->setText(
367 QString::number(m_endLine));
368 }
369 else {
370 m_tableWin->table()->item(row, EndSampIndex)->setText("N/A");
371 m_tableWin->table()->item(row, EndLineIndex)->setText("N/A");
372 }
373
374 if (m_shadowLength->isValid()) {
375 m_tableWin->table()->item(row, ShadowLengthKmIndex)->setText(
376 QString::number(m_shadowLength->kilometers()));
377 m_tableWin->table()->item(row, ShadowLengthMIndex)->setText(
378 QString::number(m_shadowLength->meters()));
379 }
380 else {
381 m_tableWin->table()->item(row, ShadowLengthKmIndex)->setText("N/A");
382 m_tableWin->table()->item(row, ShadowLengthMIndex)->setText("N/A");
383 }
384
385 if (m_shadowHeight->isValid()) {
386 m_tableWin->table()->item(row, ShadowHeightKmIndex)->setText(
387 QString::number(m_shadowHeight->kilometers()));
388 m_tableWin->table()->item(row, ShadowHeightMIndex)->setText(
389 QString::number(m_shadowHeight->meters()));
390 }
391 else {
392 m_tableWin->table()->item(row, ShadowHeightKmIndex)->setText("N/A");
393 m_tableWin->table()->item(row, ShadowHeightMIndex)->setText("N/A");
394 }
395
396 if (m_incidenceAngle->isValid()) {
397 m_tableWin->table()->item(row, IncidenceAngleDegreesIndex)->setText(
398 QString::number(m_incidenceAngle->degrees()));
399 m_tableWin->table()->item(row, IncidenceAngleRadiansIndex)->setText(
400 QString::number(m_incidenceAngle->radians()));
401 }
402 else {
403 m_tableWin->table()->item(row, IncidenceAngleDegreesIndex)->setText("N/A");
404 m_tableWin->table()->item(row, IncidenceAngleRadiansIndex)->setText("N/A");
405 }
406
407 m_tableWin->table()->item(row, PathIndex)->setText(m_path);
408 m_tableWin->table()->item(row, FileNameIndex)->setText(m_fileName);
409 }
410
411
429
430
435 int newRowPos = m_tableWin->table()->rowCount();
436 m_tableWin->table()->insertRow(newRowPos);
437 for (int c = 0; c < m_tableWin->table()->columnCount(); c++) {
438 QTableWidgetItem *item = new QTableWidgetItem("");
439 m_tableWin->table()->setItem(newRowPos, c, item);
440 }
441 m_tableWin->table()->scrollToItem(m_tableWin->table()->item(newRowPos, 0),
442 QAbstractItemView::PositionAtBottom);
443 }
444
445
451 try {
452 if (m_startSamp != Null && m_startLine != Null &&
453 m_endSamp != Null && m_endLine != Null) {
454 m_path = FileName(cubeViewport()->cube()->fileName()).path();
455 m_fileName = FileName(cubeViewport()->cube()->fileName()).name();
456
457 /* |
458 * \ _ /
459 * -= (_) =- THE SUN
460 * / \ -
461 * | - <--- vector from the sun that intersects P1 and P2
462 * -
463 * -_ |
464 * /^\- |
465 * / | \ - |
466 * / H| \ - |
467 * ________/ | \__T_-|_________
468 * P1 ^ P2
469 * Shadow
470 *
471 * T: Angle from the horizon to the sun
472 * H: Difference in planetary radius between P1 and P2
473 * L : length(Shadow)
474 * H = L * tan(T)
475 *
476 * We do not want the local incidence angle for T.
477 *
478 * Equation to variable mapping:
479 * T: theta
480 * H: m_shadowHeight
481 * L: m_shadowLength
482 * P1: m_startSurfacePoint
483 * P2: m_endSurfacePoint
484 */
485
486 bool success = true;
487 Camera *cam = cubeViewport()->cube()->camera();
488 success = cam->SetImage(m_startSamp, m_startLine);
489
490 // Vector is in meters
491 QVector3D sunDirection;
492
493 if (success) {
494 *m_startSurfacePoint = cam->GetSurfacePoint();
495 double sunPosition[3];
496 cam->sunPosition(sunPosition);
497
498 Distance targetRadii[3];
499 cam->radii(targetRadii);
500
501 double origin[3] = {0.0, 0.0, 0.0};
502 SpiceBoolean surfptSuccess;
503 // Vector is in kilometers
504 double naifVectorFromSunToP1[3] = {0.0, 0.0, 0.0};
505
506 surfpt_c(origin, sunPosition, targetRadii[0].kilometers(),
507 targetRadii[1].kilometers(), targetRadii[2].kilometers(),
508 naifVectorFromSunToP1, &surfptSuccess);
509 success = surfptSuccess;
510
511 if (success) {
512 sunDirection = QVector3D(
513 naifVectorFromSunToP1[0] * 1000.0,
514 naifVectorFromSunToP1[1] * 1000.0,
515 naifVectorFromSunToP1[2] * 1000.0).normalized();
516 }
517 }
518
519 if (success) {
520 success = cam->SetImage(m_endSamp, m_endLine);
521 }
522
523 if (success) {
524 *m_endSurfacePoint = cam->GetSurfacePoint();
525
526 *m_incidenceAngle = Angle(cam->IncidenceAngle(), Angle::Degrees);
527 Angle theta = Angle(90.0, Angle::Degrees) - *m_incidenceAngle;
528
529 Displacement deltaX = m_startSurfacePoint->GetX() - m_endSurfacePoint->GetX();
530
531 Displacement deltaY = m_startSurfacePoint->GetY() - m_endSurfacePoint->GetY();
532
533 Displacement deltaZ = m_startSurfacePoint->GetZ() - m_endSurfacePoint->GetZ();
534
535 *m_shadowLength = Distance(sqrt( deltaX.meters() * deltaX.meters() +
536 deltaY.meters() * deltaY.meters() +
537 deltaZ.meters() * deltaZ.meters() ),
539
540 *m_shadowHeight = Distance(m_shadowLength->meters() * tan( theta.radians() ),
542 }
543 }
544 }
545 catch (IException &) {
546 reinitialize();
547 }
548
550 }
551
552
555 if (m_shadowHeight->isValid()) {
556 Distance::Units displayUnits =
558 m_unitsComboBox->currentIndex()).toInt();
559
560 switch (displayUnits) {
561 case Distance::Meters:
562 m_shadowHeightLineEdit->setText(
564 break;
566 m_shadowHeightLineEdit->setText(
568 break;
570 case Distance::Pixels:
571 m_shadowHeightLineEdit->setText("Not Supported");
572 break;
573 }
574
575 }
576 else {
577 m_shadowHeightLineEdit->setText("");
578 }
579 }
580
581
587 MdiCubeViewport *activeViewport = cubeViewport();
588
589 bool hasCamera = true;
590 try {
591 hasCamera = activeViewport &&
592 (activeViewport->cube()->camera() != NULL);
593 }
594 catch (IException &) {
595 hasCamera = false;
596 }
597
598 m_shadowHeightLineEdit->setEnabled(hasCamera);
599 m_unitsComboBox->setEnabled(hasCamera);
600 m_enabled = hasCamera;
601
603 }
604
605}
606
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
double degrees() const
Get the angle in units of Degrees.
Definition Angle.h:232
@ Degrees
Degrees are generally considered more human readable, 0-360 is one circle, however most math does not...
Definition Angle.h:56
Camera * camera()
Return a camera associated with the cube.
Definition Cube.cpp:1457
void viewportToCube(int x, int y, double &sample, double &line) const
Turns a viewport into a cube.
Cube * cube() const
Displacement is a signed length, usually in meters.
Distance measurement, usually in meters.
Definition Distance.h:34
bool isValid() const
Test if this distance has been initialized or not.
Definition Distance.cpp:192
double kilometers() const
Get the distance in kilometers.
Definition Distance.cpp:106
Units
This is a list of available units to access and store Distances in.
Definition Distance.h:41
@ SolarRadii
"Solar radius is a unit of distance used to express the size of stars in astronomy equal to the curre...
Definition Distance.h:59
@ Pixels
The distance is being specified in pixels.
Definition Distance.h:47
@ Kilometers
The distance is being specified in kilometers.
Definition Distance.h:45
@ Meters
The distance is being specified in meters.
Definition Distance.h:43
double meters() const
Get the distance in meters.
Definition Distance.cpp:85
File name manipulation and expansion.
Definition FileName.h:100
QString path() const
Returns the path of the file name.
Definition FileName.cpp:103
QString name() const
Returns the name of the file excluding the path and the attributes in the file name.
Definition FileName.cpp:162
Isis exception class.
Definition IException.h:91
Cube display widget for certain Isis MDI applications.
void updateShadowHeightEdit()
Change the value in the distance edit to match the units.
SurfacePoint * m_endSurfacePoint
End ground coordinate of the measurement.
QComboBox * m_unitsComboBox
User can choose the height line edit's units (M or KM)
QAction * toolPadAction(ToolPad *pad)
Create an action for activating this tool.
QString m_path
Current cube file path.
QLineEdit * m_shadowHeightLineEdit
This displays the currently calculated height of the measured shadow.
void recalculateShadowHeight()
Try to calculate the shadow height.
void updateTool()
This enables/disables this tool's functionality based on the active viewport's compatibility.
virtual void mouseButtonPress(QPoint p, Qt::MouseButton s)
When the mouse left button is pressed we start tracking.
@ StartLonIndex
Start lon table column index.
@ EndLineIndex
End cube line table column index.
@ ShadowHeightMIndex
Shadow height in meters table column index.
@ IncidenceAngleDegreesIndex
Incidence angle in degrees table column index.
@ StartLatIndex
Start lat table column index.
@ StartLineIndex
Start cube line table column index.
@ ShadowLengthKmIndex
Shadow length in kilometers table column index.
@ IncidenceAngleRadiansIndex
Incidence angle in radians table column index.
@ ShadowLengthMIndex
Shadow length in meters table column index.
@ EndSampIndex
End cube sample table column index.
@ EndLonIndex
End lon table column index.
@ ShadowHeightKmIndex
Shadow height in kilometers table column index.
@ EndLatIndex
End lat table column index.
@ FileNameIndex
Cube file name table column index.
@ StartSampIndex
Start cube sample table column index.
@ PathIndex
Cube file path table column index.
QWidget * createToolBarWidget(QStackedWidget *parent)
Creates the widget (button) that goes on the tool bar.
Distance * m_shadowHeight
Calculated shadow height.
Distance * m_shadowLength
Calculated shadow length.
void updateRow(int row)
This method updates the row in the table window with the current measure information.
bool m_tracking
True if currently tracking the user's mouse position and calculating values on every mouse move.
double m_endLine
End line of the measurement.
QString m_fileName
Current cube file name.
void paintViewport(MdiCubeViewport *vp, QPainter *painter)
Paint anything we need to on the viewport.
QCheckBox * m_drawInSunDirection
Check box to enable/disable confining tracking to sun direction.
SurfacePoint * m_startSurfacePoint
Start ground coordinate of the measurement.
SunShadowTool(QWidget *parent)
Construct a sun shadow tool.
Angle * m_incidenceAngle
Incidence angle from the normal at the end point.
void reinitialize()
Clear all calculated values and then re-calculate them.
virtual void mouseButtonRelease(QPoint p, Qt::MouseButton s)
When the mouse left button is released we finish tracking.
bool m_enabled
True if this tool is enabled (capable of working). Requires a camera.
virtual void mouseMove(QPoint p)
When the mouse moves, if we're tracking then we go ahead and update all of our calculated values for ...
double m_startSamp
Start sample of the measurement.
Angle * m_trackingAngle
The angle that we want mouse tracking to be in.
double m_endSamp
End sample of the measurement.
TableMainWindow * m_tableWin
Table window for displaying all of the table information.
void addTo(QMenu *menu)
Adds the measure action to the given menu.
double m_startLine
Start line of the measurement.
void addRow()
Add a results row to the table.
This class defines a body-fixed surface point.
Latitude GetLatitude() const
Return the body-fixed latitude for the surface point.
Longitude GetLongitude() const
Return the body-fixed longitude for the surface point.
a subclass of the qisis mainwindow, tablemainwindow handles all of the table tasks.
QTableWidget * table() const
Returns the table.
void addToTable(bool setOn, const QString &heading, const QString &menuText="", int insertAt=-1, Qt::Orientation o=Qt::Horizontal, QString toolTip="")
Adds a new column to the table when a new curve is added to the plot.
void setTrackListItems(bool track=false)
If this property is true, the class will keep track of the checked/unchecked items in the dock area w...
void setStatusMessage(QString message)
sets the status message in the lower lefthand corner of the window.
Base class for the Qisis tools.
Definition Tool.h:67
MdiCubeViewport * cubeViewport() const
Return the current cubeviewport.
Definition Tool.h:197
QString toolIconDir() const
returns the path to the icon directory.
Definition Tool.h:113
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
int toInt(const QString &string)
Global function to convert from a string to an integer.
Definition IString.cpp:93
const double Null
Value for an Isis Null pixel.