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