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