Isis 3 Programmer Reference
SpatialPlotTool.cpp
1#include "SpatialPlotTool.h"
2
3#include <iostream>
4
5#include <geos/geom/Polygon.h>
6#include <geos/geom/Point.h>
7
8#include <QHBoxLayout>
9#include <QLabel>
10#include <QMenu>
11#include <QMessageBox>
12#include <QStackedWidget>
13
14#include "Brick.h"
15#include "Camera.h"
16#include "Cube.h"
17#include "CubePlotCurve.h"
18#include "Distance.h"
19#include "InterestOperator.h"
20#include "Latitude.h"
21#include "Longitude.h"
22#include "MdiCubeViewport.h"
23#include "PlotWindow.h"
24#include "PolygonTools.h"
25#include "Projection.h"
26#include "RingPlaneProjection.h"
27#include "TProjection.h"
28#include "Pvl.h"
29#include "RubberBandComboBox.h"
30#include "RubberBandTool.h"
31#include "Statistics.h"
32#include "SurfacePoint.h"
33#include "ToolPad.h"
34#include "UniversalGroundMap.h"
35
36using std::cerr;
37
38namespace Isis {
45 m_spatialCurves(new QMap<MdiCubeViewport *, QPointer<CubePlotCurve> >) {
46 //connect(m_toolPadAction, SIGNAL(activated()), this, SLOT(showPlotWindow()));
47 connect(this, SIGNAL(viewportChanged()), this, SLOT(viewportSelected()));
48
49 m_xUnitsCombo = new QComboBox;
50 }
51
52
59
60
68 m_rubberBandCombo->reset();
69 m_rubberBandCombo->setVisible(true);
70 m_rubberBandCombo->setEnabled(true);
71 rubberBandTool()->setDrawActiveViewportOnly(false);
72 }
73
74
83 m_toolPadAction = new QAction(toolpad);
84 m_toolPadAction->setText("Spatial Plot Tool");
85 m_toolPadAction->setIcon(QPixmap(toolIconDir() + "/spatial_plot.png"));
86 QString text = "<b>Function:</b> Create a spatial plot of the selected pixels' DN values.";
87 m_toolPadAction->setWhatsThis(text);
88 return m_toolPadAction;
89 }
90
91
101 QWidget *wrapper = new QWidget(parent);
102
107 true
108 );
109
111 m_interpolationCombo->addItem("Nearest Neighbor",
112 Interpolator::NearestNeighborType);
113 m_interpolationCombo->addItem("BiLinear",
114 Interpolator::BiLinearType);
115 m_interpolationCombo->addItem("Cubic Convolution",
116 Interpolator::CubicConvolutionType);
117 m_interpolationCombo->setCurrentIndex(
118 m_interpolationCombo->findText("Nearest Neighbor"));
119 connect(m_interpolationCombo, SIGNAL(currentIndexChanged(int)),
120 this, SLOT(refreshPlot()));
121
122 QWidget *abstractToolWidgets =
124
125 QHBoxLayout *layout = new QHBoxLayout(wrapper);
126 layout->setMargin(0);
127 layout->addWidget(m_rubberBandCombo);
128 layout->addWidget(new QLabel("Interpolation:"));
129 layout->addWidget(m_interpolationCombo);
130 layout->addWidget(abstractToolWidgets);
131 layout->addWidget(m_xUnitsCombo);
132 layout->addStretch(1);
133 wrapper->setLayout(layout);
134
135 return wrapper;
136 }
137
138
145
146 PlotCurve::Units preferredUnits =
147 (PlotCurve::Units)m_xUnitsCombo->itemData(
148 m_xUnitsCombo->currentIndex()).toInt();
149
150 while (m_xUnitsCombo->count())
151 m_xUnitsCombo->removeItem(0);
152
153 m_xUnitsCombo->addItem("Pixel Number", PlotCurve::PixelNumber);
154
155 bool haveGroundMaps = true;
156 foreach (MdiCubeViewport *cvp, viewportsToPlot()) {
157 haveGroundMaps = haveGroundMaps && cvp->universalGroundMap();
158 }
159
160 if (haveGroundMaps) {
161 m_xUnitsCombo->addItem("Meters", PlotCurve::Meters);
162 m_xUnitsCombo->addItem("Kilometers", PlotCurve::Kilometers);
163 }
164
165 if (m_xUnitsCombo->findData(preferredUnits) != -1) {
166 m_xUnitsCombo->setCurrentIndex(
167 m_xUnitsCombo->findData(preferredUnits));
168 }
169
170 m_xUnitsCombo->setVisible(m_xUnitsCombo->count() > 1);
171 }
172
173
180 PlotWindow *window = new PlotWindow(
181 "Spatial " + PlotWindow::defaultWindowTitle(),
182 (PlotCurve::Units)m_xUnitsCombo->itemData(
183 m_xUnitsCombo->currentIndex()).toInt(),
184 PlotCurve::CubeDN, qobject_cast<QWidget *>(parent()));
185 return window;
186 }
187
188
195 m_spatialCurves->clear();
196 }
197
198
206 if (selectedWindow()) {
207 selectedWindow()->raise();
208 }
209
210 if (rubberBandTool()->isValid()) {
211 refreshPlot();
212 }
213 else {
214 QMessageBox::information(NULL, "Error",
215 "The selected Area contains no valid pixels",
216 QMessageBox::Ok);
217 }
218 }
219
220
226 MdiCubeViewport *activeViewport = cubeViewport();
227
228 if (activeViewport && rubberBandTool()->isValid()) {
229 // Find which window we want to paste into
230 PlotWindow *targetWindow = selectedWindow(true);
231
232 // if the selected window won't work, create a new one
233 if (targetWindow->xAxisUnits() !=
234 m_xUnitsCombo->itemData(m_xUnitsCombo->currentIndex()).toInt()) {
235 targetWindow = addWindow();
236 }
237
238 // get curves for active viewport and also for any linked viewports
239 foreach (MdiCubeViewport *viewport, viewportsToPlot()) {
240 QVector<QPointF> data = getSpatialStatistics(viewport);
241
242 // load data into curve
243 if (data.size() > 0) {
244 QList<QPoint> rubberBandPoints = rubberBandTool()->vertices();
245
247 int band = ((viewport->isGray()) ? viewport->grayBand() :
248 viewport->redBand());
249 (*m_spatialCurves)[viewport]->setData(new QwtPointSeriesData(data));
250 (*m_spatialCurves)[viewport]->setSource(
251 viewport, rubberBandPoints, band);
252 }
253 }
254
255 targetWindow->replot();
256 updateTool();
257 }
258 }
259
260
266 PlotWindow *targetWindow = selectedWindow();
267
268 if (targetWindow) {
269 PlotCurve::Units targetUnits = (PlotCurve::Units)m_xUnitsCombo->itemData(
270 m_xUnitsCombo->currentIndex()).toInt();
271
272 QPen spatialPen(Qt::white);
273 spatialPen.setWidth(1);
274 spatialPen.setStyle(Qt::SolidLine);
275
276 foreach (MdiCubeViewport *viewport, viewportsToPlot()) {
277 if (!(*m_spatialCurves)[viewport] ||
278 (*m_spatialCurves)[viewport]->xUnits() != targetUnits) {
279 CubePlotCurve *plotCurve = createCurve("DN Values", spatialPen,
280 targetUnits, CubePlotCurve::CubeDN);
281 m_spatialCurves->insert(viewport, plotCurve);
282 targetWindow->add(plotCurve);
283 }
284 }
285 }
286 }
287
288
289 SurfacePoint SpatialPlotTool::resultToSurfacePoint(UniversalGroundMap *groundMap) {
290 SurfacePoint result;
291
292 if (groundMap) {
293 Distance radius;
294
295 if (groundMap->Camera())
296 radius = groundMap->Camera()->LocalRadius();
297 else if (groundMap->Projection())
298 radius = Distance(groundMap->Projection()->LocalRadius(), Distance::Meters);
299
301 Longitude(groundMap->UniversalLongitude(), Angle::Degrees), radius);
302 }
303
304 return result;
305 }
306
307
316 QList<QPoint> vertices = rubberBandTool()->vertices();
317
318 QVector<QPointF> data;
319
320 PlotCurve::Units targetUnits = (PlotCurve::Units)m_xUnitsCombo->itemData(
321 m_xUnitsCombo->currentIndex()).toInt();
322
323 if (cvp && vertices.size()) {
324 Interpolator interp;
325 interp.SetType(
327 m_interpolationCombo->currentIndex()).toInt());
328
329 Portal dataReader(interp.Samples(), interp.Lines(),
330 cvp->cube()->pixelType());
331
332 int band = ((cvp->isGray()) ? cvp->grayBand() : cvp->redBand());
333
334 if (rubberBandTool()->currentMode() == RubberBandTool::LineMode) {
335 double startSample = Null;
336 double endSample = Null;
337 double startLine = Null;
338 double endLine = Null;
339
340 cvp->viewportToCube(vertices[0].x(), vertices[0].y(),
341 startSample, startLine);
342 cvp->viewportToCube(vertices[1].x(), vertices[1].y(),
343 endSample, endLine);
344
345 // round to the nearest pixel increment
346 int lineLength = qRound(sqrt(pow(startSample - endSample, 2) +
347 pow(startLine - endLine, 2)));
348
349 SurfacePoint startPoint;
350 UniversalGroundMap *groundMap = cvp->universalGroundMap();
351 if (targetUnits != PlotCurve::PixelNumber) {
352 if (groundMap->SetImage(startSample, startLine)) {
353 startPoint = resultToSurfacePoint(groundMap);
354 }
355 else {
356 QMessageBox::warning(qobject_cast<QWidget *>(parent()),
357 tr("Failed to project points along line"),
358 tr("Failed to project (calculate a latitude, longitude, and radius) for the "
359 "starting point of the line (sample [%1], line [%2]).")
360 .arg(startSample).arg(startLine));
361 return data;
362 }
363 }
364
365 if (lineLength > 0) {
366 for(int index = 0; index <= lineLength; index++) {
367 // % across * delta x + initial = x position of point (sample)
368 double sample = (index / (double)lineLength) * (endSample - startSample) +
369 startSample;
370 // move back for interpolation
371 sample -= (interp.Samples() / 2.0 - 0.5);
372
373 double line = (index / (double)lineLength) * (endLine - startLine) +
374 startLine;
375 line -= (interp.Lines() / 2.0 - 0.5);
376
377 dataReader.SetPosition(sample, line, band);
378 cvp->cube()->read(dataReader);
379
380 double result = interp.Interpolate(sample + 0.5, line + 0.5, dataReader.DoubleBuffer());
381
382 if (!IsSpecial(result)) {
383 double plotXValue = index + 1;
384
385 if (targetUnits != PlotCurve::PixelNumber) {
386 plotXValue = sample;
387
388 if (groundMap->SetImage(sample, line)) {
389 Distance xDistance = startPoint.GetDistanceToPoint(resultToSurfacePoint(groundMap));
390
391 if (targetUnits == PlotCurve::Meters)
392 plotXValue = xDistance.meters();
393 else if (targetUnits == PlotCurve::Kilometers)
394 plotXValue = xDistance.kilometers();
395 }
396 else {
397 QMessageBox::warning(qobject_cast<QWidget *>(parent()),
398 tr("Failed to project points along line"),
399 tr("Failed to project (calculate a latitude, longitude, and radius) for a "
400 "point along the line (sample [%1], line [%2]).")
401 .arg(startSample).arg(startLine));
402 return data;
403 }
404 }
405
406 data.append(QPointF(plotXValue, result));
407 }
408 }
409 }
410 else {
411 QMessageBox::information(NULL, "Error",
412 "The selected Area contains no valid pixels",
413 QMessageBox::Ok);
414 }
415 }
416 else if (rubberBandTool()->currentMode() == RubberBandTool::RotatedRectangleMode) {
417 /*
418 * We have a rotated rectangle:
419 *
420 * --acrossLength-->
421 * --------------------
422 * |A B|
423 * | | |
424 * | | |
425 * | | |
426 * | | |
427 * | | | rectangleLength
428 * | | |
429 * | | |
430 * | | |
431 * | | |
432 * | | V
433 * |D C|
434 * -------------------
435 *
436 * A is the point where the user initially clicked to start drawing the
437 * rectangle. A is clickSample, clickLine.
438 *
439 * B is the initial mouse release that defines the width and direction
440 * of the rectangle. B is acrossSample, acrossLine.
441 *
442 * C is not needed for our calculations.
443 *
444 * D is endSample, endLine.
445 */
446 double clickSample = Null;
447 double clickLine = Null;
448 double acrossSample = Null;
449 double acrossLine = Null;
450 double endSample = Null;
451 double endLine = Null;
452
453 cvp->viewportToCube(vertices[0].x(), vertices[0].y(),
454 clickSample, clickLine);
455 cvp->viewportToCube(vertices[1].x(), vertices[1].y(),
456 acrossSample, acrossLine);
457 cvp->viewportToCube(vertices[3].x(), vertices[3].y(),
458 endSample, endLine);
459
460 double acrossVectorX = acrossSample - clickSample;
461 double acrossVectorY = acrossLine - clickLine;
462
463 // Get length of "green" line on the screen
464 int acrossLength = qRound(sqrt(acrossVectorX * acrossVectorX +
465 acrossVectorY * acrossVectorY));
466
467 double sampleStepAcross = (1.0 / (double)acrossLength) * acrossVectorX;
468 double lineStepAcross = (1.0 / (double)acrossLength) * acrossVectorY;
469
470 double lengthVectorSample = endSample - clickSample;
471 double lengthVectorLine = endLine - clickLine;
472
473 // Get length of "red" line on the screen
474 int rectangleLength = qRound(sqrt(lengthVectorSample * lengthVectorSample +
475 lengthVectorLine * lengthVectorLine));
476
477 // Prevent length of zero for later calculations
478 if (rectangleLength == 0) {
479 rectangleLength = 1;
480 }
481
482 SurfacePoint startPoint;
483 UniversalGroundMap *groundMap = cvp->universalGroundMap();
484 if (targetUnits != PlotCurve::PixelNumber) {
485 double midStartSample = (clickSample + acrossSample) / 2.0;
486 double midStartLine = (clickLine + acrossLine) / 2.0;
487 if (groundMap->SetImage(midStartSample, midStartLine)) {
488 startPoint = resultToSurfacePoint(groundMap);
489 }
490 else {
491 QMessageBox::warning(qobject_cast<QWidget *>(parent()),
492 tr("Failed to project points along line"),
493 tr("Failed to project (calculate a latitude, longitude, and radius) for the "
494 "starting point of the line (sample [%1], line [%2]).")
495 .arg(midStartSample).arg(midStartLine));
496 return data;
497 }
498 }
499
500 // walk the "red" line on the screen
501 for(int index = 0; index <= rectangleLength; index++) {
502 Statistics acrossStats;
503
504 // % along length * lengthVectorSample + clickSample = x position of point
505 double sample = (index / (double)rectangleLength) * lengthVectorSample +
506 clickSample;
507 // move back for interpolation
508 sample -= (interp.Samples() / 2.0 - 0.5);
509
510 double line = (index / (double)rectangleLength) * lengthVectorLine +
511 clickLine;
512 line -= (interp.Lines() / 2.0 - 0.5);
513
514 double sampleMid = sample + (acrossLength / 2.0) * sampleStepAcross;
515 double lineMid = line + (acrossLength / 2.0) * lineStepAcross;
516
517 // For each pixel length in the red line direction, we are now recursing through each
518 // pixel length in the green line's direction and adding the pixel values
519 for(int acrossPixel = 0;
520 acrossPixel <= acrossLength;
521 acrossPixel++) {
522 dataReader.SetPosition(sample, line, band);
523 cvp->cube()->read(dataReader);
524 double pixelValue = interp.Interpolate(sample + 0.5, line + 0.5,
525 dataReader.DoubleBuffer());
526
527 if (!IsSpecial(pixelValue)) {
528 acrossStats.AddData(pixelValue);
529 }
530
531 sample += sampleStepAcross;
532 line += lineStepAcross;
533 }
534
535 if (!IsSpecial(acrossStats.Average())) {
536 double plotXValue = index + 1;
537
538 if (targetUnits != PlotCurve::PixelNumber) {
539 if (groundMap->SetImage(sampleMid, lineMid)) {
540 Distance xDistance = startPoint.GetDistanceToPoint(resultToSurfacePoint(groundMap));
541
542 if (targetUnits == PlotCurve::Meters)
543 plotXValue = xDistance.meters();
544 else if (targetUnits == PlotCurve::Kilometers)
545 plotXValue = xDistance.kilometers();
546 }
547 else {
548 QMessageBox::warning(qobject_cast<QWidget *>(parent()),
549 tr("Failed to project points along line"),
550 tr("Failed to project (calculate a latitude, longitude, and radius) for a "
551 "point along the line (sample [%1], line [%2]).")
552 .arg(sampleMid).arg(lineMid));
553 return data;
554 }
555 }
556
557 data.append(QPointF(plotXValue, acrossStats.Average()));
558 }
559 }
560 }
561 }
562
563 return data;
564 }
565}
566
Parent class for plotting tools which provides common functionality.
PlotWindow * selectedWindow(bool createIfNeeded=true)
Get the 'active' plot window (the window selected by the user to contain new curves).
QWidget * createToolBarWidget(QStackedWidget *parent)
This provides the standard plot tool options, such as selecting an active plot window.
QList< MdiCubeViewport * > viewportsToPlot()
Get a list of linked viewports that should be plotting when a new plot is requested.
virtual void updateTool()
This forwards all update calls to the plot windows.
PlotWindow * addWindow()
This creates and initializes everything about a plot window.
static CubePlotCurve * createCurve(QString name, QPen pen, PlotCurve::Units xUnits, PlotCurve::Units yUnits)
This is a helper method for children.
@ Degrees
Degrees are generally considered more human readable, 0-360 is one circle, however most math does not...
Definition Angle.h:56
This is a plot curve with information relating it to a particular cube or region of a cube.
int redBand() const
int grayBand() const
bool isGray() const
Distance measurement, usually in meters.
Definition Distance.h:34
@ Meters
The distance is being specified in meters.
Definition Distance.h:43
double meters() const
Get the distance in meters.
Definition Distance.cpp:85
Pixel interpolator.
interpType
The interpolator type, including: None, Nearest Neighbor, BiLinear or Cubic Convultion.
void SetType(const interpType &type)
Sets the type of interpolation.
This class is designed to encapsulate the concept of a Latitude.
Definition Latitude.h:51
This class is designed to encapsulate the concept of a Longitude.
Definition Longitude.h:40
Cube display widget for certain Isis MDI applications.
Units
These are all the possible units for the x or y data in a plot curve.
Definition PlotCurve.h:54
@ CubeDN
The data is a Cube DN value.
Definition PlotCurve.h:67
@ Kilometers
The data is in kilometers.
Definition PlotCurve.h:87
@ PixelNumber
The data is a pixel #.
Definition PlotCurve.h:79
@ Meters
The data is in meters.
Definition PlotCurve.h:83
static QString defaultWindowTitle()
This is the typical suffix for plot windows, it's here in case we want to update all plot windows to ...
Buffer for containing a two dimensional section of an image.
Definition Portal.h:36
Combo box for choosing a rubber band type.
@ RotatedRectangle
RotatedRectangle.
void setDrawActiveViewportOnly(bool activeOnly=false)
This called to set whether rubber band is drawn on active viewport only rather than all linked viewpo...
QList< QPoint > vertices()
This method returns the vertices.
Distance LocalRadius() const
Returns the local radius at the intersection point.
Definition Sensor.cpp:269
QAction * toolPadAction(ToolPad *pad)
This method configures the QAction for this tool.
SpatialPlotTool(QWidget *parent)
Create a spatial plot tool.
QPointer< QComboBox > m_interpolationCombo
Allows the user to choose the interpolation type.
QPointer< RubberBandComboBox > m_rubberBandCombo
Spatial plot rubber band combo box.
void validatePlotCurves()
This method sets up the names, line style, and color of the all the CubePlotCurves that will be used ...
QScopedPointer< QMap< MdiCubeViewport *, QPointer< CubePlotCurve > > > m_spatialCurves
Spatial curve.
void viewportSelected()
This protected slot is called when user selects a viewport.
virtual PlotWindow * createWindow()
Creates a new plot window compatible with the curves in this tool.
QPointer< QAction > m_toolPadAction
Plot tool's action.
void enableRubberBandTool()
This method is called when the tool is activated by the parent, or when the plot mode is changed.
void updateTool()
Updates plot tool.
QVector< QPointF > getSpatialStatistics(MdiCubeViewport *)
virtual void rubberBandComplete()
Called when the user has finished drawing with the rubber band.
void refreshPlot()
This method replots the data, with current settings and rubber band, in the plot window.
virtual void detachCurves()
Forget about all existing spatial plot curves.
QWidget * createToolBarWidget(QStackedWidget *parent)
Creates the widgets for the tool bar.
This class is used to accumulate statistics on double arrays.
Definition Statistics.h:94
This class defines a body-fixed surface point.
MdiCubeViewport * cubeViewport() const
Return the current cubeviewport.
Definition Tool.h:197
QString toolIconDir() const
returns the path to the icon directory.
Definition Tool.h:113
Universal Ground Map.
double UniversalLongitude() const
Returns the universal longitude of the camera model or projection.
bool SetImage(double sample, double line)
Returns whether the sample/line postion was set successfully in the camera model or projection.
double UniversalLatitude() const
Returns the universal latitude of the camera model or projection.
Isis::Projection * Projection() const
Return the projection associated with the ground map (NULL implies none)
Isis::Camera * Camera() const
Return the camera associated with the ground map (NULL implies none)
This is free and unencumbered software released into the public domain.
This is free and unencumbered software released into the public domain.
Definition Apollo.h:16
int toInt(const QString &string)
Global function to convert from a string to an integer.
Definition IString.cpp:93
const double Null
Value for an Isis Null pixel.
bool IsSpecial(const double d)
Returns if the input pixel is special.