USGS

Isis 3.0 Object Programmers' Reference

Home

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 <QMenu>
13 #include <QStackedWidget>
14 
15 #include "Brick.h"
16 #include "Camera.h"
17 #include "Cube.h"
18 #include "CubePlotCurve.h"
19 #include "Distance.h"
20 #include "InterestOperator.h"
21 #include "Latitude.h"
22 #include "Longitude.h"
23 #include "MdiCubeViewport.h"
24 #include "PlotWindow.h"
25 #include "PolygonTools.h"
26 #include "Projection.h"
27 #include "RingPlaneProjection.h"
28 #include "TProjection.h"
29 #include "Pvl.h"
30 #include "RubberBandComboBox.h"
31 #include "RubberBandTool.h"
32 #include "Statistics.h"
33 #include "SurfacePoint.h"
34 #include "ToolPad.h"
35 #include "UniversalGroundMap.h"
36 
37 using std::cerr;
38 
39 namespace Isis {
46  m_spatialCurves(new QMap<MdiCubeViewport *, QPointer<CubePlotCurve> >) {
47  //connect(m_toolPadAction, SIGNAL(activated()), this, SLOT(showPlotWindow()));
48  connect(this, SIGNAL(viewportChanged()), this, SLOT(viewportSelected()));
49 
50  m_xUnitsCombo = new QComboBox;
51  }
52 
53 
59  }
60 
61 
69  m_rubberBandCombo->reset();
70  m_rubberBandCombo->setVisible(true);
71  m_rubberBandCombo->setEnabled(true);
72  rubberBandTool()->setDrawActiveViewportOnly(false);
73  }
74 
75 
84  m_toolPadAction = new QAction(toolpad);
85  m_toolPadAction->setText("Spatial Plot Tool");
86  m_toolPadAction->setIcon(QPixmap(toolIconDir() + "/spatial_plot.png"));
87  QString text = "<b>Function:</b> Create a spatial plot of the selected pixels' DN values.";
88  m_toolPadAction->setWhatsThis(text);
89  return m_toolPadAction;
90  }
91 
92 
102  QWidget *wrapper = new QWidget(parent);
103 
108  true
109  );
110 
112  m_interpolationCombo->addItem("Nearest Neighbor",
113  Interpolator::NearestNeighborType);
114  m_interpolationCombo->addItem("BiLinear",
115  Interpolator::BiLinearType);
116  m_interpolationCombo->addItem("Cubic Convolution",
117  Interpolator::CubicConvolutionType);
118  m_interpolationCombo->setCurrentIndex(
119  m_interpolationCombo->findText("BiLinear"));
120  connect(m_interpolationCombo, SIGNAL(currentIndexChanged(int)),
121  this, SLOT(refreshPlot()));
122 
123  QWidget *abstractToolWidgets =
125 
126  QHBoxLayout *layout = new QHBoxLayout(wrapper);
127  layout->setMargin(0);
128  layout->addWidget(m_rubberBandCombo);
129  layout->addWidget(new QLabel("Interpolation:"));
130  layout->addWidget(m_interpolationCombo);
131  layout->addWidget(abstractToolWidgets);
132  layout->addWidget(m_xUnitsCombo);
133  layout->addStretch(1);
134  wrapper->setLayout(layout);
135 
136  return wrapper;
137  }
138 
139 
146 
147  PlotCurve::Units preferredUnits =
148  (PlotCurve::Units)m_xUnitsCombo->itemData(
149  m_xUnitsCombo->currentIndex()).toInt();
150 
151  while (m_xUnitsCombo->count())
152  m_xUnitsCombo->removeItem(0);
153 
154  m_xUnitsCombo->addItem("Pixel Number", PlotCurve::PixelNumber);
155 
156  bool haveGroundMaps = true;
157  foreach (MdiCubeViewport *cvp, viewportsToPlot()) {
158  haveGroundMaps = haveGroundMaps && cvp->universalGroundMap();
159  }
160 
161  if (haveGroundMaps) {
162  m_xUnitsCombo->addItem("Meters", PlotCurve::Meters);
163  m_xUnitsCombo->addItem("Kilometers", PlotCurve::Kilometers);
164  }
165 
166  if (m_xUnitsCombo->findData(preferredUnits) != -1) {
167  m_xUnitsCombo->setCurrentIndex(
168  m_xUnitsCombo->findData(preferredUnits));
169  }
170 
171  m_xUnitsCombo->setVisible(m_xUnitsCombo->count() > 1);
172  }
173 
174 
181  PlotWindow *window = new PlotWindow(
182  "Spatial " + PlotWindow::defaultWindowTitle(),
183  (PlotCurve::Units)m_xUnitsCombo->itemData(
184  m_xUnitsCombo->currentIndex()).toInt(),
185  PlotCurve::CubeDN, qobject_cast<QWidget *>(parent()));
186  return window;
187  }
188 
189 
196  m_spatialCurves->clear();
197  }
198 
199 
207  if (selectedWindow()) {
208  selectedWindow()->raise();
209  }
210 
211  if (rubberBandTool()->isValid()) {
212  refreshPlot();
213  }
214  else {
215  QMessageBox::information(NULL, "Error",
216  "The selected Area contains no valid pixels",
217  QMessageBox::Ok);
218  }
219  }
220 
221 
227  MdiCubeViewport *activeViewport = cubeViewport();
228 
229  if (activeViewport && rubberBandTool()->isValid()) {
230  // Find which window we want to paste into
231  PlotWindow *targetWindow = selectedWindow(true);
232 
233  // if the selected window won't work, create a new one
234  if (targetWindow->xAxisUnits() !=
235  m_xUnitsCombo->itemData(m_xUnitsCombo->currentIndex()).toInt()) {
236  targetWindow = addWindow();
237  }
238 
239  // get curves for active viewport and also for any linked viewports
240  foreach (MdiCubeViewport *viewport, viewportsToPlot()) {
241  QVector<QPointF> data = getSpatialStatistics(viewport);
242 
243  // load data into curve
244  if (data.size() > 0) {
245  QList<QPoint> rubberBandPoints = rubberBandTool()->vertices();
246 
248  int band = ((viewport->isGray()) ? viewport->grayBand() :
249  viewport->redBand());
250  (*m_spatialCurves)[viewport]->setData(new QwtPointSeriesData(data));
251  (*m_spatialCurves)[viewport]->setSource(
252  viewport, rubberBandPoints, band);
253  }
254  }
255 
256  targetWindow->replot();
257  updateTool();
258  }
259  }
260 
261 
267  PlotWindow *targetWindow = selectedWindow();
268 
269  if (targetWindow) {
270  PlotCurve::Units targetUnits = (PlotCurve::Units)m_xUnitsCombo->itemData(
271  m_xUnitsCombo->currentIndex()).toInt();
272 
273  QPen spatialPen(Qt::white);
274  spatialPen.setWidth(1);
275  spatialPen.setStyle(Qt::SolidLine);
276 
277  foreach (MdiCubeViewport *viewport, viewportsToPlot()) {
278  if (!(*m_spatialCurves)[viewport] ||
279  (*m_spatialCurves)[viewport]->xUnits() != targetUnits) {
280  CubePlotCurve *plotCurve = createCurve("DN Values", spatialPen,
281  targetUnits, CubePlotCurve::CubeDN);
282  m_spatialCurves->insert(viewport, plotCurve);
283  targetWindow->add(plotCurve);
284  }
285  }
286  }
287  }
288 
289 
290  SurfacePoint SpatialPlotTool::resultToSurfacePoint(UniversalGroundMap *groundMap) {
291  SurfacePoint result;
292 
293  if (groundMap) {
294  Distance radius;
295 
296  if (groundMap->Camera())
297  radius = groundMap->Camera()->LocalRadius();
298  else if (groundMap->Projection())
299  radius = Distance(groundMap->Projection()->LocalRadius(), Distance::Meters);
300 
301  result = SurfacePoint(Latitude(groundMap->UniversalLatitude(), Angle::Degrees),
302  Longitude(groundMap->UniversalLongitude(), Angle::Degrees), radius);
303  }
304 
305  return result;
306  }
307 
308 
317  QList<QPoint> vertices = rubberBandTool()->vertices();
318 
319  QVector<QPointF> data;
320 
321  PlotCurve::Units targetUnits = (PlotCurve::Units)m_xUnitsCombo->itemData(
322  m_xUnitsCombo->currentIndex()).toInt();
323 
324  if (cvp && vertices.size()) {
325  Interpolator interp;
326  interp.SetType(
328  m_interpolationCombo->currentIndex()).toInt());
329 
330  Portal dataReader(interp.Samples(), interp.Lines(),
331  cvp->cube()->pixelType());
332 
333  int band = ((cvp->isGray()) ? cvp->grayBand() : cvp->redBand());
334 
335  if (rubberBandTool()->currentMode() == RubberBandTool::LineMode) {
336  double startSample = Null;
337  double endSample = Null;
338  double startLine = Null;
339  double endLine = Null;
340 
341  cvp->viewportToCube(vertices[0].x(), vertices[0].y(),
342  startSample, startLine);
343  cvp->viewportToCube(vertices[1].x(), vertices[1].y(),
344  endSample, endLine);
345 
346  // round to the nearest pixel increment
347  int lineLength = qRound(sqrt(pow(startSample - endSample, 2) +
348  pow(startLine - endLine, 2)));
349 
350  SurfacePoint startPoint;
351  UniversalGroundMap *groundMap = cvp->universalGroundMap();
352  if (targetUnits != PlotCurve::PixelNumber) {
353  if (groundMap->SetImage(startSample, startLine)) {
354  startPoint = resultToSurfacePoint(groundMap);
355  }
356  else {
357  QMessageBox::warning(qobject_cast<QWidget *>(parent()),
358  tr("Failed to project points along line"),
359  tr("Failed to project (calculate a latitude, longitude, and radius) for the "
360  "starting point of the line (sample [%1], line [%2]).")
361  .arg(startSample).arg(startLine));
362  return data;
363  }
364  }
365 
366  if (lineLength > 0) {
367  for(int index = 0; index <= lineLength; index++) {
368  // % across * delta x + initial = x position of point (sample)
369  double sample = (index / (double)lineLength) * (endSample - startSample) +
370  startSample;
371  // move back for interpolation
372  sample -= (interp.Samples() / 2.0 - 0.5);
373 
374  double line = (index / (double)lineLength) * (endLine - startLine) +
375  startLine;
376  line -= (interp.Lines() / 2.0 - 0.5);
377 
378  dataReader.SetPosition(sample, line, band);
379  cvp->cube()->read(dataReader);
380 
381  double result = interp.Interpolate(sample + 0.5, line + 0.5, dataReader.DoubleBuffer());
382 
383  if (!IsSpecial(result)) {
384  double plotXValue = index + 1;
385 
386  if (targetUnits != PlotCurve::PixelNumber) {
387  plotXValue = sample;
388 
389  if (groundMap->SetImage(sample, line)) {
390  Distance xDistance = startPoint.GetDistanceToPoint(resultToSurfacePoint(groundMap));
391 
392  if (targetUnits == PlotCurve::Meters)
393  plotXValue = xDistance.meters();
394  else if (targetUnits == PlotCurve::Kilometers)
395  plotXValue = xDistance.kilometers();
396  }
397  else {
398  QMessageBox::warning(qobject_cast<QWidget *>(parent()),
399  tr("Failed to project points along line"),
400  tr("Failed to project (calculate a latitude, longitude, and radius) for a "
401  "point along the line (sample [%1], line [%2]).")
402  .arg(startSample).arg(startLine));
403  return data;
404  }
405  }
406 
407  data.append(QPointF(plotXValue, result));
408  }
409  }
410  }
411  else {
412  QMessageBox::information(NULL, "Error",
413  "The selected Area contains no valid pixels",
414  QMessageBox::Ok);
415  }
416  }
417  else if (rubberBandTool()->currentMode() == RubberBandTool::RotatedRectangleMode) {
418  /*
419  * We have a rotated rectangle:
420  *
421  * --across-->
422  * --------------
423  * |A B|
424  * | | |
425  * | | |
426  * | | |
427  * | | |
428  * | | | length
429  * | | |
430  * | | |
431  * | | |
432  * | | |
433  * | | V
434  * |D C|
435  * --------------
436  *
437  * A is the point where the user initially clicked to start drawing the
438  * rectangle. A is clickSample, clickLine.
439  *
440  * B is the initial mouse release that defines the width and direction
441  * of the rectangle. B is acrossSample, acrossLine.
442  *
443  * C is not needed for our calculations.
444  *
445  * D is endSample, endLine.
446  */
447  double clickSample = Null;
448  double clickLine = Null;
449  double acrossSample = Null;
450  double acrossLine = Null;
451  double endSample = Null;
452  double endLine = Null;
453 
454  cvp->viewportToCube(vertices[0].x(), vertices[0].y(),
455  clickSample, clickLine);
456  cvp->viewportToCube(vertices[1].x(), vertices[1].y(),
457  acrossSample, acrossLine);
458  cvp->viewportToCube(vertices[3].x(), vertices[3].y(),
459  endSample, endLine);
460 
461  double acrossVectorX = acrossSample - clickSample;
462  double acrossVectorY = acrossLine - clickLine;
463 
464  int acrossLength = qRound(sqrt(acrossVectorX * acrossVectorX +
465  acrossVectorY * acrossVectorY));
466  double sampleStepAcross = (1.0 / (double)acrossLength) * acrossVectorX;
467  double lineStepAcross = (1.0 / (double)acrossLength) * acrossVectorY;
468 
469  double lengthVectorSample = endSample - clickSample;
470  double lengthVectorLine = endLine - clickLine;
471  int rectangleLength = qRound(sqrt(lengthVectorSample * lengthVectorSample +
472  lengthVectorLine * lengthVectorLine));
473 
474  SurfacePoint startPoint;
475  UniversalGroundMap *groundMap = cvp->universalGroundMap();
476  if (targetUnits != PlotCurve::PixelNumber) {
477  double midStartSample = (clickSample + acrossSample) / 2.0;
478  double midStartLine = (clickLine + acrossLine) / 2.0;
479  if (groundMap->SetImage(midStartSample, midStartLine)) {
480  startPoint = resultToSurfacePoint(groundMap);
481  }
482  else {
483  QMessageBox::warning(qobject_cast<QWidget *>(parent()),
484  tr("Failed to project points along line"),
485  tr("Failed to project (calculate a latitude, longitude, and radius) for the "
486  "starting point of the line (sample [%1], line [%2]).")
487  .arg(midStartSample).arg(midStartLine));
488  return data;
489  }
490  }
491 
492  // walk the "green" line on the screen
493  for(int index = 0; index <= rectangleLength; index++) {
494  Statistics acrossStats;
495 
496  // % along length * lengthVectorSample + clickSample = x position of point
497  double sample = (index / (double)rectangleLength) * lengthVectorSample +
498  clickSample;
499  // move back for interpolation
500  sample -= (interp.Samples() / 2.0 - 0.5);
501 
502  double line = (index / (double)rectangleLength) * lengthVectorLine +
503  clickLine;
504  line -= (interp.Lines() / 2.0 - 0.5);
505 
506  double sampleMid = sample + (acrossLength / 2.0) * sampleStepAcross;
507  double lineMid = line + (acrossLength / 2.0) * lineStepAcross;
508 
509  for(int acrossPixel = 0;
510  acrossPixel <= acrossLength;
511  acrossPixel++) {
512  dataReader.SetPosition(sample, line, band);
513  cvp->cube()->read(dataReader);
514  double pixelValue = interp.Interpolate(sample + 0.5, line + 0.5,
515  dataReader.DoubleBuffer());
516 
517  if (!IsSpecial(pixelValue)) {
518  acrossStats.AddData(pixelValue);
519  }
520 
521  sample += sampleStepAcross;
522  line += lineStepAcross;
523  }
524 
525  if (!IsSpecial(acrossStats.Average())) {
526  double plotXValue = index + 1;
527 
528  if (targetUnits != PlotCurve::PixelNumber) {
529  if (groundMap->SetImage(sampleMid, lineMid)) {
530  Distance xDistance = startPoint.GetDistanceToPoint(resultToSurfacePoint(groundMap));
531 
532  if (targetUnits == PlotCurve::Meters)
533  plotXValue = xDistance.meters();
534  else if (targetUnits == PlotCurve::Kilometers)
535  plotXValue = xDistance.kilometers();
536  }
537  else {
538  QMessageBox::warning(qobject_cast<QWidget *>(parent()),
539  tr("Failed to project points along line"),
540  tr("Failed to project (calculate a latitude, longitude, and radius) for a "
541  "point along the line (sample [%1], line [%2]).")
542  .arg(sampleMid).arg(lineMid));
543  return data;
544  }
545  }
546 
547  data.append(QPointF(plotXValue, acrossStats.Average()));
548  }
549  }
550  }
551  }
552 
553  return data;
554  }
555 }
556