USGS

Isis 3.0 Object Programmers' Reference

Home

SpectralPlotTool.cpp
1 #include "IsisDebug.h"
2 
3 #include "SpectralPlotTool.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 "Brick.h"
12 #include "Cube.h"
13 #include "CubePlotCurve.h"
14 #include "InterestOperator.h"
15 #include "MdiCubeViewport.h"
16 #include "SpectralPlotTool.h"
17 #include "SpectralPlotWindow.h"
18 #include "PolygonTools.h"
19 #include "Pvl.h"
20 #include "RubberBandComboBox.h"
21 #include "RubberBandTool.h"
22 #include "Statistics.h"
23 #include "ToolPad.h"
24 
25 using std::cerr;
26 
27 namespace Isis {
28 
37  AbstractPlotTool(parent),
38  m_maxCurves(new QMap< MdiCubeViewport *, QPointer<CubePlotCurve> >),
39  m_minCurves(new QMap< MdiCubeViewport *, QPointer<CubePlotCurve> >),
40  m_avgCurves(new QMap< MdiCubeViewport *, QPointer<CubePlotCurve> >),
41  m_stdDev1Curves(new QMap< MdiCubeViewport *, QPointer<CubePlotCurve> >),
42  m_stdDev2Curves(new QMap< MdiCubeViewport *, QPointer<CubePlotCurve> >),
43  m_stdErr1Curves(new QMap< MdiCubeViewport *, QPointer<CubePlotCurve> >),
44  m_stdErr2Curves(new QMap< MdiCubeViewport *, QPointer<CubePlotCurve> >) {
45  connect(this, SIGNAL(viewportChanged()), this, SLOT(viewportSelected()));
46 
48  }
49 
50 
56  //m_autoScale->setChecked(true);
57  }
58 
59 
66  {
67  ASSERT(m_displayCombo);
68  return m_displayCombo;
69  }
70 
71 
79  if (m_rubberBandCombo) {
80  m_rubberBandCombo->reset();
81  rubberBandTool()->setDrawActiveViewportOnly(false);
82 
83  m_rubberBandCombo->setEnabled(true);
84  m_rubberBandCombo->setVisible(true);
85  }
86  }
87 
88 
94  QDialog *selectCurvesDialog = new QDialog;
95  selectCurvesDialog->setWindowTitle("Select Curves to Plot");
96 
97  QGridLayout *layout = new QGridLayout;
98 
99  QLabel *header = new QLabel("Select which curves to plot when new data is "
100  "selected");
101  layout->addWidget(header, 0, 0, 1, 2, Qt::AlignHCenter);
102 
103  QList<QAction *> actions;
104  actions.append(m_plotAvgAction);
105  actions.append(m_plotMinAction);
106  actions.append(m_plotMaxAction);
107  actions.append(m_plotStdDev1Action);
108  actions.append(m_plotStdDev2Action);
109  actions.append(m_plotStdErr1Action);
110  actions.append(m_plotStdErr2Action);
111 
112  int row = 2;
113  foreach (QAction *action, actions) {
114  QLabel *label = new QLabel(action->text());
115  layout->addWidget(label, row, 0, 1, 1);
116 
117  QCheckBox *actionCheckbox = new QCheckBox;
118  actionCheckbox->setChecked(action->isChecked());
119  connect(actionCheckbox, SIGNAL(stateChanged(int)),
120  action, SLOT(toggle()));
121  layout->addWidget(actionCheckbox, row, 1, 1, 1, Qt::AlignRight);
122  row++;
123  }
124 
125  row++;
126  QPushButton *okButton = new QPushButton("Ok");
127  connect(okButton, SIGNAL(clicked()),
128  selectCurvesDialog, SLOT(close()));
129  layout->addWidget(okButton, row, 0, 1, 2);
130 
131  selectCurvesDialog->setLayout(layout);
132  selectCurvesDialog->exec();
133  }
134 
135 
144  m_toolPadAction = new QAction(toolpad);
145  m_toolPadAction->setText("Spectral Plot Tool");
146  m_toolPadAction->setIcon(QPixmap(toolIconDir() + "/spectral_plot.png"));
147  QString text = "<b>Function:</b> Create a spectral plot using statistics across a spectrum "
148  "(bands).";
149  m_toolPadAction->setWhatsThis(text);
150  return m_toolPadAction;
151  }
152 
153 
163  QWidget *wrapper = new QWidget(parent);
164  wrapper->setContextMenuPolicy(Qt::ActionsContextMenu);
165 
166  m_plotAvgAction = new QAction("Average", this);
167  m_plotAvgAction->setCheckable(true);
168  m_plotAvgAction->setChecked(true);
169 
170  m_plotMinAction = new QAction("Minimum", this);
171  m_plotMinAction->setCheckable(true);
172  m_plotMinAction->setChecked(false);
173 
174  m_plotMaxAction = new QAction("Maximum", this);
175  m_plotMaxAction->setCheckable(true);
176  m_plotMaxAction->setChecked(false);
177 
178  m_plotStdDev1Action = new QAction("+ Sigma", this);
179  m_plotStdDev1Action->setCheckable(true);
180  m_plotStdDev1Action->setChecked(false);
181 
182  m_plotStdDev2Action = new QAction("- Sigma", this);
183  m_plotStdDev2Action->setCheckable(true);
184  m_plotStdDev2Action->setChecked(false);
185 
186  m_plotStdErr1Action = new QAction("+ Std Error", this);
187  m_plotStdErr1Action->setCheckable(true);
188  m_plotStdErr1Action->setChecked(false);
189 
190  m_plotStdErr2Action = new QAction("- Std Error", this);
191  m_plotStdErr2Action->setCheckable(true);
192  m_plotStdErr2Action->setChecked(false);
193 
194  wrapper->addAction(m_plotAvgAction);
195  wrapper->addAction(m_plotMinAction);
196  wrapper->addAction(m_plotMaxAction);
197  wrapper->addAction(m_plotStdDev1Action);
198  wrapper->addAction(m_plotStdDev2Action);
199  wrapper->addAction(m_plotStdErr1Action);
200  wrapper->addAction(m_plotStdErr2Action);
201 
206  );
207 
208  QWidget *abstractToolWidgets =
210 
211  QPushButton *plotCurvesButton = new QPushButton("Select Curves to Plot");
212  connect(plotCurvesButton, SIGNAL(clicked()),
213  this, SLOT(selectCurvesToPlot()));
214 
215  QHBoxLayout *layout = new QHBoxLayout(wrapper);
216  layout->setMargin(0);
217  layout->addWidget(m_rubberBandCombo);
218  layout->addWidget(spectralDisplayCombo());
219  layout->addWidget(plotCurvesButton);
220  layout->addWidget(abstractToolWidgets);
221  layout->addStretch(1);
222  wrapper->setLayout(layout);
223 
224  return wrapper;
225  }
226 
227 
234  void SpectralPlotTool::addTo(QMenu *menu) {
235  menu->addAction(m_toolPadAction);
236  }
237 
238 
245 
246  PlotCurve::Units preferredUnits =
247  (PlotCurve::Units)m_displayCombo->itemData(
248  m_displayCombo->currentIndex()).toInt();
249 
250  while (m_displayCombo->count())
251  m_displayCombo->removeItem(0);
252 
253  m_displayCombo->addItem("Band Number", PlotCurve::Band);
254 
255  bool supportsWavelength = true;
256 
257  foreach (MdiCubeViewport *cvp, viewportsToPlot()) {
258  int bandCount = cvp->cube()->bandCount();
259 
260  // if single band then disable spectral plot
261  Pvl &pvl = *cvp->cube()->label();
262  supportsWavelength = supportsWavelength &&
263  pvl.findObject("IsisCube").hasGroup("BandBin");
264 
265  if (supportsWavelength) {
266  PvlGroup &bandBin = pvl.findObject("IsisCube").findGroup("BandBin");
267  supportsWavelength = supportsWavelength &&
268  bandBin.hasKeyword("Center") &&
269  bandBin["Center"].size() == bandCount;
270  }
271  }
272 
273  if (supportsWavelength) {
274  m_displayCombo->addItem("Wavelength", PlotCurve::Wavelength);
275  }
276 
277  if (m_displayCombo->findData(preferredUnits) != -1) {
278  m_displayCombo->setCurrentIndex(
279  m_displayCombo->findData(preferredUnits));
280  }
281 
282  m_displayCombo->setVisible(m_displayCombo->count() > 1);
283  }
284 
285 
292  PlotWindow *window = new SpectralPlotWindow(
293  (PlotCurve::Units)m_displayCombo->itemData(
294  m_displayCombo->currentIndex()).toInt(),
295  qobject_cast<QWidget *>(parent()));
296  window->setWindowTitle("Spectral " + PlotWindow::defaultWindowTitle());
297 
298  return window;
299  }
300 
301 
308  m_minCurves->clear();
309  m_maxCurves->clear();
310  m_avgCurves->clear();
311  m_stdDev1Curves->clear();
312  m_stdDev2Curves->clear();
313  m_stdErr1Curves->clear();
314  m_stdErr2Curves->clear();
315  }
316 
317 
325  if (selectedWindow()) {
326  selectedWindow()->raise();
327  }
328 
329  if (rubberBandTool()->isValid()) {
330  refreshPlot();
331  }
332  else {
333  QMessageBox::information(NULL, "Error",
334  "The selected Area contains no valid pixels",
335  QMessageBox::Ok);
336  }
337  }
338 
339 
344  MdiCubeViewport *activeViewport = cubeViewport();
345 
346  if (activeViewport && rubberBandTool()->isValid()) {
347  // Find which window we want to paste into
348  PlotWindow *targetWindow = selectedWindow(true);
349 
350  // if the selected window won't work, create a new one
351  if (targetWindow->xAxisUnits() !=
352  m_displayCombo->itemData(m_displayCombo->currentIndex()).toInt()) {
353  targetWindow = addWindow();
354  }
355 
356  // get curves for active viewport and also for any linked viewports
357  foreach (MdiCubeViewport *viewport, viewportsToPlot()) {
358  /* We'll need X-Axis labels and a xMax to scale to.*/
359  QVector<double> labels;
360  Statistics wavelengthStats;
361 
362  QVector<QPointF> avgData, minData, maxData, std1Data, std2Data,
363  stdErr1Data, stdErr2Data, wavelengthData;
364  QVector<Statistics> plotStats;
365 
366  getSpectralStatistics(labels, plotStats, viewport);
367 
368  for (int index = 0; index < labels.size(); index++) {
369  if (!IsSpecial(plotStats[index].Average()) &&
370  !IsSpecial(plotStats[index].Minimum()) &&
371  !IsSpecial(plotStats[index].Maximum())) {
372  avgData.append(QPointF(labels[index], plotStats[index].Average()));
373  minData.append(QPointF(labels[index], plotStats[index].Minimum()));
374  maxData.append(QPointF(labels[index], plotStats[index].Maximum()));
375 
376  if (!IsSpecial(plotStats[index].StandardDeviation())) {
377  std1Data.append(QPointF(labels[index],
378  plotStats[index].Average() +
379  plotStats[index].StandardDeviation()));
380  std2Data.append(QPointF(labels[index],
381  plotStats[index].Average() -
382  plotStats[index].StandardDeviation()));
383 
384  double standardError = plotStats[index].StandardDeviation() /
385  sqrt(plotStats[index].ValidPixels());
386 
387  stdErr1Data.append(QPointF(labels[index],
388  plotStats[index].Average() +
389  standardError));
390  stdErr2Data.append(QPointF(labels[index],
391  plotStats[index].Average() -
392  standardError));
393  }
394  }
395  } /*end for loop*/
396 
397  if (labels.size() > 0) {
398  QList<QPoint> rubberBandPoints = rubberBandTool()->vertices();
399 
401  if (m_plotAvgAction->isChecked()) {
402  (*m_avgCurves)[viewport]->setData(new QwtPointSeriesData(avgData));
403  (*m_avgCurves)[viewport]->setSource(viewport, rubberBandPoints);
404  }
405 
406  if (m_plotMinAction->isChecked()) {
407  (*m_minCurves)[viewport]->setData(new QwtPointSeriesData(minData));
408  (*m_minCurves)[viewport]->setSource(viewport, rubberBandPoints);
409  }
410 
411  if (m_plotMaxAction->isChecked()) {
412  (*m_maxCurves)[viewport]->setData(new QwtPointSeriesData(maxData));
413  (*m_maxCurves)[viewport]->setSource(viewport, rubberBandPoints);
414  }
415 
416  if (m_plotStdDev1Action->isChecked()) {
417  (*m_stdDev1Curves)[viewport]->setData(
418  new QwtPointSeriesData(std1Data));
419  (*m_stdDev1Curves)[viewport]->setSource(viewport,
420  rubberBandPoints);
421  }
422 
423  if (m_plotStdDev2Action->isChecked()) {
424  (*m_stdDev2Curves)[viewport]->setData(
425  new QwtPointSeriesData(std2Data));
426  (*m_stdDev2Curves)[viewport]->setSource(viewport,
427  rubberBandPoints);
428  }
429 
430  if (m_plotStdErr1Action->isChecked()) {
431  (*m_stdErr1Curves)[viewport]->setData(
432  new QwtPointSeriesData(stdErr1Data));
433  (*m_stdErr1Curves)[viewport]->setSource(viewport,
434  rubberBandPoints);
435  }
436 
437  if (m_plotStdErr2Action->isChecked()) {
438  (*m_stdErr2Curves)[viewport]->setData(
439  new QwtPointSeriesData(stdErr2Data));
440  (*m_stdErr2Curves)[viewport]->setSource(viewport,
441  rubberBandPoints);
442  }
443  }
444  }
445 
446  targetWindow->replot();
447  updateTool();
448  }
449  }
450 
451 
457  PlotWindow *targetWindow = selectedWindow();
458 
459  if (targetWindow) {
460  PlotCurve::Units targetUnits = (PlotCurve::Units)m_displayCombo->itemData(
461  m_displayCombo->currentIndex()).toInt();
462 
463  QPen avgPen(Qt::white);
464  avgPen.setWidth(1);
465  avgPen.setStyle(Qt::SolidLine);
466 
467  QPen minMaxPen(Qt::cyan);
468 // minMaxPen.setStyle(Qt::DashLine);
469  minMaxPen.setWidth(1);
470  minMaxPen.setStyle(Qt::SolidLine);
471 
472  QPen stdDevPen(Qt::red);
473  stdDevPen.setWidth(1);
474 // stdDevPen.setStyle(Qt::DotLine);
475  stdDevPen.setStyle(Qt::SolidLine);
476 
477  QPen stdErrPen(Qt::green);
478  stdErrPen.setWidth(1);
479 // stdErrPen.setStyle(Qt::DotLine);
480  stdErrPen.setStyle(Qt::SolidLine);
481 
482  foreach (MdiCubeViewport *viewport, viewportsToPlot()) {
483  if (m_plotAvgAction->isChecked() &&
484  (!(*m_avgCurves)[viewport] ||
485  (*m_avgCurves)[viewport]->xUnits() != targetUnits)) {
486  CubePlotCurve *plotCurve = createCurve("Average", avgPen,
487  targetUnits, CubePlotCurve::CubeDN);
488  m_avgCurves->insert(viewport, plotCurve);
489  targetWindow->add(plotCurve);
490  }
491 
492  if (m_plotMinAction->isChecked() &&
493  (!(*m_minCurves)[viewport] ||
494  (*m_minCurves)[viewport]->xUnits() != targetUnits)) {
495  CubePlotCurve *plotCurve = createCurve("Minimum", minMaxPen,
496  targetUnits, CubePlotCurve::CubeDN);
497  m_minCurves->insert(viewport, plotCurve);
498  targetWindow->add(plotCurve);
499  }
500 
501  if (m_plotMaxAction->isChecked() &&
502  (!(*m_maxCurves)[viewport] ||
503  (*m_maxCurves)[viewport]->xUnits() != targetUnits)) {
504  CubePlotCurve *plotCurve = createCurve("Maximum", minMaxPen,
505  targetUnits, CubePlotCurve::CubeDN);
506  m_maxCurves->insert(viewport, plotCurve);
507  targetWindow->add(plotCurve);
508  }
509 
510  if (m_plotStdDev1Action->isChecked() &&
511  (!(*m_stdDev1Curves)[viewport] ||
512  (*m_stdDev1Curves)[viewport]->xUnits() != targetUnits)) {
513  CubePlotCurve *plotCurve = createCurve("+ Sigma", stdDevPen,
514  targetUnits, CubePlotCurve::CubeDN);
515  m_stdDev1Curves->insert(viewport, plotCurve);
516  targetWindow->add(plotCurve);
517  }
518 
519  if (m_plotStdDev2Action->isChecked() &&
520  (!(*m_stdDev2Curves)[viewport] ||
521  (*m_stdDev2Curves)[viewport]->xUnits() != targetUnits)) {
522  CubePlotCurve *plotCurve = createCurve("- Sigma", stdDevPen,
523  targetUnits, CubePlotCurve::CubeDN);
524  m_stdDev2Curves->insert(viewport, plotCurve);
525  targetWindow->add(plotCurve);
526  }
527 
528  if (m_plotStdErr1Action->isChecked() &&
529  (!(*m_stdErr1Curves)[viewport] ||
530  (*m_stdErr1Curves)[viewport]->xUnits() != targetUnits)) {
531  CubePlotCurve *plotCurve = createCurve("+ Std Error", stdErrPen,
532  targetUnits, CubePlotCurve::CubeDN);
533  m_stdErr1Curves->insert(viewport, plotCurve);
534  targetWindow->add(plotCurve);
535  }
536 
537  if (m_plotStdErr2Action->isChecked() &&
538  (!(*m_stdErr2Curves)[viewport] ||
539  (*m_stdErr2Curves)[viewport]->xUnits() != targetUnits)) {
540  CubePlotCurve *plotCurve = createCurve("- Std Error", stdErrPen,
541  targetUnits, CubePlotCurve::CubeDN);
542  m_stdErr2Curves->insert(viewport, plotCurve);
543  targetWindow->add(plotCurve);
544  }
545  }
546  }
547  }
548 
549 
561  QVector<Statistics> &data,
562  MdiCubeViewport *viewport) {
563  QList<QPoint> vertices = rubberBandTool()->vertices();
564 
565  if (vertices.size() < 1) return;
566 
567  double ss, sl, es, el, x, y;
568  std::vector <int> x_contained, y_contained;
569 
570  // Convert vertices to their sub-pixel sample/line values
571  viewport->viewportToCube(vertices[0].x(), vertices[0].y(), ss, sl);
572  viewport->viewportToCube(vertices[2].x(), vertices[2].y(), es, el);
573 
574  // round the start and end sample/line sub-pixel points to the nearest int (pixel)
575  ss = round(ss);
576  sl = round(sl);
577  es = round(es);
578  el = round(el);
579 
580  // calculate number of samples will be in Brick's shape buffer with absolute value
581  // in case user makes a rectangle from right to left
582  int samps = ( (int)abs(es - ss) + 1) ;
583  Cube *cube = viewport->cube();
584  Brick *brick = new Brick(*cube, samps, 1, 1);
585  Pvl &pvl = *viewport->cube()->label();
586 
587  if (rubberBandTool()->currentMode() == RubberBandTool::PolygonMode) {
588 // samps = 1;
589  geos::geom::CoordinateSequence *pts = new geos::geom::CoordinateArraySequence();
590  for (int i = 0; i < vertices.size(); i++) {
591  viewport->viewportToCube(vertices[i].x(), vertices[i].y(), x, y);
592  // add the x,y vertices (double) to the pts CoordinateSequence
593  pts->add(geos::geom::Coordinate(x, y));
594  }/*end for*/
595 
596  /*Add the first point again in order to make a closed line string*/
597  viewport->viewportToCube(vertices[0].x(), vertices[0].y(), x, y);
598  pts->add(geos::geom::Coordinate(x, y));
599 
600  geos::geom::Polygon *poly = globalFactory.createPolygon(
601  globalFactory.createLinearRing(pts), NULL);
602 
603  const geos::geom::Envelope *envelope = poly->getEnvelopeInternal();
604 
605  // round the (double) max x's and y's and min x's and y's to the nearest pixel
606  for (int y = (int)round(envelope->getMinY());
607  y <= (int)round(envelope->getMaxY()); y++) {
608  for (int x = (int)round(envelope->getMinX());
609  x <= (int)round(envelope->getMaxX()); x++) {
610  // create a point at the center of the pixel
611  geos::geom::Coordinate c(x, y);
612  geos::geom::Point *p = globalFactory.createPoint(c);
613  // check if the center of the pixel is in the polygon's envelope (the selection)
614  bool contains = p->within(poly);
615  delete p;
616  if (contains) {
617  // these pixels will be used for computing statistics
618  x_contained.push_back(x);
619  y_contained.push_back(y);
620  }
621 
622  } /*end x*/
623  }/*end y*/
624 
625  delete poly;
626  poly = NULL;
627  }
628 
629 
630  for (int band = 1; band <= cube->bandCount(); band++) {
631  Statistics stats;
632 
633  /*Rectangle*/
634  if (rubberBandTool()->currentMode() == RubberBandTool::RectangleMode) {
635  for (int line = (int)std::min(sl, el); line <= (int)std::max(sl, el); line++) {
636  // set Brick's base position at left-most endpoint
637  brick->SetBasePosition(std::min(ss, es), line, band);
638  cube->read(*brick);
639  stats.AddData(brick->DoubleBuffer(), samps);
640  }
641  }
642 
643  /*Polygon*/
644  if (rubberBandTool()->currentMode() == RubberBandTool::PolygonMode) {
645  for (unsigned int j = 0; j < x_contained.size(); j++) {
646 
647  brick->SetBasePosition(x_contained[j], y_contained[j], band);
648  cube->read(*brick);
649  stats.AddData(*brick->DoubleBuffer());
650 
651  }
652  }
653 
654 
655  PlotCurve::Units targetUnits = (PlotCurve::Units)m_displayCombo->itemData(
656  m_displayCombo->currentIndex()).toInt();
657  if (targetUnits == PlotCurve::Band) {
658  labels.push_back(band);
659  }
660  else if (targetUnits == PlotCurve::Wavelength) {
661  if (pvl.findObject("IsisCube").hasGroup("BandBin")) {
662  PvlGroup &bandBin = pvl.findObject("IsisCube").findGroup("BandBin");
663  if (bandBin.hasKeyword("Center")) {
664  PvlKeyword &wavelength = bandBin.findKeyword("Center");
665  if (wavelength.size() > (band - 1)) {
666  labels.push_back(toDouble(wavelength[band-1]));
667  }
668  }
669  }
670  }
671 
672  if (stats.Average() == Null) {
673  data.push_back(stats);
674  }
675  else {
676  data.push_back(stats);
677  }
678  }
679 
680  delete brick;
681  }
682 }
683