|
Isis 3.0 Object Programmers' Reference |
Home |
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