USGS

Isis 3.0 Object Programmers' Reference

Home

SunShadowTool.cpp

00001 #include "IsisDebug.h"
00002 #include "SunShadowTool.h"
00003 
00004 #include <QApplication>
00005 #include <QFileDialog>
00006 #include <QHBoxLayout>
00007 #include <QLineEdit>
00008 #include <QMenu>
00009 #include <QMenuBar>
00010 #include <QMessageBox>
00011 #include <QStatusBar>
00012 
00013 #include "Angle.h"
00014 #include "Camera.h"
00015 #include "Distance.h"
00016 #include "FileName.h"
00017 #include "Latitude.h"
00018 #include "Longitude.h"
00019 #include "MdiCubeViewport.h"
00020 #include "Projection.h"
00021 #include "SurfacePoint.h"
00022 #include "ToolPad.h"
00023 
00024 namespace Isis {
00030   SunShadowTool::SunShadowTool(QWidget *parent) : Tool(parent) {
00031     m_enabled = false;
00032     m_tracking = false;
00033     m_shadowHeight = NULL;
00034     m_shadowLength = NULL;
00035     m_trackingAngle = NULL;
00036     m_drawInSunDirection = NULL;
00037     m_startSurfacePoint = NULL;
00038     m_endSurfacePoint = NULL;
00039     m_incidenceAngle = NULL;
00040 
00041     m_tableWin = new TableMainWindow("Sun Shadow Measurements", parent);
00042     m_tableWin->setTrackListItems(true);
00043     m_tableWin->installEventFilter(this);
00044 
00045     m_tableWin->addToTable(false, "Feature\nName", "Feature Name");
00046     m_tableWin->addToTable(false, "Feature\nType", "Feature Type");
00047     m_tableWin->addToTable(true,
00048         "Start\nLatitude:Start\nLongitude:End\nLatitude:End\nLongitude",
00049         "Ground Range", -1, Qt::Horizontal,
00050         "Start Latitude/Longitude to End Latitude/Longitude");
00051     m_tableWin->addToTable(false,
00052         "Start\nSample:Start\nLine:End\nSample:End\nLine",
00053         "Pixel Range", -1, Qt::Horizontal,
00054         "Start Sample/Line to End Sample/Line");
00055     m_tableWin->addToTable(true, "Shadow Length\n(km)", "Shadow Length (km)");
00056     m_tableWin->addToTable(true, "Shadow Length\n(m)", "Shadow Length (m)");
00057     m_tableWin->addToTable(true, "Shadow Height\n(km)", "Shadow Height (km)");
00058     m_tableWin->addToTable(true, "Shadow Height\n(m)", "Shadow Height (m)");
00059     m_tableWin->addToTable(true, "Incidence Angle\n(degrees)",
00060                            "Incidence Angle (degrees)");
00061     m_tableWin->addToTable(true, "Incidence Angle\n(radians)",
00062                            "Incidence Angle (radians)");
00063     m_tableWin->addToTable(false, "Path", "Path");
00064     m_tableWin->addToTable(false, "FileName", "FileName");
00065     m_tableWin->addToTable(false, "Notes", "Notes");
00066 
00067     m_tableWin->setStatusMessage("Click, Drag, and Release to Measure a Line");
00068 
00069     connect(this, SIGNAL(viewportChanged()),
00070             this, SLOT(reinitialize()));
00071 
00072     m_shadowHeight = new Distance;
00073     m_shadowLength = new Distance;
00074     m_trackingAngle = new Angle;
00075     m_startSurfacePoint = new SurfacePoint;
00076     m_endSurfacePoint = new SurfacePoint;
00077     m_incidenceAngle = new Angle;
00078   }
00079 
00080 
00087   QAction *SunShadowTool::toolPadAction(ToolPad *toolpad) {
00088     QAction *action = new QAction(toolpad);
00089     action->setIcon(QPixmap(toolIconDir() + "/sunshadow.png"));
00090     action->setToolTip("Sun Shadow (U)");
00091     action->setShortcut(Qt::Key_U);
00092 
00093     QString text  =
00094       "<b>Function:</b> Calculate heights or depths of features in the active "
00095       "viewport given the measurement of a shadow. The shadow measurement "
00096       "should originate from the top of the feature and end when the shadow "
00097       "ends.\n"
00098       "<p><b>Shortcut:</b> U</p> ";
00099     action->setWhatsThis(text);
00100 
00101     return action;
00102   }
00103 
00104 
00113   QWidget *SunShadowTool::createToolBarWidget(QStackedWidget *parent) {
00114     QWidget *hbox = new QWidget(parent);
00115     QToolButton *showTableButton = new QToolButton(hbox);
00116     showTableButton->setText("Table");
00117     showTableButton->setToolTip("Record Measurement Data in Table");
00118     QString text =
00119         "<b>Function:</b> This button will bring up a table that will record "
00120         "the starting and ending points of the line, along with the calculated "
00121         "values for the two points on the image. To measure a shadow, "
00122         "click on the first point and releasing the mouse at the second point."
00123         "\n<p><b>Shortcut:</b>  CTRL+M</p>";
00124     showTableButton->setWhatsThis(text);
00125     showTableButton->setShortcut(Qt::CTRL + Qt::Key_M);
00126     connect(showTableButton, SIGNAL(clicked()), m_tableWin, SLOT(showTable()));
00127     connect(showTableButton, SIGNAL(clicked()), m_tableWin, SLOT(syncColumns()));
00128     connect(showTableButton, SIGNAL(clicked()), m_tableWin, SLOT(raise()));
00129     showTableButton->setEnabled(true);
00130 
00131     m_shadowHeightLineEdit = new QLineEdit(hbox);
00132     m_shadowHeightLineEdit->setText("");
00133     m_shadowHeightLineEdit->setMaxLength(12);
00134     m_shadowHeightLineEdit->setToolTip("Shadow Height");
00135     text = "<b>Function: </b> Shows the height of the shadow drawn on "
00136            "the image.";
00137     m_shadowHeightLineEdit->setWhatsThis(text);
00138     m_shadowHeightLineEdit->setReadOnly(true);
00139 
00140     m_unitsComboBox = new QComboBox(hbox);
00141     m_unitsComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
00142     m_unitsComboBox->addItem("Meters", Distance::Meters);
00143     m_unitsComboBox->addItem("Kilometers", Distance::Kilometers);
00144 
00145     connect(m_unitsComboBox, SIGNAL(activated(int)),
00146             this, SLOT(updateShadowHeightEdit()));
00147 
00148     m_drawInSunDirection = new QCheckBox("Draw in Sun Direction");
00149     m_drawInSunDirection->setChecked(true);
00150 
00151     QHBoxLayout *layout = new QHBoxLayout(hbox);
00152     layout->setMargin(0);
00153     layout->addWidget(m_drawInSunDirection);
00154     layout->addWidget(m_shadowHeightLineEdit);
00155     layout->addWidget(m_unitsComboBox);
00156     layout->addWidget(showTableButton);
00157     layout->addStretch(1);
00158     hbox->setLayout(layout);
00159     return hbox;
00160   }
00161 
00162 
00169   void SunShadowTool::addTo(QMenu *menu) {
00170   }
00171 
00172 
00180   void SunShadowTool::paintViewport(MdiCubeViewport *vp, QPainter *painter) {
00181     if (vp == cubeViewport()) {
00182       if (m_startSamp != Null && m_endSamp != Null &&
00183           m_startLine != Null && m_endLine != Null) {
00184         int vpStartX;
00185         int vpStartY;
00186         vp->cubeToViewport(m_startSamp, m_startLine,
00187                            vpStartX, vpStartY);
00188 
00189         int vpEndX;
00190         int vpEndY;
00191         vp->cubeToViewport(m_endSamp, m_endLine,
00192                            vpEndX, vpEndY);
00193 
00194         painter->setPen(QPen(Qt::red));
00195         painter->drawLine(QPoint(vpStartX, vpStartY), QPoint(vpEndX, vpEndY));
00196       }
00197     }
00198   }
00199 
00200 
00207   void SunShadowTool::mouseMove(QPoint p) {
00208     if (m_tracking && m_trackingAngle->isValid()) {
00209       cubeViewport()->viewportToCube(p.x(), p.y(),
00210                                      m_endSamp, m_endLine);
00211 
00212       if (m_drawInSunDirection->isChecked()) {
00213         // Recalculate the end line based on our drawing angle...
00214         // y = x * tan(angle) for the right triangle created from the
00215         //   user drawing a line.
00216         //
00217         //           E
00218         //         / |
00219         //        /  |
00220         //       /   | L
00221         //      /    |
00222         //     /A    |
00223         //   S-------|
00224         // S = Mouse Start Pos
00225         // A = sun angle
00226         // E = mouse end pos
00227         // L = Line height of the triangle
00228         //       (needs calculated, L = m_endLine - m_startLine)
00229         //
00230         Angle verticalDown(90, Angle::Degrees);
00231         Angle verticalUp(270, Angle::Degrees);
00232         bool adjustLine = true;
00233 
00234         if (*m_trackingAngle == verticalDown ||
00235             *m_trackingAngle == verticalUp) {
00236           m_endSamp = m_startSamp;
00237 
00238           adjustLine = false;
00239         }
00240 
00241         if (adjustLine) {
00242           m_endLine = m_startLine +
00243               (m_endSamp - m_startSamp) * tan(m_trackingAngle->radians());
00244         }
00245       }
00246 
00247       recalculateShadowHeight();
00248 
00249       if (m_tableWin->table()->rowCount()) {
00250         updateRow(m_tableWin->table()->rowCount() - 1);
00251       }
00252 
00253       cubeViewport()->viewport()->update();
00254     }
00255   }
00256 
00257 
00264   void SunShadowTool::mouseButtonPress(QPoint p, Qt::MouseButton s) {
00265     if (m_enabled && s == Qt::LeftButton) {
00266       if (m_tableWin->isVisible())
00267         addRow();
00268 
00269       reinitialize();
00270 
00271       cubeViewport()->viewportToCube(p.x(), p.y(),
00272                                      m_startSamp, m_startLine);
00273 
00274       Camera *cam = cubeViewport()->cube()->camera();
00275 
00276       if (cam->SetImage(m_startSamp, m_startLine)) {
00277         m_tracking = true;
00278         *m_trackingAngle = Angle(cam->SunAzimuth(), Angle::Degrees);
00279       }
00280       else {
00281         m_tracking = false;
00282         m_startSamp = Null;
00283         m_startLine = Null;
00284       }
00285       cubeViewport()->viewport()->update();
00286     }
00287   }
00288 
00289 
00296   void SunShadowTool::mouseButtonRelease(QPoint p, Qt::MouseButton s) {
00297     if (s == Qt::LeftButton && m_tracking) {
00298       mouseMove(p);
00299     }
00300 
00301     m_tracking = false;
00302   }
00303 
00304 
00305 
00313   void SunShadowTool::updateRow(int row) {
00314     ASSERT(row < m_tableWin->table()->rowCount());
00315 
00316     if (row >= m_tableWin->table()->rowCount() || !
00317         m_tableWin->isVisible()) {
00318       return;
00319     }
00320 
00321     // Blank out the row to remove stuff left over from previous cvps
00322     for (int c = 0; c < m_tableWin->table()->columnCount(); c++) {
00323       m_tableWin->table()->item(row, c)->setText("");
00324     }
00325 
00326     // Write all the new info to the current row
00327     if (m_startSurfacePoint->Valid()) {
00328       m_tableWin->table()->item(row, StartLatIndex)->setText(
00329           QString::number(m_startSurfacePoint->GetLatitude().degrees()));
00330       m_tableWin->table()->item(row, StartLonIndex)->setText(
00331           QString::number(m_startSurfacePoint->GetLongitude().degrees()));
00332     }
00333     else {
00334       m_tableWin->table()->item(row, StartLatIndex)->setText("N/A");
00335       m_tableWin->table()->item(row, StartLonIndex)->setText("N/A");
00336     }
00337 
00338     if (m_endSurfacePoint->Valid()) {
00339       m_tableWin->table()->item(row, EndLatIndex)->setText(
00340           QString::number(m_endSurfacePoint->GetLatitude().degrees()));
00341       m_tableWin->table()->item(row, EndLonIndex)->setText(
00342           QString::number(m_endSurfacePoint->GetLongitude().degrees()));
00343     }
00344     else {
00345       m_tableWin->table()->item(row, EndLatIndex)->setText("N/A");
00346       m_tableWin->table()->item(row, EndLonIndex)->setText("N/A");
00347     }
00348 
00349     if (m_startSamp != Null && m_startLine != Null) {
00350       m_tableWin->table()->item(row, StartSampIndex)->setText(
00351           QString::number(m_startSamp));
00352       m_tableWin->table()->item(row, StartLineIndex)->setText(
00353           QString::number(m_startLine));
00354     }
00355     else {
00356       m_tableWin->table()->item(row, StartSampIndex)->setText("N/A");
00357       m_tableWin->table()->item(row, StartLineIndex)->setText("N/A");
00358     }
00359 
00360     if (m_endSamp != Null && m_endLine != Null) {
00361       m_tableWin->table()->item(row, EndSampIndex)->setText(
00362           QString::number(m_endSamp));
00363       m_tableWin->table()->item(row, EndLineIndex)->setText(
00364           QString::number(m_endLine));
00365     }
00366     else {
00367       m_tableWin->table()->item(row, EndSampIndex)->setText("N/A");
00368       m_tableWin->table()->item(row, EndLineIndex)->setText("N/A");
00369     }
00370 
00371     if (m_shadowLength->isValid()) {
00372       m_tableWin->table()->item(row, ShadowLengthKmIndex)->setText(
00373           QString::number(m_shadowLength->kilometers()));
00374       m_tableWin->table()->item(row, ShadowLengthMIndex)->setText(
00375           QString::number(m_shadowLength->meters()));
00376     }
00377     else {
00378       m_tableWin->table()->item(row, ShadowLengthKmIndex)->setText("N/A");
00379       m_tableWin->table()->item(row, ShadowLengthMIndex)->setText("N/A");
00380     }
00381 
00382     if (m_shadowHeight->isValid()) {
00383       m_tableWin->table()->item(row, ShadowHeightKmIndex)->setText(
00384           QString::number(m_shadowHeight->kilometers()));
00385       m_tableWin->table()->item(row, ShadowHeightMIndex)->setText(
00386           QString::number(m_shadowHeight->meters()));
00387     }
00388     else {
00389       m_tableWin->table()->item(row, ShadowHeightKmIndex)->setText("N/A");
00390       m_tableWin->table()->item(row, ShadowHeightMIndex)->setText("N/A");
00391     }
00392 
00393     if (m_incidenceAngle->isValid()) {
00394       m_tableWin->table()->item(row, IncidenceAngleDegreesIndex)->setText(
00395           QString::number(m_incidenceAngle->degrees()));
00396       m_tableWin->table()->item(row, IncidenceAngleRadiansIndex)->setText(
00397           QString::number(m_incidenceAngle->radians()));
00398     }
00399     else {
00400       m_tableWin->table()->item(row, IncidenceAngleDegreesIndex)->setText("N/A");
00401       m_tableWin->table()->item(row, IncidenceAngleRadiansIndex)->setText("N/A");
00402     }
00403 
00404     m_tableWin->table()->item(row, PathIndex)->setText(m_path);
00405     m_tableWin->table()->item(row, FileNameIndex)->setText(m_fileName);
00406   }
00407 
00408 
00412   void SunShadowTool::reinitialize() {
00413     m_startSamp = Null;
00414     m_endSamp   = Null;
00415     m_startLine = Null;
00416     m_endLine   = Null;
00417 
00418     *m_shadowHeight = Distance();
00419     *m_shadowLength = Distance();
00420     *m_startSurfacePoint = SurfacePoint();
00421     *m_endSurfacePoint = SurfacePoint();
00422     *m_incidenceAngle = Angle();
00423 
00424     recalculateShadowHeight();
00425   }
00426 
00427 
00431   void SunShadowTool::addRow() {
00432     int newRowPos = m_tableWin->table()->rowCount();
00433     m_tableWin->table()->insertRow(newRowPos);
00434     for (int c = 0; c < m_tableWin->table()->columnCount(); c++) {
00435       QTableWidgetItem *item = new QTableWidgetItem("");
00436       m_tableWin->table()->setItem(newRowPos, c, item);
00437     }
00438     m_tableWin->table()->scrollToItem(m_tableWin->table()->item(newRowPos, 0),
00439                                       QAbstractItemView::PositionAtBottom);
00440   }
00441 
00442 
00447   void SunShadowTool::recalculateShadowHeight() {
00448     try {
00449       if (m_startSamp != Null && m_startLine != Null &&
00450           m_endSamp != Null && m_endLine != Null) {
00451         m_path = FileName(cubeViewport()->cube()->fileName()).path();
00452         m_fileName = FileName(cubeViewport()->cube()->fileName()).name();
00453 
00454         //
00455         // \  |  /
00456         //  \ | /
00457         //    _
00458         //-- / \ --    THE SUN
00459         //-- \_/ --
00460         //  / | \  -
00461         // /  |  \   -      <--- vector from the sun that intersects P1 and P2
00462         //             -
00463         //     _         -_
00464         //     ^         /^\-     P2
00465         //     |        / | \  -   |
00466         //   H |       /  |  \   - v
00467         //    _v______/  P1   \__T_-_________
00468         //                      ^
00469         //                    Shadow
00470         //
00471         //   T: Angle from the horizon to the sun
00472         //   H: Difference in planetary radius between P1 and P2
00473         //   L : length(Shadow)
00474         //   H = L * sin(T)
00475         //
00476         // We do not want the local incidence angle for T.
00477         //
00478         // Equation to variable mapping:
00479         //   T: theta
00480         //   H: m_shadowHeight
00481         //   L: m_shadowLength
00482         //   P1: m_startSurfacePoint
00483         //   P2: m_endSurfacePoint
00484 
00485         bool success = true;
00486         Camera *cam = cubeViewport()->cube()->camera();
00487         success = cam->SetImage(m_startSamp, m_startLine);
00488 
00489         // Vector is in meters
00490         QVector3D sunDirection;
00491 
00492         if (success) {
00493           *m_startSurfacePoint = cam->GetSurfacePoint();
00494           double sunPosition[3];
00495           cam->sunPosition(sunPosition);
00496 
00497           Distance targetRadii[3];
00498           cam->radii(targetRadii);
00499 
00500           double origin[3] = {0.0, 0.0, 0.0};
00501           SpiceBoolean surfptSuccess;
00502           // Vector is in kilometers
00503           double naifVectorFromSunToP1[3] = {0.0, 0.0, 0.0};
00504 
00505           surfpt_c(origin, sunPosition, targetRadii[0].kilometers(),
00506                    targetRadii[1].kilometers(), targetRadii[2].kilometers(),
00507                    naifVectorFromSunToP1, &surfptSuccess);
00508           success = surfptSuccess;
00509 
00510           if (success) {
00511             sunDirection = QVector3D(
00512                 naifVectorFromSunToP1[0] * 1000.0,
00513                 naifVectorFromSunToP1[1] * 1000.0,
00514                 naifVectorFromSunToP1[2] * 1000.0).normalized();
00515           }
00516         }
00517 
00518         if (success) {
00519           success = cam->SetImage(m_endSamp, m_endLine);
00520         }
00521 
00522         if (success) {
00523           *m_endSurfacePoint = cam->GetSurfacePoint();
00524 
00525           *m_incidenceAngle = Angle(cam->IncidenceAngle(), Angle::Degrees);
00526           Angle theta = Angle(90.0, Angle::Degrees) - *m_incidenceAngle;
00527 
00528           Displacement deltaX = m_startSurfacePoint->GetX() -
00529                                 m_endSurfacePoint->GetX();
00530 
00531           Displacement deltaY = m_startSurfacePoint->GetY() -
00532                                 m_endSurfacePoint->GetY();
00533 
00534           Displacement deltaZ = m_startSurfacePoint->GetZ() -
00535                                 m_endSurfacePoint->GetZ();
00536 
00537           *m_shadowLength = Distance(
00538             sqrt(
00539               deltaX.meters() * deltaX.meters() +
00540               deltaY.meters() * deltaY.meters() +
00541               deltaZ.meters() * deltaZ.meters()),
00542             Distance::Meters);
00543 
00544           *m_shadowHeight = Distance(
00545               m_shadowLength->meters() * sin(theta.radians()),
00546               Distance::Meters);
00547         }
00548       }
00549     }
00550     catch (IException &) {
00551       reinitialize();
00552     }
00553 
00554     updateShadowHeightEdit();
00555   }
00556 
00557 
00559   void SunShadowTool::updateShadowHeightEdit() {
00560     if (m_shadowHeight->isValid()) {
00561       Distance::Units displayUnits =
00562           (Distance::Units)m_unitsComboBox->itemData(
00563             m_unitsComboBox->currentIndex()).toInt();
00564 
00565       switch (displayUnits) {
00566         case Distance::Meters:
00567           m_shadowHeightLineEdit->setText(
00568               toString(m_shadowHeight->meters()));
00569           break;
00570         case Distance::Kilometers:
00571           m_shadowHeightLineEdit->setText(
00572               toString(m_shadowHeight->kilometers()));
00573           break;
00574         case Distance::SolarRadii:
00575         case Distance::Pixels:
00576           m_shadowHeightLineEdit->setText("Not Supported");
00577           break;
00578       }
00579 
00580     }
00581     else {
00582       m_shadowHeightLineEdit->setText("");
00583     }
00584   }
00585 
00586 
00591   void SunShadowTool::updateTool() {
00592     MdiCubeViewport *activeViewport = cubeViewport();
00593 
00594     bool hasCamera = true;
00595     try {
00596       hasCamera = activeViewport &&
00597                   (activeViewport->cube()->camera() != NULL);
00598     }
00599     catch (IException &) {
00600       hasCamera = false;
00601     }
00602 
00603     m_shadowHeightLineEdit->setEnabled(hasCamera);
00604     m_unitsComboBox->setEnabled(hasCamera);
00605     m_enabled = hasCamera;
00606 
00607     updateShadowHeightEdit();
00608   }
00609 
00610 }
00611