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
28namespace 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
Defines an angle and provides unit conversions.
Definition Angle.h:45
@ Degrees
Degrees are generally considered more human readable, 0-360 is one circle, however most math does not...
Definition Angle.h:56
@ Radians
Radians are generally used in mathematical equations, 0-2*PI is one circle, however these are more di...
Definition Angle.h:63
Distance measurement, usually in meters.
Definition Distance.h:34
@ Meters
The distance is being specified in meters.
Definition Distance.h:43
QLineEdit * m_lonLineEdit
Input for longitude.
QLineEdit * m_areaLineEdit
Input for latitude.
void addToMenu(QMenu *menu)
Adds the pan action to the given menu.
QWidget * getToolBarWidget()
This method returns a widget that will be put in a tool bar when the tool is activated.
MosaicAreaTool(MosaicSceneWidget *)
MosaicAreaTool constructor.
QAction * getPrimaryAction()
Adds the action to the toolpad.
QLineEdit * m_latLineEdit
Input for latitude.
QRectF calcLatLonRange(QPointF centerLatLon, Distance size)
Given a distance and a center lat,lon this will return the bounding lat,lon rect.
QWidget * createToolBarWidget()
Creates the widget to add to the tool bar.
This widget encompasses the entire mosaic scene.
Base class for the MosaicTools.
Definition MosaicTool.h:37
QPixmap getIcon(QString iconName) const
returns the path to the icon directory.
bool isActive() const
Returns the activeness of this toool.
Definition MosaicTool.h:50
Base class for Map Projections.
Definition Projection.h:155
ProjectionType
This enum defines the subclasses of Projection supported in Isis.
Definition Projection.h:166
@ Triaxial
These projections are used to map triaxial and irregular-shaped bodies.
Definition Projection.h:166
ProjectionType projectionType() const
Returns an enum value for the projection type.
A single keyword-value pair.
Definition PvlKeyword.h:87
Contains Pvl Groups and Pvl Objects.
Definition PvlObject.h:61
Base class for Map TProjections.
This is free and unencumbered software released into the public domain.
Definition Apollo.h:16
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
Definition IString.cpp:211
bool toBool(const QString &string)
Global function to convert from a string to a boolean.
Definition IString.cpp:38