USGS

Isis 3.0 Object Programmers' Reference

Home

MosaicAreaTool.cpp

00001 #include "MosaicAreaTool.h"
00002 
00003 #include <cmath>
00004 #include <float.h>
00005 
00006 #include <QDialog>
00007 #include <QDoubleValidator>
00008 #include <QGraphicsEllipseItem>
00009 #include <QGraphicsScene>
00010 #include <QGridLayout>
00011 #include <QLabel>
00012 #include <QLineEdit>
00013 #include <QMenu>
00014 #include <QMessageBox>
00015 #include <QPushButton>
00016 
00017 #include "Angle.h"
00018 #include "Distance.h"
00019 #include "iString.h"
00020 #include "FindSpotGraphicsItem.h"
00021 #include "MosaicGraphicsView.h"
00022 #include "MosaicSceneWidget.h"
00023 #include "Projection.h"
00024 
00025 namespace Isis {
00032   MosaicAreaTool::MosaicAreaTool(MosaicSceneWidget *scene) :
00033       MosaicTool(scene) {
00034     m_box = NULL;
00035     m_drawBox = NULL;
00036     m_latLineEdit = NULL;
00037     m_lonLineEdit = NULL;
00038     m_areaLineEdit = NULL;
00039 
00040     connect(scene, SIGNAL(projectionChanged(Projection *)),
00041             this, SLOT(userChangedBox()));
00042   }
00043 
00044 
00049   void MosaicAreaTool::userChangedBox() {
00050     bool latValid = false;
00051     bool lonValid = false;
00052     bool areaValid = false;
00053 
00054     if(!m_latLineEdit || !m_lonLineEdit || !m_areaLineEdit) {
00055       clearBox();
00056       return;
00057     }
00058 
00059     QString latitude = m_latLineEdit->text();
00060 
00061     if(latitude != "Null" && latitude != "") {
00062       int cursorPos = 0;
00063       QValidator::State validLat =
00064         m_latLineEdit->validator()->validate(latitude, cursorPos);
00065       if(validLat != QValidator::Acceptable) {
00066         QMessageBox::warning(getWidget(), "Error",
00067                             "Latitude value must be in the range -90 to 90",
00068                             QMessageBox::Ok, QMessageBox::NoButton,
00069                             QMessageBox::NoButton);
00070       }
00071       else {
00072         latValid = true;
00073       }
00074     }
00075 
00076     //Validate longitude value
00077     QString longitude = m_lonLineEdit->text();
00078     if(longitude != "Null" && longitude != "" && latValid) {
00079       int cursorPos = 0;
00080       QValidator::State validLon =
00081         m_lonLineEdit->validator()->validate(longitude, cursorPos);
00082       if(validLon != QValidator::Acceptable) {
00083         QMessageBox::warning(getWidget(), "Error",
00084                             "Longitude value invalid",
00085                             QMessageBox::Ok, QMessageBox::NoButton,
00086                             QMessageBox::NoButton);
00087       }
00088       else {
00089         lonValid = true;
00090       }
00091     }
00092 
00093     QString areaString = m_areaLineEdit->text();
00094     if(areaString != "Null" && areaString != "" && latValid && lonValid) {
00095       int cursorPos = 0;
00096       QValidator::State validArea =
00097         m_areaLineEdit->validator()->validate(areaString, cursorPos);
00098       if(validArea != QValidator::Acceptable) {
00099         QMessageBox::warning(getWidget(), "Error",
00100                             "Area value invalid",
00101                             QMessageBox::Ok, QMessageBox::NoButton,
00102                             QMessageBox::NoButton);
00103       }
00104       else {
00105         areaValid = true;
00106       }
00107     }
00108 
00109 
00110     if(latValid && lonValid && areaValid) {
00111       double lat = iString(latitude.toStdString()).ToDouble();
00112       double lon = iString(longitude.toStdString()).ToDouble();
00113       double area = iString(areaString.toStdString()).ToDouble();
00114 
00115       Projection *projection = getWidget()->getProjection();
00116 
00117       if(projection && projection->SetGround(lat, lon)) {
00118         QPointF scenePos(projection->XCoord(), -1 * projection->YCoord());
00119         QRectF sceneRect(getWidget()->getView()->sceneRect());
00120 
00121         if(sceneRect.contains(scenePos)) {
00122           if(m_box != NULL) {
00123             clearBox();
00124           }
00125 
00126           Distance distance(area, Distance::Meters);
00127 
00128           QPolygonF boxPoly;
00129           QRectF latLonRange = calcLatLonRange(QPointF(lon, lat), distance);
00130 
00131           double xStep = latLonRange.width() / 100.0;
00132           double yStep = latLonRange.height() / 100.0;
00133 
00134           bool hasPole = (latLonRange.top() == -90 ||
00135                           latLonRange.bottom() == 90);
00136 
00137           double yPos = latLonRange.top();
00138           if(yPos != -90) {
00139             for(double xPos = latLonRange.left();
00140                 xPos <= latLonRange.right();
00141                 xPos += xStep) {
00142               if(projection->SetGround(yPos, xPos)) {
00143                 QPointF pos(projection->XCoord(), -1 * projection->YCoord());
00144                 boxPoly << pos;
00145               }
00146             }
00147           }
00148 
00149           double xPos = latLonRange.right();
00150           for(double yPos = latLonRange.top();
00151               !hasPole && yPos <= latLonRange.bottom();
00152               yPos += yStep) {
00153             if(projection->SetGround(yPos, xPos)) {
00154               QPointF pos(projection->XCoord(), -1 * projection->YCoord());
00155               boxPoly << pos;
00156             }
00157           }
00158 
00159           yPos = latLonRange.bottom();
00160           if(yPos != 90) {
00161             for(double xPos = latLonRange.right();
00162                 xPos >= latLonRange.left();
00163                 xPos -= xStep) {
00164               if(projection->SetGround(yPos, xPos)) {
00165                 QPointF pos(projection->XCoord(), -1 * projection->YCoord());
00166                 boxPoly << pos;
00167               }
00168             }
00169           }
00170 
00171           xPos = latLonRange.left();
00172           for(double yPos = latLonRange.bottom();
00173               !hasPole && yPos >= latLonRange.top();
00174               yPos -= yStep) {
00175             if(projection->SetGround(yPos, xPos)) {
00176               QPointF pos(projection->XCoord(), -1 * projection->YCoord());
00177               boxPoly << pos;
00178             }
00179           }
00180 
00181           if(boxPoly.size() > 0) {
00182             boxPoly << boxPoly[0];
00183 
00184             m_box = new QGraphicsPolygonItem(boxPoly);
00185             m_box->setZValue(DBL_MAX);
00186 
00187             getWidget()->getScene()->addItem(m_box);
00188             getWidget()->getView()->centerOn(scenePos);
00189           }
00190         }
00191         else {
00192           QString message = "Lat/Lon not within this view.";
00193           QMessageBox::information(getWidget(), "Cannot Calculate Box",
00194                                   message, QMessageBox::Ok);
00195         }
00196       }
00197     }
00198   }
00199 
00200 
00209   QAction *MosaicAreaTool::getPrimaryAction() {
00210     m_action = new QAction(this);
00211     m_action->setIcon(getIcon("qmos_area.png"));
00212     m_action->setToolTip("Show Area (a)");
00213     m_action->setShortcut(Qt::Key_A);
00214     QString text  =
00215       "<b>Function:</b>  Draw a box given a distance centered on a "
00216       "latitude/longitude.<br><br>"
00217       "This tool draws a black square, given an edge length in meters, "
00218       "centered on a latitude/longitude point. This box would be a square on "
00219       "the surface of the target, and is designed to be modified and warped by "
00220       "the current projection."
00221       "<p><b>Shortcut:</b>  a</p> ";
00222     m_action->setWhatsThis(text);
00223     return m_action;
00224   }
00225 
00226 
00227   QWidget *MosaicAreaTool::getToolBarWidget() {
00228     m_latLineEdit = new QLineEdit();
00229     m_latLineEdit->setValidator(new QDoubleValidator(-90.0, 90.0, 99, this));
00230 
00231     m_lonLineEdit = new QLineEdit();
00232     m_lonLineEdit->setValidator(new QDoubleValidator(this));
00233 
00234     m_areaLineEdit = new QLineEdit();
00235     m_areaLineEdit->setValidator(new QDoubleValidator(this));
00236     m_areaLineEdit->setText("10000");
00237 
00238     QLabel *latLabel = new QLabel("Latitude");
00239     QLabel *lonLabel = new QLabel("Longitude");
00240     QLabel *areaLabel = new QLabel("Size (meters)");
00241     areaLabel->setToolTip("This is the width and the height of the box");
00242 
00243     // Create the action buttons
00244     QPushButton *okButton = new QPushButton("Update Box");
00245     connect(okButton, SIGNAL(clicked()), this, SLOT(userChangedBox()));
00246 
00247     QPushButton *clearButton = new QPushButton("Clear Box");
00248     connect(clearButton, SIGNAL(clicked()), this, SLOT(clearBox()));
00249 
00250     // Put the buttons in a horizontal orientation
00251     QHBoxLayout *actionLayout = new QHBoxLayout();
00252     actionLayout->addWidget(latLabel);
00253     actionLayout->addWidget(m_latLineEdit);
00254     actionLayout->addWidget(lonLabel);
00255     actionLayout->addWidget(m_lonLineEdit);
00256     actionLayout->addWidget(areaLabel);
00257     actionLayout->addWidget(m_areaLineEdit);
00258     actionLayout->addWidget(okButton);
00259     actionLayout->addWidget(clearButton);
00260     actionLayout->addStretch(1);
00261     actionLayout->setMargin(0);
00262 
00263     QWidget *toolBarWidget = new QWidget;
00264     toolBarWidget->setLayout(actionLayout);
00265 
00266     return toolBarWidget;
00267   }
00268 
00269 
00276   void MosaicAreaTool::addToMenu(QMenu *menu) {
00277 
00278   }
00279 
00280 
00281   PvlObject MosaicAreaTool::toPvl() const {
00282     PvlObject obj(projectPvlObjectName());
00283 
00284     if(m_box) {
00285       obj += PvlKeyword("Latitude", m_latLineEdit->text());
00286       obj += PvlKeyword("Longitude", m_lonLineEdit->text());
00287       obj += PvlKeyword("Area", m_areaLineEdit->text());
00288       obj += PvlKeyword("Visible", (m_box != NULL));
00289     }
00290 
00291     return obj;
00292   }
00293 
00294 
00295   void MosaicAreaTool::fromPvl(const PvlObject &obj) {
00296     if(obj.HasKeyword("Visible")) {
00297       if(obj.HasKeyword("Latitude") && obj["Latitude"][0] != "Null")
00298         m_latLineEdit->setText(obj["Latitude"][0]);
00299 
00300       if(obj.HasKeyword("Longitude") && obj["Longitude"][0] != "Null")
00301         m_lonLineEdit->setText(obj["Longitude"][0]);
00302 
00303       if(obj.HasKeyword("Area") && obj["Area"][0] != "Null")
00304         m_areaLineEdit->setText(obj["Area"][0]);
00305 
00306       if((int)obj["Visible"][0] != 0) {
00307         userChangedBox();
00308       }
00309     }
00310   }
00311 
00312 
00313   iString MosaicAreaTool::projectPvlObjectName() const {
00314     return "MosaicAreaTool";
00315   }
00316 
00317 
00326   QWidget *MosaicAreaTool::createToolBarWidget() {
00327     QWidget *widget = new QWidget();
00328     return widget;
00329   }
00330 
00331 
00332   void MosaicAreaTool::mouseButtonRelease(QPointF mouseLoc, Qt::MouseButton s) {
00333     if(!isActive())
00334       return;
00335 
00336     if(s == Qt::LeftButton) {
00337       Projection *proj = getWidget()->getProjection();
00338 
00339       if(proj && getWidget()->getView()->sceneRect().contains(mouseLoc)) {
00340         if(proj->SetCoordinate(mouseLoc.x(), -1 * mouseLoc.y())) {
00341           if(m_drawBox != NULL) {
00342             clearBox();
00343           }
00344 
00345           m_latLineEdit->setText(QString::number(proj->Latitude(), 'g', 10));
00346           m_lonLineEdit->setText(QString::number(proj->Longitude(), 'g', 10));
00347 
00348           userChangedBox();
00349         }
00350       }
00351     }
00352   }
00353 
00354 
00359   void MosaicAreaTool::clearBox() {
00360     if(m_box != NULL) {
00361       getWidget()->getScene()->removeItem(m_box);
00362 
00363       delete m_box;
00364       m_box = NULL;
00365     }
00366   }
00367 
00368 
00376   QRectF MosaicAreaTool::calcLatLonRange(QPointF centerLatLon,
00377       Distance size) {
00378     Distance distanceFromCenter = size / 2.0;
00379     QRectF latLonBoundingBox;
00380 
00381     Angle centerLat(centerLatLon.y(), Angle::Degrees);
00382     Angle centerLon(centerLatLon.x(), Angle::Degrees);
00383 
00384     Projection *proj = getWidget()->getProjection();
00385 
00386     if(proj) {
00387       bool longitudeWraps = false;
00388       Distance radius(proj->LocalRadius(centerLat.GetDegrees()),
00389           Distance::Meters);
00390 
00391       // First we can get the angle between the latitudes...
00392       // d = arcsin ( movementDistance / radiusDistance )
00393       Angle deltaLat(asin( distanceFromCenter / radius ), Angle::Radians);
00394 
00395       latLonBoundingBox.setTop( (centerLat - deltaLat).GetDegrees() );
00396 
00397       if(latLonBoundingBox.top() < -90 && centerLatLon.y() != -90) {
00398 
00399         // Block infinite recursion
00400         if(centerLatLon.y() != 90) {
00401           qWarning("The pole is included in the area but not centered");
00402           centerLatLon.setY(-90);
00403           return calcLatLonRange(centerLatLon, size);
00404         }
00405         else
00406           return QRectF();
00407       }
00408       else if(centerLatLon.y() == -90) {
00409         longitudeWraps = true;
00410       }
00411 
00412       latLonBoundingBox.setBottom( (centerLat + deltaLat).GetDegrees() );
00413 
00414       if(latLonBoundingBox.bottom() > 90 && centerLatLon.y() != 90) {
00415 
00416         // Block infinite recursion
00417         if(centerLatLon.y() != -90) {
00418           qWarning("The pole is included in the area but not centered");
00419           centerLatLon.setY(90);
00420           return calcLatLonRange(centerLatLon, size);
00421         }
00422         else
00423           return QRectF();
00424       }
00425       else if(centerLatLon.y() == 90) {
00426         longitudeWraps = true;
00427       }
00428 
00429       // Now let's do lons...
00430       Angle widestLat(
00431           asin( sin(centerLat.GetRadians()) /
00432                 cos( distanceFromCenter / radius ) ),
00433           Angle::Radians);
00434 
00435       double valueToASin = sin(distanceFromCenter / radius) /
00436           cos(widestLat.GetRadians());
00437 
00438       if(valueToASin < -1 || valueToASin > 1)
00439         longitudeWraps = true;
00440 
00441       // Longitude wraps
00442       if(longitudeWraps) {
00443         if(proj->Has360Domain()) {
00444           latLonBoundingBox.setLeft( 0 );
00445           latLonBoundingBox.setRight( 360 );
00446         }
00447         else {
00448           latLonBoundingBox.setLeft( -180 );
00449           latLonBoundingBox.setRight( 180 );
00450         }
00451       }
00452       else {
00453         Angle deltaLon(
00454             asin( sin(distanceFromCenter / radius) /
00455                   cos(widestLat.GetRadians())),
00456             Angle::Radians);
00457         latLonBoundingBox.setLeft( (centerLon - deltaLon).GetDegrees() );
00458         latLonBoundingBox.setRight( (centerLon + deltaLon).GetDegrees() );
00459       }
00460     }
00461 
00462     return latLonBoundingBox;
00463   }
00464 }
00465