Isis 3 Programmer Reference
MosaicAreaTool.cpp
1 #include "MosaicAreaTool.h"
2 
3 #include <cmath>
4 #include <float.h>
5 
6 #include <QDialog>
7 #include <QDoubleValidator>
8 #include <QGraphicsEllipseItem>
9 #include <QGraphicsScene>
10 #include <QGridLayout>
11 #include <QLabel>
12 #include <QLineEdit>
13 #include <QMenu>
14 #include <QMessageBox>
15 #include <QPushButton>
16 
17 #include "Angle.h"
18 #include "Distance.h"
19 #include "IString.h"
20 #include "FindSpotGraphicsItem.h"
21 #include "MosaicGraphicsView.h"
22 #include "MosaicSceneWidget.h"
23 #include "Projection.h"
24 #include "TProjection.h"
25 #include "PvlKeyword.h"
26 #include "PvlObject.h"
27 
28 namespace Isis {
36  MosaicTool(scene) {
37  m_box = NULL;
38  m_drawBox = NULL;
39  m_latLineEdit = NULL;
40  m_lonLineEdit = NULL;
41  m_areaLineEdit = NULL;
42 
43  connect(scene, SIGNAL(projectionChanged(Projection *)),
44  this, SLOT(userChangedBox()));
45  }
46 
47 
52  void MosaicAreaTool::userChangedBox() {
53  bool latValid = false;
54  bool lonValid = false;
55  bool areaValid = false;
56 
58  clearBox();
59  return;
60  }
61 
62  QString latitude = m_latLineEdit->text();
63 
64  if(latitude != "Null" && latitude != "") {
65  int cursorPos = 0;
66  QValidator::State validLat =
67  m_latLineEdit->validator()->validate(latitude, cursorPos);
68  if(validLat != QValidator::Acceptable) {
69  QMessageBox::warning(getWidget(), "Error",
70  "Latitude value must be in the range -90 to 90",
71  QMessageBox::Ok, QMessageBox::NoButton,
72  QMessageBox::NoButton);
73  }
74  else {
75  latValid = true;
76  }
77  }
78 
79  //Validate longitude value
80  QString longitude = m_lonLineEdit->text();
81  if(longitude != "Null" && longitude != "" && latValid) {
82  int cursorPos = 0;
83  QValidator::State validLon =
84  m_lonLineEdit->validator()->validate(longitude, cursorPos);
85  if(validLon != QValidator::Acceptable) {
86  QMessageBox::warning(getWidget(), "Error",
87  "Longitude value invalid",
88  QMessageBox::Ok, QMessageBox::NoButton,
89  QMessageBox::NoButton);
90  }
91  else {
92  lonValid = true;
93  }
94  }
95 
96  QString areaString = m_areaLineEdit->text();
97  if(areaString != "Null" && areaString != "" && latValid && lonValid) {
98  int cursorPos = 0;
99  QValidator::State validArea =
100  m_areaLineEdit->validator()->validate(areaString, cursorPos);
101  if(validArea != QValidator::Acceptable) {
102  QMessageBox::warning(getWidget(), "Error",
103  "Area value invalid",
104  QMessageBox::Ok, QMessageBox::NoButton,
105  QMessageBox::NoButton);
106  }
107  else {
108  areaValid = true;
109  }
110  }
111 
112 
113  if(latValid && lonValid && areaValid) {
114  double lat = IString(latitude.toStdString()).ToDouble();
115  double lon = IString(longitude.toStdString()).ToDouble();
116  double area = IString(areaString.toStdString()).ToDouble();
117 
118  Projection *projection = getWidget()->getProjection();
119  Projection::ProjectionType ptype = projection->projectionType();
120 
121  if (projection && ptype == Projection::Triaxial) {
122  TProjection * tproj = (TProjection *) projection;
123  if (tproj->SetGround(lat, lon)) {
124  QPointF scenePos(projection->XCoord(), -1 * projection->YCoord());
125  QRectF sceneRect(getWidget()->getView()->sceneRect());
126 
127  if(sceneRect.contains(scenePos)) {
128  if(m_box != NULL) {
129  clearBox();
130  }
131 
132  Distance distance(area, Distance::Meters);
133 
134  QPolygonF boxPoly;
135  QRectF latLonRange = calcLatLonRange(QPointF(lon, lat), distance);
136 
137  double xStep = latLonRange.width() / 100.0;
138  double yStep = latLonRange.height() / 100.0;
139 
140  bool hasPole = (latLonRange.top() == -90 ||
141  latLonRange.bottom() == 90);
142 
143  double yPos = latLonRange.top();
144  if (yPos != -90) {
145  for(double xPos = latLonRange.left();
146  xPos <= latLonRange.right();
147  xPos += xStep) {
148  if (tproj->SetGround(yPos, xPos)) {
149  QPointF pos(tproj->XCoord(), -1 * tproj->YCoord());
150  boxPoly << pos;
151  }
152  }
153  }
154 
155  double xPos = latLonRange.right();
156  for (double yPos = latLonRange.top();
157  !hasPole && yPos <= latLonRange.bottom();
158  yPos += yStep) {
159  if (tproj->SetGround(yPos, xPos)) {
160  QPointF pos(tproj->XCoord(), -1 * tproj->YCoord());
161  boxPoly << pos;
162  }
163  }
164 
165  yPos = latLonRange.bottom();
166  if (yPos != 90) {
167  for (double xPos = latLonRange.right();
168  xPos >= latLonRange.left();
169  xPos -= xStep) {
170  if (tproj->SetGround(yPos, xPos)) {
171  QPointF pos(tproj->XCoord(), -1 * tproj->YCoord());
172  boxPoly << pos;
173  }
174  }
175  }
176 
177  xPos = latLonRange.left();
178  for (double yPos = latLonRange.bottom();
179  !hasPole && yPos >= latLonRange.top();
180  yPos -= yStep) {
181  if (tproj->SetGround(yPos, xPos)) {
182  QPointF pos(tproj->XCoord(), -1 * tproj->YCoord());
183  boxPoly << pos;
184  }
185  }
186 
187  if (boxPoly.size() > 0) {
188  boxPoly << boxPoly[0];
189 
190  m_box = new QGraphicsPolygonItem(boxPoly);
191  m_box->setZValue(DBL_MAX);
192  // Ensure lines are cosmetic (i.e. always 1 pixel on screen)
193  QPen pen;
194  pen.setCosmetic(true);
195  m_box->setPen(pen);
196 
197  getWidget()->getScene()->addItem(m_box);
198  getWidget()->getView()->centerOn(scenePos);
199  }
200  }
201  else {
202  QString message = "Lat/Lon not within this view.";
203  QMessageBox::information(getWidget(), "Cannot Calculate Box",
204  message, QMessageBox::Ok);
205  }
206  }
207  }
208  }
209  }
210 
211 
221  m_action = new QAction(this);
222  m_action->setIcon(getIcon("qmos_area.png"));
223  m_action->setToolTip("Show Area (a)");
224  m_action->setShortcut(Qt::Key_A);
225  QString text =
226  "<b>Function:</b> Draw a box given a distance centered on a "
227  "latitude/longitude.<br><br>"
228  "This tool draws a black square, given an edge length in meters, "
229  "centered on a latitude/longitude point. This box would be a square on "
230  "the surface of the target, and is designed to be modified and warped by "
231  "the current projection."
232  "<p><b>Shortcut:</b> a</p> ";
233  m_action->setWhatsThis(text);
234  return m_action;
235  }
236 
237 
239  m_latLineEdit = new QLineEdit();
240  m_latLineEdit->setValidator(new QDoubleValidator(-90.0, 90.0, 99, this));
241 
242  m_lonLineEdit = new QLineEdit();
243  m_lonLineEdit->setValidator(new QDoubleValidator(this));
244 
245  m_areaLineEdit = new QLineEdit();
246  m_areaLineEdit->setValidator(new QDoubleValidator(this));
247  m_areaLineEdit->setText("10000");
248 
249  QLabel *latLabel = new QLabel("Latitude");
250  QLabel *lonLabel = new QLabel("Longitude");
251  QLabel *areaLabel = new QLabel("Size (meters)");
252  areaLabel->setToolTip("This is the width and the height of the box");
253 
254  // Create the action buttons
255  QPushButton *okButton = new QPushButton("Update Box");
256  connect(okButton, SIGNAL(clicked()), this, SLOT(userChangedBox()));
257 
258  QPushButton *clearButton = new QPushButton("Clear Box");
259  connect(clearButton, SIGNAL(clicked()), this, SLOT(clearBox()));
260 
261  // Put the buttons in a horizontal orientation
262  QHBoxLayout *actionLayout = new QHBoxLayout();
263  actionLayout->addWidget(latLabel);
264  actionLayout->addWidget(m_latLineEdit);
265  actionLayout->addWidget(lonLabel);
266  actionLayout->addWidget(m_lonLineEdit);
267  actionLayout->addWidget(areaLabel);
268  actionLayout->addWidget(m_areaLineEdit);
269  actionLayout->addWidget(okButton);
270  actionLayout->addWidget(clearButton);
271  actionLayout->addStretch(1);
272  actionLayout->setMargin(0);
273 
274  QWidget *toolBarWidget = new QWidget;
275  toolBarWidget->setLayout(actionLayout);
276 
277  return toolBarWidget;
278  }
279 
280 
288 
289  }
290 
291 
292  PvlObject MosaicAreaTool::toPvl() const {
293  PvlObject obj(projectPvlObjectName());
294 
295  if(m_box) {
296  obj += PvlKeyword("Latitude", m_latLineEdit->text());
297  obj += PvlKeyword("Longitude", m_lonLineEdit->text());
298  obj += PvlKeyword("Area", m_areaLineEdit->text());
299  obj += PvlKeyword("Visible", toString((int)(m_box != NULL)));
300  }
301 
302  return obj;
303  }
304 
305 
306  void MosaicAreaTool::fromPvl(const PvlObject &obj) {
307  if(obj.hasKeyword("Visible")) {
308  if(obj.hasKeyword("Latitude") && obj["Latitude"][0] != "Null")
309  m_latLineEdit->setText(obj["Latitude"][0]);
310 
311  if(obj.hasKeyword("Longitude") && obj["Longitude"][0] != "Null")
312  m_lonLineEdit->setText(obj["Longitude"][0]);
313 
314  if(obj.hasKeyword("Area") && obj["Area"][0] != "Null")
315  m_areaLineEdit->setText(obj["Area"][0]);
316 
317  if(toBool(obj["Visible"][0]) != false) {
318  userChangedBox();
319  }
320  }
321  }
322 
323 
324  QString MosaicAreaTool::projectPvlObjectName() const {
325  return "MosaicAreaTool";
326  }
327 
328 
338  QWidget *widget = new QWidget();
339  return widget;
340  }
341 
342 
343  void MosaicAreaTool::mouseButtonRelease(QPointF mouseLoc, Qt::MouseButton s) {
344  if(!isActive())
345  return;
346 
347  if(s == Qt::LeftButton) {
348  TProjection *tproj = (TProjection *) getWidget()->getProjection();
349 
350  if(tproj && getWidget()->getView()->sceneRect().contains(mouseLoc)) {
351  if(tproj->SetCoordinate(mouseLoc.x(), -1 * mouseLoc.y())) {
352  if(m_drawBox != NULL) {
353  clearBox();
354  }
355 
356  m_latLineEdit->setText(QString::number(tproj->Latitude(), 'g', 10));
357  m_lonLineEdit->setText(QString::number(tproj->Longitude(), 'g', 10));
358 
359  userChangedBox();
360  }
361  }
362  }
363  }
364 
365 
370  void MosaicAreaTool::clearBox() {
371  if(m_box != NULL) {
372  getWidget()->getScene()->removeItem(m_box);
373 
374  delete m_box;
375  m_box = NULL;
376  }
377  }
378 
379 
387  QRectF MosaicAreaTool::calcLatLonRange(QPointF centerLatLon,
388  Distance size) {
389  Distance distanceFromCenter = size / 2.0;
390  QRectF latLonBoundingBox;
391 
392  Angle centerLat(centerLatLon.y(), Angle::Degrees);
393  Angle centerLon(centerLatLon.x(), Angle::Degrees);
394 
395  TProjection *tproj = (TProjection *) getWidget()->getProjection();
396 
397  if (tproj) {
398  bool longitudeWraps = false;
399  Distance radius(tproj->LocalRadius(centerLat.degrees()),
401 
402  // First we can get the angle between the latitudes...
403  // d = arcsin ( movementDistance / radiusDistance )
404  Angle deltaLat(asin( distanceFromCenter / radius ), Angle::Radians);
405 
406  latLonBoundingBox.setTop( (centerLat - deltaLat).degrees() );
407 
408  if (latLonBoundingBox.top() < -90 && centerLatLon.y() != -90) {
409 
410  // Block infinite recursion
411  if (centerLatLon.y() != 90) {
412  qWarning("The pole is included in the area but not centered");
413  centerLatLon.setY(-90);
414  return calcLatLonRange(centerLatLon, size);
415  }
416  else
417  return QRectF();
418  }
419  else if (centerLatLon.y() == -90) {
420  longitudeWraps = true;
421  }
422 
423  latLonBoundingBox.setBottom( (centerLat + deltaLat).degrees() );
424 
425  if (latLonBoundingBox.bottom() > 90 && centerLatLon.y() != 90) {
426 
427  // Block infinite recursion
428  if (centerLatLon.y() != -90) {
429  qWarning("The pole is included in the area but not centered");
430  centerLatLon.setY(90);
431  return calcLatLonRange(centerLatLon, size);
432  }
433  else
434  return QRectF();
435  }
436  else if (centerLatLon.y() == 90) {
437  longitudeWraps = true;
438  }
439 
440  // Now let's do lons...
441  Angle widestLat(
442  asin( sin(centerLat.radians()) /
443  cos( distanceFromCenter / radius ) ),
445 
446  double valueToASin = sin(distanceFromCenter / radius) /
447  cos(widestLat.radians());
448 
449  if(valueToASin < -1 || valueToASin > 1)
450  longitudeWraps = true;
451 
452  // Longitude wraps
453  if (longitudeWraps) {
454  if (tproj->Has360Domain()) {
455  latLonBoundingBox.setLeft( 0 );
456  latLonBoundingBox.setRight( 360 );
457  }
458  else {
459  latLonBoundingBox.setLeft( -180 );
460  latLonBoundingBox.setRight( 180 );
461  }
462  }
463  else {
464  Angle deltaLon(
465  asin( sin(distanceFromCenter / radius) /
466  cos(widestLat.radians())),
468  latLonBoundingBox.setLeft( (centerLon - deltaLon).degrees() );
469  latLonBoundingBox.setRight( (centerLon + deltaLon).degrees() );
470  }
471  }
472 
473  return latLonBoundingBox;
474  }
475 }
476 
QRectF calcLatLonRange(QPointF centerLatLon, Distance size)
Given a distance and a center lat,lon this will return the bounding lat,lon rect. ...
This widget encompasses the entire mosaic scene.
double radians() const
Convert an angle to a double.
Definition: Angle.h:243
Base class for Map TProjections.
Definition: TProjection.h:182
Base class for the MosaicTools.
Definition: MosaicTool.h:37
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
Definition: IString.cpp:226
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
Distance measurement, usually in meters.
Definition: Distance.h:47
bool isActive() const
Returns the activeness of this toool.
Definition: MosaicTool.h:50
MosaicAreaTool(MosaicSceneWidget *)
MosaicAreaTool constructor.
void addToMenu(QMenu *menu)
Adds the pan action to the given menu.
Base class for Map Projections.
Definition: Projection.h:171
Degrees are generally considered more human readable, 0-360 is one circle, however most math does not...
Definition: Angle.h:73
QAction * getPrimaryAction()
Adds the action to the toolpad.
double Longitude() const
This returns a longitude with correct longitude direction and domain as specified in the label object...
A single keyword-value pair.
Definition: PvlKeyword.h:98
QWidget * getToolBarWidget()
This method returns a widget that will be put in a tool bar when the tool is activated.
QWidget * createToolBarWidget()
Creates the widget to add to the tool bar.
QLineEdit * m_areaLineEdit
Input for latitude.
double ToDouble(const T &value)
Helper function to convert values to doubles.
Definition: HiCalUtil.h:248
Defines an angle and provides unit conversions.
Definition: Angle.h:62
QPixmap getIcon(QString iconName) const
returns the path to the icon directory.
Definition: MosaicTool.cpp:115
virtual bool SetCoordinate(const double x, const double y)
This method is used to set the projection x/y.
bool toBool(const QString &string)
Global function to convert from a string to a boolean.
Definition: IString.cpp:53
double Latitude() const
This returns a latitude with correct latitude type as specified in the label object.
QLineEdit * m_lonLineEdit
Input for longitude.
Namespace for ISIS/Bullet specific routines.
Definition: Apollo.h:31
The distance is being specified in meters.
Definition: Distance.h:56
Radians are generally used in mathematical equations, 0-2*PI is one circle, however these are more di...
Definition: Angle.h:80
Contains Pvl Groups and Pvl Objects.
Definition: PvlObject.h:74
QLineEdit * m_latLineEdit
Input for latitude.
ProjectionType
This enum defines the subclasses of Projection supported in Isis.
Definition: Projection.h:182
These projections are used to map triaxial and irregular-shaped bodies.
Definition: Projection.h:182