Isis 3 Programmer Reference
MosaicSceneWidget.cpp
1 #include "IsisDebug.h"
2 
3 #include "MosaicSceneWidget.h"
4 
5 #include <sstream>
6 
7 #include <QFileDialog>
8 #include <QGraphicsSceneContextMenuEvent>
9 #include <QGridLayout>
10 #include <QHBoxLayout>
11 #include <QLabel>
12 #include <QMessageBox>
13 #include <QMenu>
14 #include <QRubberBand>
15 #include <QScrollBar>
16 #include <QStatusBar>
17 #include <QString>
18 #include <QToolButton>
19 #include <QToolTip>
20 #include <QtCore>
21 #include <QtWidgets>
22 #include <QtXml>
23 
24 #include "Camera.h"
25 #include "Cube.h"
26 #include "Directory.h"
27 #include "Distance.h"
28 #include "FileName.h"
29 #include "GraphicsView.h"
30 #include "Image.h"
31 #include "ImageList.h"
32 #include "Latitude.h"
33 #include "Longitude.h"
34 #include "MosaicAreaTool.h"
35 #include "MosaicControlNetTool.h"
36 #include "MosaicFindTool.h"
37 #include "MosaicGraphicsScene.h"
38 #include "MosaicGraphicsView.h"
39 #include "MosaicGridTool.h"
40 #include "MosaicPanTool.h"
41 #include "MosaicSceneItem.h"
42 #include "MosaicSelectTool.h"
43 #include "MosaicTrackTool.h"
44 #include "MosaicZoomTool.h"
45 #include "MoveDownOneSceneWorkOrder.h"
46 #include "MoveToBottomSceneWorkOrder.h"
47 #include "MoveToTopSceneWorkOrder.h"
48 #include "MoveUpOneSceneWorkOrder.h"
49 #include "ProgressBar.h"
50 #include "Project.h"
51 #include "Projection.h"
52 #include "ProjectionConfigDialog.h"
53 #include "ProjectionFactory.h"
54 #include "PvlObject.h"
55 #include "Pvl.h"
56 #include "Shape.h"
57 #include "TextFile.h"
58 #include "Target.h"
59 #include "ToolPad.h"
60 #include "XmlStackedHandlerReader.h"
61 
62 namespace Isis {
66  MosaicSceneWidget::MosaicSceneWidget(QStatusBar *status, bool showTools,
67  bool internalizeToolBarsAndProgress, Directory *directory,
68  QWidget *parent) : QWidget(parent) {
69  m_projectImageZOrders = NULL;
70  m_projectViewTransform = NULL;
71  m_directory = directory;
72 
73  m_mosaicSceneItems = new QList<MosaicSceneItem *>;
74 
76  m_graphicsScene->installEventFilter(this);
77 
80  m_graphicsView->setInteractive(true);
81 // m_graphicsView->setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate);
82  m_graphicsView->setResizeAnchor(QGraphicsView::AnchorViewCenter);
83  // This enables OpenGL acceleration
84 // m_graphicsView->setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers)));
85 // QPixmapCache::setCacheLimit(1024 * 1024);
86 
87  m_projection = NULL;
88  m_mapButton = NULL;
89  m_quickMapAction = NULL;
90 
91  m_cubesSelectable = true;
92  m_customRubberBandEnabled = false;
93  m_customRubberBand = NULL;
94  m_rubberBandOrigin = NULL;
95  m_outlineRect = NULL;
96  m_blockingSelectionChanged = false;
97  m_queuedSelectionChanged = false;
98  m_shouldRequeueSelectionChanged = false;
99 
100  m_userToolControl = false;
101  m_ownProjection = false;
102 
103  m_progress = new ProgressBar;
104  m_progress->setVisible(false);
105 
106  QGridLayout * sceneLayout = new QGridLayout;
107  sceneLayout->setContentsMargins(0, 0, 0, 0);
108  setLayout(sceneLayout);
109 
110  // If we are making our own layout, we can create our own status area.
111  if (!status && internalizeToolBarsAndProgress)
112  status = new QStatusBar;
113 
114  // Create the tools we want
115  m_tools = new QList<MosaicTool *>;
116  m_tools->append(new MosaicSelectTool(this));
117  m_tools->append(new MosaicZoomTool(this));
118  m_tools->append(new MosaicPanTool(this));
119  MosaicControlNetTool *cnetTool = new MosaicControlNetTool(this);
120  m_tools->append(cnetTool);
121 
122  // Pass on Signals emitted from MosaicControlNetTool
123  // TODO 2016-09-09 TLS Design: Use a proxy model instead of signals?
124  connect(cnetTool, SIGNAL(modifyControlPoint(ControlPoint *)),
125  this, SIGNAL(modifyControlPoint(ControlPoint *)));
126 
127  connect(cnetTool, SIGNAL(deleteControlPoint(ControlPoint *)),
128  this, SIGNAL(deleteControlPoint(ControlPoint *)));
129 
130  connect(cnetTool, SIGNAL(createControlPoint(double, double)),
131  this, SIGNAL(createControlPoint(double, double)));
132 
133  // Pass on signals to the MosaicControlNetTool
134  connect(this, SIGNAL(cnetModified()), cnetTool, SLOT(rebuildPointGraphics()));
135 
136  m_tools->append(new MosaicAreaTool(this));
137  m_tools->append(new MosaicFindTool(this));
138  m_tools->append(new MosaicGridTool(this));
139  if (status)
140  m_tools->append(new MosaicTrackTool(this, status));
141 
142  m_tools->at(0)->activate(true);
143 
144  if (showTools) {
145 
146  if (internalizeToolBarsAndProgress) {
147  // Internalized Toolbar Layout:
148  /*
149  * -------TOOLBARS------ Colspan=2, Rowspan=1
150  * | SCENE | T |
151  * | CS=1, RS=1 | O |
152  * | | O |
153  * | | L |
154  * | | B |
155  * | | A |
156  * | | R |*Vertical tool bar CS=1, RS=1
157  * ----PROGRESS---STATUS- Colspan=2, Rowspan=1
158  *
159  *
160  */
161  QHBoxLayout *horizontalToolBarsLayout = new QHBoxLayout;
162 
163  m_permToolbar = new QToolBar("Standard Tools");
164  m_permToolbar->setWhatsThis("This area contains options that are always present in the "
165  "footprint view");
166  horizontalToolBarsLayout->addWidget(m_permToolbar);
167 
168  m_activeToolbar = new QToolBar("Active Tool", this);
169  m_activeToolbar->setObjectName("Active Tool");
170  m_activeToolbar->setWhatsThis("The currently selected tool's options will "
171  "show up here. Not all tools have options.");
172  horizontalToolBarsLayout->addWidget(m_activeToolbar);
173 
174  sceneLayout->addLayout(horizontalToolBarsLayout, 0, 0, 1, 2);
175 
176  sceneLayout->addWidget(m_graphicsView, 1, 0, 1, 1);
177 
178  m_toolpad = new ToolPad("Tool Pad", this);
179  m_toolpad->setObjectName("Tool Pad");
180  m_toolpad->setOrientation(Qt::Vertical);
181  m_toolpad->setFloatable(true);
182  sceneLayout->addWidget(m_toolpad, 1, 1, 1, 1);
183 
184  QHBoxLayout *horizontalStatusLayout = new QHBoxLayout;
185  horizontalStatusLayout->addWidget(m_progress);
186  horizontalStatusLayout->addStretch();
187  horizontalStatusLayout->addWidget(status);
188 
189  sceneLayout->addLayout(horizontalStatusLayout, 2, 0, 1, 2);
190 
191  addToPermanent(m_permToolbar);
192  m_permToolbar->addSeparator();
193 
194  addTo(m_activeToolbar);
195  addTo(m_toolpad);
196  }
197  else {
198  sceneLayout->addWidget(m_graphicsView, 0, 0, 1, 1);
199  }
200 
201  m_userToolControl = true;
202 
203  setWhatsThis("This is the mosaic scene. The opened cubes will be "
204  "shown here. You can fully interact with the files shown here.");
205 
206  getView()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
207  getView()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
208 
209  getView()->enableResizeZooming(false);
210 
211  connect(getView()->horizontalScrollBar(), SIGNAL(valueChanged(int)),
212  this, SLOT(sendVisibleRectChanged()));
213  connect(getView()->verticalScrollBar() , SIGNAL(valueChanged(int)),
214  this, SLOT(sendVisibleRectChanged()));
215  connect(getView()->horizontalScrollBar(), SIGNAL(rangeChanged(int, int)),
216  this, SLOT(sendVisibleRectChanged()));
217  connect(getView()->verticalScrollBar() , SIGNAL(rangeChanged(int, int)),
218  this, SLOT(sendVisibleRectChanged()));
219 
220  }
221  else {
222  sceneLayout->addWidget(m_graphicsView, 0, 0, 1, 1);
223 
224  getView()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
225  getView()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
226 
227  setWhatsThis("This is the mosaic world view. The opened cubes will be "
228  "shown here, but you cannot zoom in. You can select cubes by dragging "
229  "a box over them, zoom to a particular cube by right clicking on it "
230  "and selecting 'Zoom Fit', and many other actions are available.");
231  }
232 
233  // These values are OK as long as they are less than the min/greater than the max. No footprints
234  // are available yet, so 0 qualifies for both (makes a good starting point).
235  m_currentMinimumFootprintZ = 0;
236  m_currentMaximumFootprintZ = 0;
237 
238  connect(getScene(), SIGNAL(selectionChanged()),
239  this, SLOT(onSelectionChanged()));
240  // This is set up to do a single selection changed after all selections have changed, instead
241  // of 1 selection changed per 1 changed item.
242  connect(this, SIGNAL(queueSelectionChanged()),
243  this, SLOT(onQueuedSelectionChanged()), Qt::QueuedConnection);
244  }
245 
246  MosaicSceneWidget::~MosaicSceneWidget() {
247  m_outlineRect = NULL; // The scene will clean this up
248 
249  if (m_tools) {
250  foreach(MosaicTool *tool, *m_tools) {
251  delete tool;
252  tool = NULL;
253  }
254 
255  delete m_tools;
256  m_tools = NULL;
257  }
258 
259  if (m_ownProjection && m_projection) {
260  delete m_projection;
261  }
262  m_projection = NULL;
263 
264  delete m_projectImageZOrders;
265  m_projectImageZOrders = NULL;
266 
267  delete m_projectViewTransform;
268  m_projectViewTransform = NULL;
269  }
270 
271 
272  void MosaicSceneWidget::setProjection(const PvlGroup &mapping, Pvl label) {
273  Pvl tmp;
274  tmp += mapping;
275 
276  if (!mapping.hasKeyword("EquatorialRadius")) {
277  PvlGroup radii = Target::radiiGroup(label, mapping);
278  tmp.findGroup("Mapping") += radii["EquatorialRadius"];
279  tmp.findGroup("Mapping") += radii["PolarRadius"];
280  }
281 
283  m_ownProjection = true;
284  }
285 
286 
291  PvlGroup mapping(proj->Mapping());
292 
293  if (m_mapButton) {
294  PvlKeyword projectionKeyword = mapping.findKeyword("ProjectionName");
295  QString projName = projectionKeyword[0];
296  m_mapButton->setText(tr("View/Edit %1 Projection").arg(projName));
297  }
298 
299  Projection *old = m_projection;
300  m_projection = proj;
301 
302  reprojectItems();
303  emit projectionChanged(m_projection);
304 
305  if (old && m_ownProjection) {
306  delete old;
307  old = NULL;
308  }
309 
310  m_ownProjection = false;
311  }
312 
313 
314 
315  void MosaicSceneWidget::setOutlineRect(QRectF outline) {
316  if (outline.united(getView()->sceneRect()) != getView()->sceneRect())
317  outline = QRectF();
318 
319  if (!m_outlineRect) {
320  m_outlineRect = getScene()->addRect(outline,
321  QPen(Qt::black),
322  Qt::NoBrush);
323  m_outlineRect->setZValue(DBL_MAX);
324  }
325  else {
326  m_outlineRect->setRect(outline);
327  }
328 
329  if (!m_userToolControl)
330  refit();
331  }
332 
333 
334  PvlGroup MosaicSceneWidget::createInitialProjection(
335  Image *image) {
336  Projection *proj = NULL;
337  Cube *cube = image->cube();
338  Pvl *label = cube->label();
339 
340  try {
341  proj = ProjectionFactory::CreateFromCube(*label);
342  return proj->Mapping();
343  }
344  catch (IException &) {
345  Pvl mappingPvl("$base/templates/maps/equirectangular.map");
346  PvlGroup &mappingGrp = mappingPvl.findGroup("Mapping");
347  mappingGrp += PvlKeyword("LatitudeType", "Planetocentric");
348  mappingGrp += PvlKeyword("LongitudeDirection", "PositiveEast");
349  mappingGrp += PvlKeyword("LongitudeDomain", "360");
350  mappingGrp += PvlKeyword("CenterLatitude", "0");
351  mappingGrp += PvlKeyword("CenterLongitude", "180");
352  mappingGrp += PvlKeyword("MinimumLatitude", "-90");
353  mappingGrp += PvlKeyword("MaximumLatitude", "90");
354  mappingGrp += PvlKeyword("MinimumLongitude", "0");
355  mappingGrp += PvlKeyword("MaximumLongitude", "360");
356 
357  try {
358  Camera * cam = cube->camera();
359  Distance radii[3];
360  cam->radii(radii);
361 
362  mappingGrp += PvlKeyword("TargetName", cam->target()->name());
363  mappingGrp += PvlKeyword("EquatorialRadius", toString(radii[0].meters()),
364  "meters");
365  mappingGrp += PvlKeyword("PolarRadius", toString(radii[2].meters()),
366  "meters");
367 
368  }
369  catch (IException &) {
370  mappingGrp +=
371  label->findGroup("Instrument", Pvl::Traverse)["TargetName"];
372  }
373 
374  return mappingGrp;
375  }
376  }
377 
378 
379  void MosaicSceneWidget::addToPermanent(QToolBar *perm) {
380  m_mapButton = new QToolButton(this);
381  connect(this, SIGNAL(destroyed()), m_mapButton, SLOT(deleteLater()));
382  m_mapButton->setText(tr("View/Edit/Load Map File"));
383  m_mapButton->setToolTip(tr("View/Edit/Load Map File"));
384  m_mapButton->setIcon(QIcon(FileName("$base/icons/ographic.png").expanded()));
385  m_mapButton->setWhatsThis(tr("This is the projection used by the mosaic "
386  "scene. Cubes can not be shown in the scene without a projection, so "
387  "if one is not selected, a default of Equirectangular will be used. "
388  "The selected file should be a map file, examples are available in "
389  "$base/templates/maps."));
390  m_mapButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
391  connect(m_mapButton, SIGNAL(clicked()), this, SLOT(configProjectionParameters()));
392 
393  if (m_projection) {
394  PvlKeyword projectionKeyword =
395  m_projection->Mapping().findKeyword("ProjectionName");
396  QString projName = projectionKeyword[0];
397  m_mapButton->setText(projName);
398  }
399 
400  m_quickMapAction = new QAction(tr("Quick Load Map"), this);
401  m_quickMapAction->setToolTip(tr("Quick Load Map"));
402  m_quickMapAction->setIcon(QIcon(FileName("$base/icons/quickopen.png").expanded()));
403  m_quickMapAction->setWhatsThis(tr("This is the projection used by the mosaic "
404  "scene. Cubes can not be shown in the scene without a projection, so "
405  "if one is not selected, a default of Equirectangular will be used."));
406  connect(m_quickMapAction, SIGNAL(triggered()), this, SLOT(quickConfigProjectionParameters()));
407 
408  perm->addWidget(m_mapButton);
409  perm->addAction(m_quickMapAction);
410  }
411 
412 
413  void MosaicSceneWidget::addTo(QToolBar *toolbar) {
414  MosaicTool *tool;
415  foreach(tool, *m_tools) {
416  tool->addTo(toolbar);
417  }
418  }
419 
420 
421  void MosaicSceneWidget::addTo(QMenu *menu) {
422  MosaicTool *tool;
423  foreach(tool, *m_tools) {
424  tool->addTo(menu);
425  }
426  }
427 
428 
429  void MosaicSceneWidget::addTo(ToolPad *toolPad) {
430  MosaicTool *tool;
431  foreach(tool, *m_tools) {
432  tool->addTo(toolPad);
433  }
434  }
435 
436 
442  bool MosaicSceneWidget::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) {
443 
444  bool handled = false;
445  QList<QGraphicsItem *> selectedGraphicsItems = getScene()->selectedItems();
446  QList<MosaicSceneItem *> selectedImageItems;
448  foreach (QGraphicsItem *graphicsItem, selectedGraphicsItems) {
449  MosaicSceneItem *sceneImageItem = dynamic_cast<MosaicSceneItem *>(graphicsItem);
450 
451  if (!sceneImageItem) {
452  sceneImageItem = dynamic_cast<MosaicSceneItem *>(graphicsItem->parentItem());
453  }
454 
455  if (sceneImageItem && sceneImageItem->image()) {
456  selectedImageItems.append(sceneImageItem);
457  selectedImages.append(sceneImageItem->image());
458  }
459  }
460 
461  if (selectedImageItems.count()) {
462  QMenu menu;
463 
464  QAction *title = menu.addAction(tr("%L1 Selected Images").arg(selectedImages.count()));
465  title->setEnabled(false);
466  menu.addSeparator();
467 
468  Project *project = m_directory ? m_directory->project() : NULL;
469 
470  QList<QAction *> displayActs = selectedImages.supportedActions(project);
471 
472  if (m_directory) {
473  displayActs.append(NULL);
474  displayActs.append(m_directory->supportedActions(new ImageList(selectedImages)));
475  }
476 
477  QAction *displayAct;
478  foreach(displayAct, displayActs) {
479  if (displayAct == NULL) {
480  menu.addSeparator();
481  }
482  else {
483  menu.addAction(displayAct);
484  }
485  }
486 
487  handled = true;
488  menu.exec(event->screenPos());
489  }
490 
491  return handled;
492 
493  }
494 
495 
496  void MosaicSceneWidget::enableRubberBand(bool enable) {
497  m_customRubberBandEnabled = enable;
498  }
499 
500 
501  MosaicSceneItem *MosaicSceneWidget::cubeToMosaic(Image *image) {
502  if (image == NULL) {
503  QString msg = tr("Can not find a NULL image in the mosaic");
504  throw IException(IException::Programmer, msg, _FILEINFO_);
505  }
506 
507  return cubeToMosaic(image->displayProperties());
508  }
509 
510 
511  bool MosaicSceneWidget::blockSelectionChange(bool block) {
512  bool wasBlocking = m_blockingSelectionChanged;
513 
514  m_blockingSelectionChanged = block;
515 
516  return wasBlocking;
517  }
518 
519 
520  QProgressBar *MosaicSceneWidget::getProgress() {
521  return m_progress;
522  }
523 
524 
525  PvlObject MosaicSceneWidget::toPvl() const {
526  PvlObject output("MosaicScene");
527 
528  if (m_projection) {
529  output += m_projection->Mapping();
530 
531  QBuffer dataBuffer;
532  dataBuffer.open(QIODevice::ReadWrite);
533  QDataStream transformStream(&dataBuffer);
534  transformStream << getView()->transform();
535  dataBuffer.seek(0);
536 
537  PvlObject mosaicScenePosition("SceneVisiblePosition");
538  mosaicScenePosition += PvlKeyword("ViewTransform",
539  QString(dataBuffer.data().toHex()));
540  PvlKeyword scrollPos("ScrollPosition");
541  scrollPos += toString(getView()->horizontalScrollBar()->value());
542  scrollPos += toString(getView()->verticalScrollBar()->value());
543  mosaicScenePosition += scrollPos;
544 
545  output += mosaicScenePosition;
546 
547  MosaicTool *tool;
548  foreach(tool, *m_tools) {
549  if (tool->projectPvlObjectName() != "") {
550  PvlObject toolObj = tool->toPvl();
551  toolObj.setName(tool->projectPvlObjectName());
552  output += toolObj;
553  }
554  }
555 
556  PvlObject zOrders("ZOrdering");
557  foreach(MosaicSceneItem * mosaicSceneItem, *m_mosaicSceneItems) {
558  PvlKeyword zValue("ZValue");
559  zValue += mosaicSceneItem->image()->id();
560  zValue += toString(mosaicSceneItem->zValue());
561  zOrders += zValue;
562  }
563 
564  output += zOrders;
565  }
566  else {
567  throw IException(IException::User,
568  "Cannot save a scene without a projection to a project file",
569  _FILEINFO_);
570  }
571 
572  return output;
573  }
574 
575 
581  void MosaicSceneWidget::fromPvl(const PvlObject &project) {
582  setProjection(project.findGroup("Mapping"));
583  recalcSceneRect();
584 
585  MosaicTool *tool;
586  foreach(tool, *m_tools) {
587  if (tool->projectPvlObjectName() != "") {
588  if (project.hasObject(tool->projectPvlObjectName())) {
589  const PvlObject &toolSettings(
590  project.findObject(tool->projectPvlObjectName()));
591  tool->fromPvl(toolSettings);
592  }
593  }
594 
595  if (project.hasObject("ZOrdering")) {
596  const PvlObject &zOrders = project.findObject("ZOrdering");
597 
598  delete m_projectImageZOrders;
599  m_projectImageZOrders = NULL;
600  m_projectImageZOrders = new QHash<QString, double>;
601 
602  for (int zOrderIndex = 0;
603  zOrderIndex < zOrders.keywords();
604  zOrderIndex ++) {
605  const PvlKeyword &zOrder = zOrders[zOrderIndex];
606 
607  (*m_projectImageZOrders)[zOrder[0]] = toDouble(zOrder[1]);
608  }
609  }
610 
611  if (project.hasObject("SceneVisiblePosition")) {
612  const PvlObject &positionInfo =
613  project.findObject("SceneVisiblePosition");
614 
615  delete m_projectViewTransform;
616  m_projectViewTransform = new PvlObject(positionInfo);
617  }
618  }
619  }
620 
621 
622  void MosaicSceneWidget::load(XmlStackedHandlerReader *xmlReader) {
623  xmlReader->pushContentHandler(new XmlHandler(this));
624  }
625 
626 
627  void MosaicSceneWidget::save(QXmlStreamWriter &stream, Project *, FileName ) const {
628  if (m_projection) {
629  stream.writeStartElement("mosaicScene");
630 
631  stream.writeStartElement("projection");
632  PvlGroup mapping = m_projection->Mapping();
633  std::stringstream strStream;
634  strStream << mapping;
635  stream.writeCharacters(strStream.str().c_str());
636  stream.writeEndElement();
637 
638  stream.writeStartElement("images");
639  foreach(MosaicSceneItem * mosaicSceneItem, *m_mosaicSceneItems) {
640  stream.writeStartElement("image");
641  stream.writeAttribute("id", mosaicSceneItem->image()->id());
642  stream.writeAttribute("zValue", toString(mosaicSceneItem->zValue()));
643  stream.writeEndElement();
644  }
645  stream.writeEndElement();
646 
647  stream.writeStartElement("viewTransform");
648  stream.writeAttribute("scrollBarXValue", toString(getView()->horizontalScrollBar()->value()));
649  stream.writeAttribute("scrollBarYValue", toString(getView()->verticalScrollBar()->value()));
650  QBuffer dataBuffer;
651  dataBuffer.open(QIODevice::ReadWrite);
652  QDataStream transformStream(&dataBuffer);
653  transformStream << getView()->transform();
654  dataBuffer.seek(0);
655  stream.writeCharacters(dataBuffer.data().toHex());
656  stream.writeEndElement();
657 
658  foreach(MosaicTool *tool, *m_tools) {
659  QString projectPvlObjectName = tool->projectPvlObjectName();
660  if (projectPvlObjectName != "") {
661  PvlObject toolObj = tool->toPvl();
662  toolObj.setName(projectPvlObjectName);
663 
664  stream.writeStartElement("toolData");
665  stream.writeAttribute("objectName", projectPvlObjectName);
666  std::stringstream strStream;
667  strStream << toolObj;
668  stream.writeCharacters(strStream.str().c_str());
669  stream.writeEndElement();
670  }
671  }
672 
673  stream.writeEndElement();
674  }
675  }
676 
677 
678  QRectF MosaicSceneWidget::cubesBoundingRect() const {
679  QRectF boundingRect;
680 
681  MosaicSceneItem * mosaicItem;
682  foreach(mosaicItem, *m_mosaicSceneItems) {
683  if (boundingRect.isEmpty())
684  boundingRect = mosaicItem->boundingRect();
685  else
686  boundingRect = boundingRect.united(mosaicItem->boundingRect());
687  }
688 
689  if (m_outlineRect)
690  boundingRect = boundingRect.united(m_outlineRect->boundingRect());
691 
692  return boundingRect;
693  }
694 
695 
696  MosaicSceneItem *MosaicSceneWidget::cubeToMosaic(DisplayProperties *props) {
697  if (props == NULL) {
698  QString msg = tr("Can not find a NULL Display Properties in the mosaic");
699  throw IException(IException::Programmer, msg, _FILEINFO_);
700  }
701 
702  return m_displayPropsToMosaicSceneItemMap[props];
703  }
704 
705 
706  QStringList MosaicSceneWidget::cubeFileNames() {
707  QStringList cubes;
708 
709  MosaicSceneItem *mosaicSceneItem;
710  foreach(mosaicSceneItem, *m_mosaicSceneItems) {
711  if (mosaicSceneItem->image())
712  cubes.append(mosaicSceneItem->image()->fileName());
713  }
714 
715  return cubes;
716  }
717 
718 
719  Directory *MosaicSceneWidget::directory() const {
720  return m_directory;
721  }
722 
723 
724  ImageList MosaicSceneWidget::images() {
725  ImageList images;
726 
727  MosaicSceneItem *mosaicSceneItem;
728  foreach(mosaicSceneItem, *m_mosaicSceneItems) {
729  if (mosaicSceneItem->image())
730  images.append(mosaicSceneItem->image());
731  }
732 
733  return images;
734  }
735 
736 
743 
744  QList<QGraphicsItem *> selectedGraphicsItems = getScene()->selectedItems();
745  QList<MosaicSceneItem *> selectedImageItems;
747 
748  foreach (QGraphicsItem *graphicsItem, selectedGraphicsItems) {
749  MosaicSceneItem *sceneImageItem = dynamic_cast<MosaicSceneItem *>(graphicsItem);
750 
751  if (!sceneImageItem) {
752  sceneImageItem = dynamic_cast<MosaicSceneItem *>(graphicsItem->parentItem());
753  }
754 
755  if (sceneImageItem && sceneImageItem->image()) {
756  selectedImageItems.append(sceneImageItem);
757  selectedImages.append(sceneImageItem->image());
758  }
759  }
760  return selectedImages;
761 
762 
763 
764 
765 
766 // ImageList images;
767 // //qDebug()<<"MosaicSceneWidget::selectedImages TotalItems = "<<m_mosaicSceneItems->size();
768 // MosaicSceneItem *mosaicItem;
769 // foreach(mosaicItem, *m_mosaicSceneItems) {
770 // if (mosaicItem->isSelected()) {
771 // images.append(mosaicItem->image());
772 // }
773 // }
774 //
775 // return images;
776  }
777 
778 
779  QList<QAction *> MosaicSceneWidget::getExportActions() {
780  QList<QAction *> exportActs;
781 
782  QAction *exportView = new QAction(this);
783  exportView->setText("&Export View...");
784  connect(exportView, SIGNAL(triggered()), this, SLOT(exportView()));
785 
786  QAction *saveList = new QAction(this);
787  saveList->setText("Save Entire Cube List (ordered by &view)...");
788  connect(saveList, SIGNAL(triggered()), this, SLOT(saveList()));
789 
790  exportActs.append(exportView);
791  exportActs.append(saveList);
792 
793  return exportActs;
794  }
795 
796 
797  QList<QAction *> MosaicSceneWidget::getViewActions() {
798  QList<QAction *> viewActs;
799 
800  foreach(MosaicTool *tool, *m_tools) {
801  QList<QAction *> toolViewActs = tool->getViewActions();
802  viewActs.append(toolViewActs);
803  }
804 
805  return viewActs;
806  }
807 
808 
813  QList<QAction *> results;
814  bool allImagesInView = !images->isEmpty();
815 
816  foreach (Image *image, *images) {
817  allImagesInView = allImagesInView && (cubeToMosaic(image) != NULL);
818  }
819 
820  if (allImagesInView) {
821  MoveToTopSceneWorkOrder *moveToTopAct =
822  new MoveToTopSceneWorkOrder(this, m_directory->project());
823  moveToTopAct->setData(images);
824  results.append(moveToTopAct);
825 
826  MoveUpOneSceneWorkOrder *moveUpOneAct =
827  new MoveUpOneSceneWorkOrder(this, m_directory->project());
828  moveUpOneAct->setData(images);
829  results.append(moveUpOneAct);
830 
831  MoveToBottomSceneWorkOrder *moveToBottomAct =
832  new MoveToBottomSceneWorkOrder(this, m_directory->project());
833  moveToBottomAct->setData(images);
834  results.append(moveToBottomAct);
835 
836  MoveDownOneSceneWorkOrder *moveDownOneAct =
837  new MoveDownOneSceneWorkOrder(this, m_directory->project());
838  moveDownOneAct->setData(images);
839  results.append(moveDownOneAct);
840 
841  results.append(NULL);
842 
843  QAction *zoomFitAct = new QAction(tr("Zoom Fit"), this);
844  zoomFitAct->setData(qVariantFromValue(images));
845  connect(zoomFitAct, SIGNAL(triggered()), this, SLOT(fitInView()));
846  results.append(zoomFitAct);
847  }
848  return results;
849  }
850 
851 
852  bool MosaicSceneWidget::isControlNetToolActive() {
853 
854  foreach(MosaicTool *tool, *m_tools) {
855  MosaicControlNetTool *cnTool = dynamic_cast<MosaicControlNetTool *>(tool);
856  if (cnTool) {
857  if (cnTool->isActive()) return true;
858  }
859  }
860  return false;
861  }
862 
863 
864  QWidget * MosaicSceneWidget::getControlNetHelp(QWidget *cnetToolContainer) {
865  QScrollArea *cnetHelpWidgetScrollArea = new QScrollArea;
866 
867  QWidget *cnetHelpWidget = new QWidget;
868 
869  QVBoxLayout *cnetHelpLayout = new QVBoxLayout;
870  cnetHelpWidget->setLayout(cnetHelpLayout);
871 
872  QLabel *title = new QLabel("<h2>Control Networks</h2>");
873  cnetHelpLayout->addWidget(title);
874 
875  QPixmap previewPixmap;
876 
877  if (cnetToolContainer) {
878  previewPixmap = cnetToolContainer->grab().scaled(
879  QSize(500, 200), Qt::KeepAspectRatio, Qt::SmoothTransformation);
880  }
881  else {
882  ToolPad tmpToolPad("Example Tool Pad", NULL);
883  MosaicControlNetTool tmpTool(NULL);
884  tmpTool.addTo(&tmpToolPad);
885 
886  tmpToolPad.resize(QSize(32, 32));
887 
888  previewPixmap = tmpToolPad.grab();
889  }
890 
891  QLabel *previewWrapper = new QLabel;
892  previewWrapper->setPixmap(previewPixmap);
893  cnetHelpLayout->addWidget(previewWrapper);
894 
895  QLabel *overview = new QLabel("The mosaic scene can display control points "
896  "in addition to the usual cube footprints. This feature is currently "
897  "offered as one of the Mosaic Scene's tools. To open a network, click "
898  "on the control network tool. It will immediately prompt you for a "
899  "control network file if one is not open. Only control points for "
900  "which the latitude and longitude can be established will be "
901  "displayed. Other control points will be ignored by qmos.<br><br>"
902  "<b>Warning: Opening large control networks is slow.</b>"
903  "<h3>Control Network Tool Options</h3>"
904  "<ul>"
905  "<li>The control network tool opens control networks in two ways. "
906  "First, if you select the control network tool and no network is "
907  "open, then it will prompt you for one. Second, if there is an open "
908  "network, the buttons for the available options are displayed in the "
909  "active tool area.</li>"
910  "<li>The control network tool can toggle whether or not control "
911  "points are displayed on the screen using the 'Display' button. "
912  "Control points are always on top and colored based on their "
913  "ignored, locked and type values.</li>"
914  "<li>This tool can also change the color of your footprints based on "
915  "connectivity through control points. This is available through the "
916  "'Color Islands' button. When you press color islands, all of the "
917  "current cube coloring information is lost and re-done based on "
918  "how the control network connects the files. Each set of connected "
919  "cubes are colored differently; generally speaking, islands are not "
920  "a good thing to have in your control network.</li>"
921  "<li>This tool will color your footprints on a per-image basis if you "
922  "click color images, effectively reversing color islands.</li>"
923  "<li>The show movement option under 'Configure Movement Display' "
924  "only displays data when the control "
925  "network has adjusted values. This means that show movement only "
926  "works after you have done a jigsaw solution on the control network. "
927  "This displays arrows emanating from the apriori latitude/longitude "
928  "and pointing toward the adjusted latitude/longitude.</li>");
929  overview->setWordWrap(true);
930  cnetHelpLayout->addWidget(overview);
931 
932  cnetHelpWidgetScrollArea->setWidget(cnetHelpWidget);
933 
934  return cnetHelpWidgetScrollArea;
935  }
936 
937 
938  QWidget * MosaicSceneWidget::getGridHelp(QWidget *gridToolContainer) {
939  QScrollArea *gridHelpWidgetScrollArea = new QScrollArea;
940 
941  QWidget *gridHelpWidget = new QWidget;
942 
943  QVBoxLayout *gridHelpLayout = new QVBoxLayout;
944  gridHelpWidget->setLayout(gridHelpLayout);
945 
946  QLabel *title = new QLabel("<h2>Map Grid Tool</h2>");
947  gridHelpLayout->addWidget(title);
948 
949  QPixmap previewPixmap;
950 
951  if (gridToolContainer) {
952  previewPixmap = gridToolContainer->grab().scaled(
953  QSize(500, 200), Qt::KeepAspectRatio, Qt::SmoothTransformation);
954  }
955  else {
956  ToolPad tmpToolPad("Example Tool Pad", NULL);
957  MosaicGridTool tmpTool(NULL);
958  tmpTool.addTo(&tmpToolPad);
959 
960  tmpToolPad.resize(QSize(32, 32));
961 
962  previewPixmap = tmpToolPad.grab();
963  }
964 
965  QLabel *previewWrapper = new QLabel;
966  previewWrapper->setPixmap(previewPixmap);
967  gridHelpLayout->addWidget(previewWrapper);
968 
969  QLabel *overview = new QLabel("Superimpose a map grid over the area of "
970  "displayed footprints in the 'mosaic scene.'"
971  "<h2>Overview</h2>"
972  "<ul>"
973  "<li>The Map Grid Tool is activated by selecting the 'cross-hatch' "
974  "icon or typing 'g' at the keyboard."
975  "</li>"
976  "<li>The parameter options are displayed below the menubar. "
977  "Clicking the 'Grid Options' button will open the dialog. Checking "
978  "'Auto Grid' will draw a grid based on the open cubes. Hitting "
979  "'Show Grid' will display or hide the grid."
980  "</li>"
981  "<li>The map grid is defined by the loaded Map File (just as the "
982  "footprints and image data are), the opened cubes, or the grid "
983  "tool parameters."
984  "</li>"
985  "<li>If a Map File has not been selected, the default "
986  "Equirectangular projection will be used. The resulting grid "
987  "lines in the default 'Equi' map file will be drawn for the "
988  "full global range (latitude range = -90,90; longitude range = "
989  "0,360) at the default latitude and longitude increment values."
990  "</li>"
991  "<li>"
992  "If the grid lines are not immediately visible, try to "
993  "'zoom out' in the 'mosaic scene' window and modify the "
994  "Latitude and Longitude Increment parameters."
995  "</li>"
996  "</ul>"
997  "<strong>Options:</strong>"
998  "<ul>"
999  "<li>The 'Show Grid' option draws (checked) or clears (unchecked) the grid."
1000  "</li>"
1001  "<li>The 'Auto Grid' option draws a grid with extents and increments "
1002  "computed from the set of images that are opened. The values displayed in the dialog "
1003  "will reflect those used to draw the grid."
1004  "</li>"
1005  "<li>The expected units for each entry are displayed to the right of the "
1006  "dialog box."
1007  "</li>"
1008  "<li>The 'Extent Type' combo boxes allow you to pick the source of the "
1009  "grid extents from the map file, from the open cubes <default>, or manually "
1010  "entered."
1011  "</li>"
1012  "<li>The 'Auto Apply' checkbox, inside the 'Grid Options' dialog box, allows you to see "
1013  "real time updates in the grid when you change the parameters."
1014  "</li>"
1015  "<li> Depending on the projection, the grid may not behave as expected. For instance, "
1016  "with a polarstereographic projection, the pole will not be included in the 'Auto "
1017  "Grid' if it is not in the cube region. In this case the 'Manual' option for latitude "
1018  "extents allows you to force the grid to the pole."
1019  "</li>"
1020  "</ul>");
1021  overview->setWordWrap(true);
1022  gridHelpLayout->addWidget(overview);
1023 
1024  gridHelpWidgetScrollArea->setWidget(gridHelpWidget);
1025 
1026  return gridHelpWidgetScrollArea;
1027  }
1028 
1029 
1030  QWidget * MosaicSceneWidget::getLongHelp(QWidget *sceneContainer) {
1031  QScrollArea *longHelpWidgetScrollArea = new QScrollArea;
1032 
1033  QWidget *longHelpWidget = new QWidget;
1034 
1035  QVBoxLayout *longHelpLayout = new QVBoxLayout;
1036  longHelpWidget->setLayout(longHelpLayout);
1037 
1038  QLabel *title = new QLabel("<h2>Mosaic Scene</h2>");
1039  longHelpLayout->addWidget(title);
1040 
1041  if (sceneContainer) {
1042  QPixmap previewPixmap = sceneContainer->grab().scaled(
1043  QSize(500, 200), Qt::KeepAspectRatio, Qt::SmoothTransformation);
1044 
1045  QLabel *previewWrapper = new QLabel;
1046  previewWrapper->setPixmap(previewPixmap);
1047  longHelpLayout->addWidget(previewWrapper);
1048  }
1049 
1050  QLabel *overview = new QLabel("The mosaic scene displays cube footprints "
1051  "to show where files are on a target and how they overlap. "
1052  "The scene always represents projected image space and cannot show raw "
1053  "or unprojected images; images will be projected on the fly."
1054  "<h3>Tools</h3>"
1055  "<p>Interact with the mosaic scene in different ways using "
1056  "the tools. The tools are usually in a toolbar next to the scene. "
1057  "The tools define what is displayed and what happens when you "
1058  "click in the mosaic scene. The tools include</p>"
1059  "<ul><li>Select Tool</li>"
1060  "<li>Zoom Tool</li>"
1061  "<li>Pan Tool</li>"
1062  "<li>Control Network Tool</li>"
1063  "<li>Show Area Tool</li>"
1064  "<li>Find Tool</li>"
1065  "<li>Grid Tool</li></ul>"
1066  "<h3>Context Menus</h3>"
1067  "Right click on anything in the mosaic scene and a context menu will pop up showing "
1068  "a list of actions or information relevant to the item you clicked on. "
1069  "<p>Note: The context menu is not associated with any selection, only the item "
1070  "clicked on.</p>");
1071  overview->setWordWrap(true);
1072  longHelpLayout->addWidget(overview);
1073 
1074  longHelpWidgetScrollArea->setWidget(longHelpWidget);
1075 
1076  return longHelpWidgetScrollArea;
1077  }
1078 
1079 
1080  QWidget * MosaicSceneWidget::getMapHelp(QWidget *mapContainer) {
1081  QScrollArea *mapHelpWidgetScrollArea = new QScrollArea;
1082 
1083  QWidget *mapHelpWidget = new QWidget;
1084 
1085  QVBoxLayout *mapHelpLayout = new QVBoxLayout;
1086  mapHelpWidget->setLayout(mapHelpLayout);
1087 
1088  QLabel *title = new QLabel(tr("<h2>Map File</h2>"));
1089  mapHelpLayout->addWidget(title);
1090 
1091  if (mapContainer) {
1092  QPixmap previewPixmap = mapContainer->grab().scaled(
1093  QSize(500, 200), Qt::KeepAspectRatio, Qt::SmoothTransformation);
1094 
1095  QLabel *previewWrapper = new QLabel;
1096  previewWrapper->setPixmap(previewPixmap);
1097  mapHelpLayout->addWidget(previewWrapper);
1098  }
1099 
1100  QLabel *overviewMapIcon = new QLabel;
1101 
1102  overviewMapIcon->setPixmap(
1103  QIcon(FileName("$base/icons/ographic.png").expanded()).pixmap(32, 32));
1104  mapHelpLayout->addWidget(overviewMapIcon);
1105 
1106  QLabel *defaultMapFile = new QLabel(tr(
1107  "<h3>Default Map File</h3>"
1108  "The mosaic scene's projection is defined by a \"Map File\" that consists of keywords "
1109  "that describe the map layout to be used. If a cube or a list of cubes are "
1110  "loaded before a map file is selected, the default map file defines the "
1111  "equirectangular projection, planetocentric latitude, positive longitude east, 360 "
1112  "longitude domain, latitude range=90S-90N, longitude range=0-360E. The radius will "
1113  "default to the IAU standards (ellipsoid or sphere) for the specific planetary body "
1114  "defined for the \"TargetName\" in the labels of the image cube(s)."));
1115 
1116  defaultMapFile->setWordWrap(true);
1117  mapHelpLayout->addWidget(defaultMapFile);
1118 
1119  QLabel *userDefinedMapFileOverview = new QLabel(tr(
1120  "<h3>User Defined Map File</h3>"
1121  "You can load an existing \"Map File\" before loading images into %1 by selecting the "
1122  "\"View/Edit/Load Map File\" option. You will be greeted with a dialog box that will "
1123  "enable you to select an existing map file by clicking on \"Load Map File.\" Once "
1124  "the map file is selected, the contents is displayed in the dialog box where "
1125  "modifications can be made as well. If the modified map file is to be used later, "
1126  "save the map file by clicking on \"Save Map File\" button.")
1127  .arg(QCoreApplication::applicationName()));
1128 
1129  userDefinedMapFileOverview->setWordWrap(true);
1130  mapHelpLayout->addWidget(userDefinedMapFileOverview);
1131 
1132  QLabel *userDefinedMapFileQuickLoad = new QLabel(tr(
1133  "The \"Quick Load Map\" option (lightning icon) allows you to efficiently select a "
1134  "prepared \"Map File\" without an immediate need to view or edit the contents."));
1135 
1136  userDefinedMapFileQuickLoad->setWordWrap(true);
1137  mapHelpLayout->addWidget(userDefinedMapFileQuickLoad);
1138 
1139  QLabel *userDefinedMapFileAnyTime = new QLabel(tr(
1140  "At any point, you have access to the \"View/Edit\" functionality to modify or load a "
1141  "different map file."));
1142 
1143  userDefinedMapFileAnyTime->setWordWrap(true);
1144  mapHelpLayout->addWidget(userDefinedMapFileAnyTime);
1145 
1146  QString mapProjWorkshopUrl("https://isis.astrogeology.usgs.gov/fixit/projects/"
1147  "isis/wiki/Learning_About_Map_Projections");
1148  QLabel *preparingMapFile = new QLabel(tr(
1149  "<h3>Preparing a Map File</h3>"
1150  "Please refer to Isis applications such as 'maptemplate' or 'mosrange' for more details "
1151  "on creating a custom map file that defines the desired projection, latitude "
1152  "system, and longitude direction and domain. This program will use the latitude range "
1153  "and longitude range if they exist in the loaded file. A choice of map templates that can be used as "
1154  "a starting point for supported map projections can be found in $base/templates/maps (refer "
1155  "to maptemplate or mosrange for more details and information on the required parameters "
1156  "for a projection). Note that through the file name selection box, $base will need "
1157  "to be replaced with the specific Isis3 system path. The website: "
1158  "<a href='%1'>%1</a> also provides useful information about map projections.")
1159  .arg(mapProjWorkshopUrl));
1160 
1161  preparingMapFile->setOpenExternalLinks(true);
1162  preparingMapFile->setWordWrap(true);
1163  mapHelpLayout->addWidget(preparingMapFile);
1164 
1165  QLabel *mapFileDisplayResults = new QLabel(tr(
1166  "<h3>Display Results with the Map File</h3>"
1167  "The footprints and image data that are displayed in the mosaic scene are defined by the "
1168  "loaded \"Map File\" regardless of whether the opened cubes are Level1 (raw "
1169  "camera space) or Level2 (map projected). The associated footprint polygons for "
1170  "Level2 cubes will be re-mapped as needed based on the loaded map file."));
1171 
1172  mapFileDisplayResults->setWordWrap(true);
1173  mapHelpLayout->addWidget(mapFileDisplayResults);
1174 
1175  QLabel *editingMapFileOverview = new QLabel(tr(
1176  "<h3>Editing a Map File</h3>"
1177  "Editing a map file is possible through the dialog box displayed by clicking on the "
1178  "'View/Edit/Load Map File' icon/button. The edits are "
1179  "applied to the current session and will be included with a 'Saved Project' (refer to "
1180  "the help under File-Save Project or Save Project as)."));
1181 
1182  editingMapFileOverview->setWordWrap(true);
1183  mapHelpLayout->addWidget(editingMapFileOverview);
1184 
1185  QLabel *saveMapFileToDiskBullet = new QLabel(tr(
1186  "<ul>"
1187  "<li>"
1188  "To save or write the changes to a map file on disk, choose 'Save Map File' button. "
1189  "Map files can be saved to an existing map file (overwrites) or to a new file. This "
1190  "program always saves <strong>exactly</strong> what you see, the text, in the dialog "
1191  "box."
1192  "</li>"
1193  "</ul>"));
1194 
1195  saveMapFileToDiskBullet->setWordWrap(true);
1196  mapHelpLayout->addWidget(saveMapFileToDiskBullet);
1197 
1198  QLabel *mapFileValidityBullet = new QLabel(tr(
1199  "<ul>"
1200  "<li>"
1201  "As you modify the contents of a loaded map file in the dialog box, the entry is "
1202  "verified as you type with a bold black indicator message displaying whether the "
1203  "text is valid or is not valid. If you want to see the actual error messages, "
1204  "select the 'Show Errors' box and the errors will be displayed in red font "
1205  "along with the black bolded message. The errors will update "
1206  "as you type."
1207  "</li>"
1208  "</ul>"));
1209 
1210  mapFileValidityBullet->setWordWrap(true);
1211  mapHelpLayout->addWidget(mapFileValidityBullet);
1212 
1213  QLabel *mapFileCommentsBullet = new QLabel(tr(
1214  "<ul>"
1215  "<li>"
1216  "Map files may contain 'commented-out' lines (text that starts with \"#\" at "
1217  "the beginning of the line). These are referred to as \"unnecessary\""
1218  "or \"unknown\" keywords, they are simply ignored. If these lines are to be saved to "
1219  "the output map file on disk, click 'Save Map File' BEFORE clicking 'Ok' or 'Apply.' "
1220  "The comments are removed from the dialog box when you hit 'Ok' or 'Apply,' if they "
1221  "are just above \"End_Group\" or follow \"End_Group\" or \"End\".<br/><br/>"
1222 
1223  "If you want these comments retained, make sure they are immediately above a valid "
1224  "keyword inside of \"Group = Mapping.\" Note that any lines (commented or not) will "
1225  "not be saved if they are placed outside of \"Group = Mapping\" and \"End_Group\"."
1226  "</li>"
1227  "</ul>"));
1228 
1229  mapFileCommentsBullet->setWordWrap(true);
1230  mapHelpLayout->addWidget(mapFileCommentsBullet);
1231 
1232  mapHelpWidgetScrollArea->setWidget(mapHelpWidget);
1233 
1234  return mapHelpWidgetScrollArea;
1235  }
1236 
1237 
1238  QWidget * MosaicSceneWidget::getPreviewHelp(QWidget *worldViewContainer) {
1239  QScrollArea *previewHelpWidgetScrollArea = new QScrollArea;
1240 
1241  QWidget *previewHelpWidget = new QWidget;
1242 
1243  QVBoxLayout *previewHelpLayout = new QVBoxLayout;
1244  previewHelpWidget->setLayout(previewHelpLayout);
1245 
1246  QLabel *title = new QLabel("<h2>Mosaic World View</h2>");
1247  previewHelpLayout->addWidget(title);
1248 
1249  if (worldViewContainer) {
1250  QPixmap previewPixmap = worldViewContainer->grab().scaled(
1251  QSize(500, 200), Qt::KeepAspectRatio, Qt::SmoothTransformation);
1252 
1253  QLabel *previewWrapper = new QLabel;
1254  previewWrapper->setPixmap(previewPixmap);
1255  previewHelpLayout->addWidget(previewWrapper);
1256  }
1257 
1258  QLabel *overview = new QLabel("The mosaic world view displays cube "
1259  "footprints to show you where your files are on a target and their "
1260  "general arrangement. The world view does not have tools like "
1261  "mosaic scenes, but otherwise they are very similar.");
1262  overview->setWordWrap(true);
1263  previewHelpLayout->addWidget(overview);
1264 
1265  previewHelpWidgetScrollArea->setWidget(previewHelpWidget);
1266 
1267  return previewHelpWidgetScrollArea;
1268  }
1269 
1270 
1271  MosaicSceneItem *MosaicSceneWidget::addImage(Image *image) {
1272  if (m_projection == NULL) {
1273  setProjection(createInitialProjection(image), *image->cube()->label());
1274  }
1275 
1276  MosaicSceneItem *mosItem = NULL;
1277 
1278  // Verify we don't have this cube already... if we do, ignore the add request
1279  if (!cubeToMosaic(image)) {
1280  mosItem = new MosaicSceneItem(image, this);
1281 
1282  connect(mosItem, SIGNAL(changed(const QList<QRectF> &)),
1283  m_graphicsView, SLOT(updateScene(const QList<QRectF> &)));
1284  connect(mosItem, SIGNAL(mosaicCubeClosed(Image *)),
1285  this, SIGNAL(mosCubeClosed(Image *)));
1286 
1287  // We want everything to have a unique Z value so we can manage the z order
1288  // well.
1289  if (m_projectImageZOrders && m_projectImageZOrders->contains(image->id())) {
1290  double zOrder = m_projectImageZOrders->value(image->id());
1291  m_projectImageZOrders->remove(image->id());
1292 
1293  foreach (MosaicSceneItem *mosaicItem, *m_mosaicSceneItems) {
1294  if (mosaicItem->zValue() == zOrder) {
1295  mosaicItem->setZValue(maximumZ() + 1);
1296  m_currentMaximumFootprintZ = maximumZ() + 1;
1297  }
1298  }
1299 
1300  m_currentMaximumFootprintZ = qMax(zOrder, maximumZ());
1301  mosItem->setZValue(zOrder);
1302  }
1303  else {
1304  mosItem->setZValue(maximumZ() + 1);
1305  m_currentMaximumFootprintZ = maximumZ() + 1;
1306  }
1307 
1308  getScene()->addItem(mosItem);
1309  m_mosaicSceneItems->append(mosItem);
1310  m_displayPropsToMosaicSceneItemMap[image->displayProperties()] = mosItem;
1311 
1312  connect(mosItem, SIGNAL(destroyed(QObject *)),
1313  this, SLOT(removeMosItem(QObject *)));
1314 
1315  ImageDisplayProperties *prop = image->displayProperties();
1316  connect(prop, SIGNAL(moveDownOne()),
1317  this, SLOT(moveDownOne()));
1318  connect(prop, SIGNAL(moveToBottom()),
1319  this, SLOT(moveToBottom()));
1320  connect(prop, SIGNAL(moveUpOne()),
1321  this, SLOT(moveUpOne()));
1322  connect(prop, SIGNAL(moveToTop()),
1323  this, SLOT(moveToTop()));
1324  connect(prop, SIGNAL(zoomFit()),
1325  this, SLOT(fitInView()));
1326  }
1327 
1328  return mosItem;
1329  }
1330 
1331 
1332  double MosaicSceneWidget::maximumZ() {
1333  return m_currentMaximumFootprintZ;
1334  }
1335 
1336 
1337  double MosaicSceneWidget::minimumZ() {
1338  return m_currentMinimumFootprintZ;
1339  }
1340 
1341  void MosaicSceneWidget::recalcSceneRect() {
1342  if (m_projection) {
1343  double minX, minY, maxX, maxY;
1344  m_projection->XYRange(minX, maxX, minY, maxY);
1345 
1346  QRectF projRect(minX, -maxY, maxX - minX, maxY - minY);
1347  QRectF cubesBounding = cubesBoundingRect();
1348 
1349  QRectF bounding = projRect.united(cubesBounding);
1350 
1351  if (m_outlineRect && m_outlineRect->isVisible())
1352  bounding = bounding.united(m_outlineRect->boundingRect());
1353 
1354  getView()->setSceneRect(bounding);
1355  }
1356  }
1357 
1358  void MosaicSceneWidget::addImages(ImageList images) {
1359  if (m_userToolControl)
1360  m_progress->setText("Loading primary scene");
1361  else
1362  m_progress->setText("Loading secondary scene");
1363 
1364  m_progress->setRange(0, images.size() - 1);
1365  m_progress->setValue(0);
1366  m_progress->setVisible(true);
1367 
1368  foreach(Image *image, images) {
1369  try {
1370  addImage(image);
1371  }
1372  catch (IException &e) {
1373  e.print();
1374  }
1375 
1376  m_progress->setValue(m_progress->value() + 1);
1377  }
1378 
1379  recalcSceneRect();
1380 
1381  if (m_projectViewTransform) {
1382  PvlObject &positionInfo = *m_projectViewTransform;
1383  QByteArray hexValues(positionInfo["ViewTransform"][0].toLatin1());
1384  QDataStream transformStream(QByteArray::fromHex(hexValues));
1385 
1386  QTransform viewTransform;
1387  transformStream >> viewTransform;
1388  getView()->setTransform(viewTransform);
1389 
1390  QPoint projectScrollPos(toInt(positionInfo["ScrollPosition"][0]),
1391  toInt(positionInfo["ScrollPosition"][1]));
1392 
1393  getView()->horizontalScrollBar()->setValue(projectScrollPos.x());
1394  getView()->verticalScrollBar()->setValue(projectScrollPos.y());
1395  }
1396  else {
1397  refit();
1398  }
1399 
1400  if (!m_projectImageZOrders || m_projectImageZOrders->isEmpty()) {
1401  delete m_projectViewTransform;
1402  m_projectViewTransform = NULL;
1403  }
1404 
1405  m_progress->setVisible(false);
1406  emit cubesChanged();
1407  }
1408 
1409 
1410  void MosaicSceneWidget::removeImages(ImageList images) {
1411  // TODO: 2016-08-02 TLS Should this be done similarly to addImages. Re-do Image list
1412  // then redo scene?
1413  foreach(Image *image, images) {
1414  try {
1415  MosaicSceneItem *item = cubeToMosaic(image);
1416  item->deleteLater();
1417  removeMosItem(item);
1418  }
1419  catch (IException &e) {
1420  e.print();
1421  }
1422  }
1423  }
1424 
1425 
1430  QString output =
1431  QFileDialog::getSaveFileName((QWidget *)parent(),
1432  "Choose output file",
1433  QDir::currentPath() + "/untitled.png",
1434  QString("Images (*.png *.jpg *.tif)"));
1435  if (output.isEmpty()) return;
1436 
1437  // Use png format is the user did not add a suffix to their output filename.
1438  if (QFileInfo(output).suffix().isEmpty()) {
1439  output = output + ".png";
1440  }
1441 
1442  QString format = QFileInfo(output).suffix();
1443  QPixmap pm = getScene()->views().last()->grab();
1444 
1445  std::string formatString = format.toStdString();
1446  if (!pm.save(output, formatString.c_str())) {
1447  QMessageBox::information(this, "Error",
1448  "Unable to save [" + output + "]");
1449  }
1450  }
1451 
1452 
1453  void MosaicSceneWidget::saveList() {
1454  QString output =
1455  QFileDialog::getSaveFileName((QWidget *)parent(),
1456  "Choose output file",
1457  QDir::currentPath() + "/files.lis",
1458  QString("List File (*.lis);;Text File (*.txt);;All Files (*.*)"));
1459  if (output.isEmpty()) return;
1460 
1461  TextFile file(output, "overwrite");
1462 
1463  QList<MosaicSceneItem *> sorted = *m_mosaicSceneItems;
1464  qSort(sorted.begin(), sorted.end(), zOrderGreaterThan);
1465 
1466  MosaicSceneItem *sceneItem;
1467  foreach(sceneItem, sorted) {
1468  file.PutLine( sceneItem->image()->fileName() );
1469  }
1470  }
1471 
1472 
1473  void MosaicSceneWidget::removeMosItem(QObject *mosItem) {
1474  MosaicSceneItem *castedMosItem = (MosaicSceneItem *) mosItem;
1475  m_mosaicSceneItems->removeAll(castedMosItem);
1476  m_displayPropsToMosaicSceneItemMap.remove(
1477  m_displayPropsToMosaicSceneItemMap.key(castedMosItem));
1478  recalcSceneRect();
1479  emit cubesChanged();
1480  }
1481 
1482 
1488  QRectF sceneRect = cubesBoundingRect();
1489 
1490  if (sceneRect.isEmpty())
1491  return;
1492 
1493  double xPadding = sceneRect.width() * 0.10;
1494  double yPadding = sceneRect.height() * 0.10;
1495 
1496  sceneRect.adjust(-xPadding, -yPadding, xPadding, yPadding);
1497  getView()->fitInView(sceneRect, Qt::KeepAspectRatio);
1498  }
1499 
1500 
1501  void MosaicSceneWidget::setCubesSelectable(bool selectable) {
1502  if (m_cubesSelectable != selectable) {
1503  m_cubesSelectable = selectable;
1504 
1505  MosaicSceneItem *mosaicSceneItem;
1506  foreach(mosaicSceneItem, *m_mosaicSceneItems) {
1507  mosaicSceneItem->scenePropertiesChanged();
1508  }
1509  }
1510  }
1511 
1512 
1518  ProjectionConfigDialog configDialog(this);
1519  configDialog.exec();
1520  }
1521 
1522 
1523  void MosaicSceneWidget::quickConfigProjectionParameters() {
1524  ProjectionConfigDialog configDialog(this);
1525  configDialog.setQuickConfig(true);
1526  configDialog.exec();
1527  }
1528 
1529 
1530  void MosaicSceneWidget::sendVisibleRectChanged() {
1531  QPointF topLeft = getView()->mapToScene(0, 0);
1532  QPointF bottomRight = getView()->mapToScene(
1533  (int)getView()->width(),
1534  (int)getView()->height());
1535 
1536  QRectF visibleRect(topLeft, bottomRight);
1537  emit visibleRectChanged(visibleRect);
1538  }
1539 
1540 
1541  bool MosaicSceneWidget::eventFilter(QObject *obj, QEvent *event) {
1542  bool stopProcessingEvent = true;
1543 
1544  switch(event->type()) {
1545  case QMouseEvent::GraphicsSceneMousePress: {
1546  if (m_customRubberBandEnabled) {
1547  // Intiate the rubber banding!
1548  if (!m_customRubberBand) {
1549  m_customRubberBand = new QRubberBand(QRubberBand::Rectangle,
1550  getView());
1551  }
1552 
1553  if (!m_rubberBandOrigin) {
1554  m_rubberBandOrigin = new QPoint;
1555  }
1556 
1557  *m_rubberBandOrigin = getView()->mapFromScene(
1558  ((QGraphicsSceneMouseEvent *)event)->scenePos());
1559  m_customRubberBand->setGeometry(QRect(*m_rubberBandOrigin, QSize()));
1560  m_customRubberBand->show();
1561  }
1562 
1563  emit mouseButtonPress(
1564  ((QGraphicsSceneMouseEvent *)event)->scenePos(),
1565  ((QGraphicsSceneMouseEvent *)event)->button());
1566  stopProcessingEvent = false;
1567  break;
1568  }
1569 
1570  case QMouseEvent::GraphicsSceneMouseRelease: {
1571  bool signalEmitted = false;
1572  if (m_customRubberBandEnabled && m_rubberBandOrigin &&
1573  m_customRubberBand) {
1574  if (m_customRubberBand->geometry().width() +
1575  m_customRubberBand->geometry().height() > 10) {
1576  emit rubberBandComplete(
1577  getView()->mapToScene(
1578  m_customRubberBand->geometry()).boundingRect(),
1579  ((QGraphicsSceneMouseEvent *)event)->button());
1580  signalEmitted = true;
1581  }
1582 
1583  delete m_rubberBandOrigin;
1584  m_rubberBandOrigin = NULL;
1585 
1586  delete m_customRubberBand;
1587  m_customRubberBand = NULL;
1588  }
1589 
1590  if (!signalEmitted) {
1591  stopProcessingEvent = false;
1592  emit mouseButtonRelease(
1593  ((QGraphicsSceneMouseEvent *)event)->scenePos(),
1594  ((QGraphicsSceneMouseEvent *)event)->button());
1595  }
1596  break;
1597  }
1598 
1599  case QMouseEvent::GraphicsSceneMouseDoubleClick:
1600  emit mouseDoubleClick(
1601  ((QGraphicsSceneMouseEvent *)event)->scenePos());
1602  stopProcessingEvent = false;
1603  break;
1604 
1605  case QMouseEvent::GraphicsSceneMouseMove:
1606  if (m_customRubberBandEnabled && m_rubberBandOrigin &&
1607  m_customRubberBand) {
1608  QPointF scenePos = ((QGraphicsSceneMouseEvent *)event)->scenePos();
1609  QPoint screenPos = getView()->mapFromScene(scenePos);
1610 
1611  QRect rubberBandRect =
1612  QRect(*m_rubberBandOrigin, screenPos).normalized();
1613 
1614  m_customRubberBand->setGeometry(rubberBandRect);
1615  }
1616  else {
1617  stopProcessingEvent = false;
1618  }
1619 
1620  emit mouseMove(
1621  ((QGraphicsSceneMouseEvent *)event)->scenePos());
1622  break;
1623 
1624  case QEvent::GraphicsSceneWheel:
1625  emit mouseWheel(
1626  ((QGraphicsSceneWheelEvent *)event)->scenePos(),
1627  ((QGraphicsSceneWheelEvent *)event)->delta());
1628  event->accept();
1629  stopProcessingEvent = true;
1630  break;
1631 
1632  case QMouseEvent::Enter:
1633  emit mouseEnter();
1634  stopProcessingEvent = false;
1635  break;
1636 
1637  case QMouseEvent::Leave:
1638  emit mouseLeave();
1639  stopProcessingEvent = false;
1640  break;
1641 
1642  case QEvent::GraphicsSceneHelp: {
1643  setToolTip("");
1644  bool toolTipFound = false;
1645 
1646  QGraphicsItem *sceneItem;
1647  foreach(sceneItem, getScene()->items()) {
1648  if (!toolTipFound) {
1649  if (sceneItem->contains(
1650  ((QGraphicsSceneHelpEvent*)event)->scenePos()) &&
1651  sceneItem->toolTip().size() > 0) {
1652  setToolTip(sceneItem->toolTip());
1653  toolTipFound = true;
1654  }
1655  }
1656  }
1657 
1658  if (toolTipFound) {
1659  stopProcessingEvent = true;
1660  QToolTip::showText(
1661  ((QGraphicsSceneHelpEvent*)event)->screenPos(),
1662  toolTip());
1663  }
1664  break;
1665  }
1666 
1667  default:
1668  stopProcessingEvent = false;
1669  break;
1670  }
1671 
1672  return stopProcessingEvent;
1673  }
1674 
1675 
1683  if (m_mosaicSceneItems->size() == 0)
1684  return;
1685 
1686  if (m_userToolControl)
1687  m_progress->setText("Reprojecting primary scene");
1688  else
1689  m_progress->setText("Reprojecting secondary scene");
1690 
1691  // This gives some pretty graphics as thing work
1692  int reprojectsPerUpdate = qMax(1, m_mosaicSceneItems->size() / 20);
1693 
1694  m_progress->setRange(0,
1695  (m_mosaicSceneItems->size() - 1) / reprojectsPerUpdate + 1);
1696  m_progress->setValue(0);
1697  m_progress->setVisible(true);
1698 
1699  MosaicSceneItem *mosaicSceneItem;
1700 
1701  int progressCountdown = reprojectsPerUpdate;
1702  foreach(mosaicSceneItem, *m_mosaicSceneItems) {
1703  try {
1704  mosaicSceneItem->reproject();
1705  }
1706  catch (IException &e) {
1707  QString msg = "The file [";
1708 
1709  if (mosaicSceneItem->image())
1710  msg += mosaicSceneItem->image()->displayProperties()->displayName();
1711 
1712  msg += "] is being removed due to not being able to project onto the scene";
1713 
1715  tmp.print();
1716  mosaicSceneItem->image()->deleteLater();
1717  }
1718 
1719  progressCountdown --;
1720  if (progressCountdown == 0) {
1721  m_progress->setValue(m_progress->value() + 1);
1722  progressCountdown = reprojectsPerUpdate;
1723  refit();
1724  }
1725  }
1726 
1727  m_progress->setValue(m_progress->maximum());
1728 
1729  recalcSceneRect();
1730  refit();
1731  m_progress->setVisible(false);
1732  }
1733 
1734 
1735  void MosaicSceneWidget::moveDownOne() {
1736  DisplayProperties *props = qobject_cast<DisplayProperties *>(sender());
1737 
1738  if (props) {
1739  moveDownOne(cubeToMosaic(props));
1740  }
1741  }
1742 
1743 
1746  double MosaicSceneWidget::moveDownOne(MosaicSceneItem *item) {
1747  MosaicSceneItem *nextDown = getNextItem(item, false);
1748  double originalZ = item->zValue();
1749 
1750  if (nextDown) {
1751  double newZValue = nextDown->zValue() - 1;
1752  moveZ(item, newZValue, true);
1753  }
1754 
1755  return originalZ;
1756  }
1757 
1758 
1761  double MosaicSceneWidget::moveDownOne(Image *image) {
1762  return moveDownOne(cubeToMosaic(image));
1763  }
1764 
1765 
1768  QList<double> MosaicSceneWidget::moveDownOne(ImageList *images) {
1769  QList<double> results;
1770 
1771  foreach (Image *image, *images) {
1772  results.append(moveDownOne(image));
1773  }
1774 
1775  return results;
1776  }
1777 
1778 
1779  void MosaicSceneWidget::moveToBottom() {
1780  DisplayProperties *props = qobject_cast<DisplayProperties *>(sender());
1781  if (props) {
1782  moveToBottom(cubeToMosaic(props));
1783  }
1784  }
1785 
1786 
1791  double MosaicSceneWidget::moveToBottom(MosaicSceneItem *item) {
1792  double originalZ = item->zValue();
1793  double minZ = minimumZ();
1794 
1795  if (originalZ != minZ) {
1796  // We know min-1 isn't already used
1797  int newZValue = qRound(minZ - 1);
1798  item->setZValue(newZValue);
1799 
1800  // Remove this if we enable the compress
1801  m_currentMinimumFootprintZ--;
1802  }
1803 
1804  return originalZ;
1805  }
1806 
1807 
1812  double MosaicSceneWidget::moveToBottom(Image *image) {
1813  return moveToBottom(cubeToMosaic(image));
1814  }
1815 
1816 
1821  QList<double> MosaicSceneWidget::moveToBottom(ImageList *images) {
1822  QList<double> results;
1823 
1824  foreach (Image *image, *images) {
1825  results.append(moveToBottom(image));
1826  }
1827 
1828  return results;
1829  }
1830 
1831 
1832  void MosaicSceneWidget::moveUpOne() {
1833  DisplayProperties *props = qobject_cast<DisplayProperties *>(sender());
1834 
1835  if (props) {
1836  moveUpOne(cubeToMosaic(props));
1837  }
1838  }
1839 
1840 
1843  double MosaicSceneWidget::moveUpOne(MosaicSceneItem *item) {
1844  MosaicSceneItem *nextUp = getNextItem(item, true);
1845  double originalZ = item->zValue();
1846 
1847  if (nextUp) {
1848  double newZValue = nextUp->zValue() + 1;
1849  moveZ(item, newZValue, true);
1850  }
1851 
1852  return originalZ;
1853  }
1854 
1855 
1858  double MosaicSceneWidget::moveUpOne(Image *image) {
1859  return moveUpOne(cubeToMosaic(image));
1860  }
1861 
1862 
1865  QList<double> MosaicSceneWidget::moveUpOne(ImageList *images) {
1866  QList<double> results;
1867 
1868  foreach (Image *image, *images) {
1869  results.append(moveUpOne(image));
1870  }
1871 
1872  return results;
1873  }
1874 
1875 
1876  void MosaicSceneWidget::moveToTop() {
1877  DisplayProperties *props = qobject_cast<DisplayProperties *>(sender());
1878 
1879  if (props) {
1880  moveToTop(cubeToMosaic(props));
1881  }
1882  }
1883 
1884 
1889  double MosaicSceneWidget::moveToTop(MosaicSceneItem *item) {
1890  double originalZ = item->zValue();
1891  double maxZ = maximumZ();
1892 
1893  if (originalZ != maxZ) {
1894  // We know max+1 isn't already used
1895  int newZValue = qRound(maxZ + 1);
1896  item->setZValue(newZValue);
1897 
1898  // Remove this if we enable the compress
1899  m_currentMaximumFootprintZ++;
1900  }
1901 
1902  // Compress... this makes this method have a time complexity of N instead of constant; there
1903  // isn't really a good justification for the slow down. I'm leaving this (working) code here
1904  // for reference and in case it's needed later.
1905  // foreach (MosaicSceneItem *otherItem, *m_mosaicSceneItems) {
1906  // double otherItemZ = otherItem->zValue();
1907  // if (otherItemZ > originalZ) {
1908  // otherItem->setZValue(otherItemZ - 1.0);
1909  // }
1910  // }
1911 
1912  return originalZ;
1913  }
1914 
1915 
1920  double MosaicSceneWidget::moveToTop(Image *image) {
1921  return moveToTop(cubeToMosaic(image));
1922  }
1923 
1924 
1929  QList<double> MosaicSceneWidget::moveToTop(ImageList *images) {
1930  QList<double> results;
1931 
1932  foreach (Image *image, *images) {
1933  results.append(moveToTop(image));
1934  }
1935 
1936 
1937 // printZ(m_mosaicSceneItems);
1938  return results;
1939  }
1940 
1941 
1957  double MosaicSceneWidget::moveZ(MosaicSceneItem *sceneItem, double newZ,
1958  bool newZValueMightExist) {
1959  double originalZ = sceneItem->zValue();
1960 
1961  if (newZValueMightExist) {
1962  // Adjust items between original and new position, recalculate min/max - time complexity=N
1963  m_currentMinimumFootprintZ = 0.0;
1964  m_currentMaximumFootprintZ = 0.0;
1965 
1966  foreach (MosaicSceneItem *otherItem, *m_mosaicSceneItems) {
1967  double otherItemOrigZ = otherItem->zValue();
1968  double otherItemNewZ = otherItemOrigZ;
1969 
1970  // Moving downwards (new Z is lower than current Z) and item is in the middle
1971  if (originalZ > newZ && otherItemOrigZ >= newZ && otherItemOrigZ < originalZ) {
1972  otherItemNewZ = otherItemOrigZ + 1;
1973  }
1974  // Moving upwards (new Z is higher than current Z) and item is in the middle
1975  else if (originalZ < newZ && otherItemOrigZ <= newZ && otherItemOrigZ > originalZ) {
1976  otherItemNewZ = otherItemOrigZ - 1;
1977  }
1978 
1979  m_currentMinimumFootprintZ = qMin(m_currentMinimumFootprintZ, otherItemNewZ);
1980  m_currentMaximumFootprintZ = qMax(m_currentMaximumFootprintZ, otherItemNewZ);
1981  otherItem->setZValue(otherItemNewZ);
1982  }
1983  }
1984 
1985  sceneItem->setZValue(newZ);
1986 
1987  if (!newZValueMightExist) {
1988  // If we moved the max or min item, adjust the max down or min up respectively
1989  if (originalZ == maximumZ() && newZ < originalZ) {
1990  m_currentMaximumFootprintZ--;
1991  }
1992  else if (originalZ == minimumZ() && newZ > originalZ) {
1993  m_currentMinimumFootprintZ++;
1994  }
1995  }
1996 
1997  return originalZ;
1998  }
1999 
2000 
2001  double MosaicSceneWidget::moveZ(Image *image, double newZ,
2002  bool newZValueMightExist) {
2003  return moveZ(cubeToMosaic(image), newZ, newZValueMightExist);
2004  }
2005 
2006 
2007  void MosaicSceneWidget::fitInView() {
2008  if (m_userToolControl) {
2009  QRectF boundingBox;
2010 
2011  DisplayProperties *props = qobject_cast<DisplayProperties *>(sender());
2012 
2013  if (props) {
2014  MosaicSceneItem *item = cubeToMosaic(props);
2015  boundingBox = item->boundingRect();
2016  }
2017  else {
2018  QAction *action = qobject_cast<QAction *>(sender());
2019 
2020  if (action) {
2021  ImageList *images = action->data().value<ImageList *>();
2022 
2023  foreach (Image *image, *images) {
2024  ASSERT(cubeToMosaic(image));
2025  boundingBox = boundingBox.united(cubeToMosaic(image)->boundingRect());
2026  }
2027  }
2028  }
2029 
2030  if (!boundingBox.isNull()) {
2031  double xPadding = boundingBox.width() * 0.10;
2032  double yPadding = boundingBox.height() * 0.10;
2033 
2034  boundingBox.setLeft(boundingBox.left() - xPadding);
2035  boundingBox.setRight(boundingBox.right() + xPadding);
2036 
2037  boundingBox.setTop(boundingBox.top() - yPadding);
2038  boundingBox.setBottom(boundingBox.bottom() + yPadding);
2039 
2040  getView()->fitInView(boundingBox, Qt::KeepAspectRatio);
2041  getView()->centerOn(boundingBox.center());
2042  }
2043  }
2044  }
2045 
2046 
2047  void MosaicSceneWidget::onSelectionChanged() {
2048  if (!m_blockingSelectionChanged) {
2049  if (!m_queuedSelectionChanged) {
2050  emit queueSelectionChanged();
2051  m_queuedSelectionChanged = true;
2052  }
2053  else {
2054  m_shouldRequeueSelectionChanged = true;
2055  }
2056  }
2057  }
2058 
2059 
2060  void MosaicSceneWidget::onQueuedSelectionChanged() {
2061  m_queuedSelectionChanged = false;
2062 
2063  if (m_shouldRequeueSelectionChanged) {
2064  m_shouldRequeueSelectionChanged = false;
2065  onSelectionChanged();
2066  }
2067  else {
2068  foreach(MosaicSceneItem *mosaicSceneItem, *m_mosaicSceneItems) {
2069  mosaicSceneItem->updateSelection(true);
2070  }
2071  }
2072  }
2073 
2074 
2077  MosaicSceneItem *nextZValueItem = NULL;
2078  MosaicSceneItem *mosaicSceneItem;
2079 
2080  foreach(mosaicSceneItem, *m_mosaicSceneItems) {
2081  if (mosaicSceneItem != item &&
2082  mosaicSceneItem->boundingRect().intersects(item->boundingRect())) {
2083  // Does this item qualify as above or below at all?
2084  if ( (up && mosaicSceneItem->zValue() > item->zValue()) ||
2085  (!up && mosaicSceneItem->zValue() < item->zValue())) {
2086  // It is in the correct direction, set the initial guess if we don't
2087  // have one or test if it's better
2088  if (!nextZValueItem) {
2089  nextZValueItem = mosaicSceneItem;
2090  }
2091  else {
2092  // We know it qualifies, we want to know if it's closer than
2093  // nextZValueItem
2094  if ((up && mosaicSceneItem->zValue() < nextZValueItem->zValue()) ||
2095  (!up && mosaicSceneItem->zValue() > nextZValueItem->zValue())) {
2096  nextZValueItem = mosaicSceneItem;
2097  }
2098  }
2099  }
2100  }
2101  }
2102 
2103  return nextZValueItem;
2104  }
2105 
2106 
2107  bool MosaicSceneWidget::zOrderGreaterThan(MosaicSceneItem *first,
2108  MosaicSceneItem *second) {
2109  return first->zValue() > second->zValue();
2110  }
2111 
2112 
2113  MosaicSceneWidget::XmlHandler::XmlHandler(MosaicSceneWidget *scene) {
2114  m_scene = scene;
2115  m_scrollBarXValue = -1;
2116  m_scrollBarYValue = -1;
2117  m_imagesToAdd = NULL;
2118 
2119  m_imagesToAdd = new ImageList;
2120  }
2121 
2122 
2123  MosaicSceneWidget::XmlHandler::~XmlHandler() {
2124  delete m_imagesToAdd;
2125  m_imagesToAdd = NULL;
2126  }
2127 
2128 
2129  bool MosaicSceneWidget::XmlHandler::startElement(const QString &namespaceURI,
2130  const QString &localName, const QString &qName, const QXmlAttributes &atts) {
2131  bool result = XmlStackedHandler::startElement(namespaceURI, localName, qName, atts);
2132 
2133  m_characterData = "";
2134 
2135  if (result) {
2136  if (localName == "image" && m_scene->m_directory) {
2137  QString id = atts.value("id");
2138  double zValue = atts.value("zValue").toDouble();
2139  Image *image = m_scene->m_directory->project()->image(id);
2140  // If Image for id doesn't exist, check shapes. If corresponds to Shape, new Image will
2141  // need to be created.
2142  if (!image) {
2143  Shape *shape = m_scene->m_directory->project()->shape(id);
2144  if (shape) {
2145  image = new Image(shape->cube(), shape->footprint(), id);
2146  }
2147  }
2148  if (image) {
2149  m_imagesToAdd->append(image);
2150  m_imageZValues.append(zValue);
2151 // m_scene->cubeToMosaic(image)->setZValue(zValue);
2152  }
2153  }
2154  else if (localName == "viewTransform") {
2155  m_scrollBarXValue = atts.value("scrollBarXValue").toInt();
2156  m_scrollBarYValue = atts.value("scrollBarYValue").toInt();
2157  }
2158  }
2159 
2160  return result;
2161  }
2162 
2163 
2164  bool MosaicSceneWidget::XmlHandler::characters(const QString &ch) {
2165  bool result = XmlStackedHandler::characters(ch);
2166 
2167  if (result) {
2168  m_characterData += ch;
2169  }
2170 
2171  return result;
2172  }
2173 
2174 
2175  bool MosaicSceneWidget::XmlHandler::endElement(const QString &namespaceURI,
2176  const QString &localName, const QString &qName) {
2177  bool result = XmlStackedHandler::endElement(namespaceURI, localName, qName);
2178 
2179  if (result) {
2180  if (localName == "projection") {
2181  std::stringstream strStream(m_characterData.toStdString());
2182  PvlGroup mappingGroup;
2183  strStream >> mappingGroup;
2184  m_scene->setProjection(mappingGroup);
2185  }
2186  else if (localName == "viewTransform") {
2187  QByteArray hexValues(m_characterData.toLatin1());
2188  QDataStream transformStream(QByteArray::fromHex(hexValues));
2189 
2190  QTransform viewTransform;
2191  transformStream >> viewTransform;
2192  m_scene->getView()->show();
2193  QCoreApplication::processEvents();
2194  m_scene->getView()->setTransform(viewTransform);
2195  m_scene->getView()->horizontalScrollBar()->setValue(m_scrollBarXValue);
2196  m_scene->getView()->verticalScrollBar()->setValue(m_scrollBarYValue);
2197  }
2198  else if (localName == "toolData") {
2199  PvlObject toolSettings;
2200  std::stringstream strStream(m_characterData.toStdString());
2201  strStream >> toolSettings;
2202 
2203  foreach (MosaicTool *tool, *m_scene->m_tools) {
2204  if (tool->projectPvlObjectName() == toolSettings.name()) {
2205  tool->fromPvl(toolSettings);
2206  }
2207  }
2208  }
2209  else if (localName == "images" && m_imagesToAdd->count()) {
2210  ASSERT(m_imagesToAdd->count() == m_imageZValues.count());
2211  m_scene->addImages(*m_imagesToAdd);
2212 
2213  for (int i = 0; i < m_imageZValues.count(); i++) {
2214  m_scene->cubeToMosaic(m_imagesToAdd->at(i))->setZValue(m_imageZValues[i]);
2215  m_scene->m_currentMinimumFootprintZ = qMin(m_scene->m_currentMinimumFootprintZ,
2216  m_imageZValues[i]);
2217  m_scene->m_currentMaximumFootprintZ = qMax(m_scene->m_currentMaximumFootprintZ,
2218  m_imageZValues[i]);
2219  }
2220  }
2221  }
2222 
2223  m_characterData = "";
2224 
2225  return result;
2226  }
2227 }
static Isis::Projection * Create(Isis::Pvl &label, bool allowDefaults=false)
This method returns a pointer to a Projection object.
Handles zoom operations for Isis qt apps.
//TODO: Remove debug printout & comment // 2016-08-25 Tracie Sucharski - Checking Directory pointer f...
int keywords() const
Returns the number of keywords contained in the PvlContainer.
Definition: PvlContainer.h:100
MosaicGraphicsView * m_graphicsView
The graphics view.
Internalizes a list of images and allows for operations on the entire list.
Definition: ImageList.h:55
static Isis::Projection * CreateFromCube(Isis::Cube &cube)
This method is a helper method.
void reproject()
Called anytime the user reprojects the cube.
Move images, one by one, on top of the immediately-above intersecting image in a scene This workorder...
The main project for ipce.
Definition: Project.h:289
PvlGroupIterator findGroup(const QString &name, PvlGroupIterator beg, PvlGroupIterator end)
Find a group with the specified name, within these indexes.
Definition: PvlObject.h:141
QList< QAction * > supportedActions(DataType data)
Returns a list of supported actions for a WorkOrder.
Definition: Directory.h:359
A graphics view that resizes in a more friendly way.
void setProjection(Projection *)
This method takes ownership of proj.
double moveToBottom(MosaicSceneItem *)
This doesn&#39;t compress the Z values - the original Z values of this scene item is guaranteed to be emp...
Handles selection operations for Isis qt apps.
MosaicSceneWidget(QStatusBar *status, bool showTools, bool internalizeToolBarsAndProgress, Directory *directory, QWidget *parent=0)
Create a scene widget.
double moveToTop(MosaicSceneItem *)
This doesn&#39;t compress the Z values - the original Z values of this scene item is guaranteed to be emp...
PvlObjectIterator findObject(const QString &name, PvlObjectIterator beg, PvlObjectIterator end)
Find the index of object with a specified name, between two indexes.
Definition: PvlObject.h:286
MosaicSceneItem * getNextItem(MosaicSceneItem *item, bool up)
Implemented because we want invisible items too.
Base class for the MosaicTools.
Definition: MosaicTool.h:37
int toInt(const QString &string)
Global function to convert from a string to an integer.
Definition: IString.cpp:108
bool contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
This is called by MosaicGraphicsScene::contextMenuEvent.
void refit()
This method refits t:he items in the graphics view.
QString displayName() const
Returns the display name.
double moveZ(MosaicSceneItem *sceneItem, double newZ, bool newZValueMightExist=true)
This method moves the given scene item to the given Z value.
This displays a box with a given distance from a point.
Search child objects.
Definition: PvlObject.h:170
QGraphicsScene * m_graphicsScene
The graphics scene that makes up this widget.
void configProjectionParameters()
This happens when the user clicks on the map action (the button that is named after the current proje...
QString toString(bool boolToConvert)
Global function to convert a boolean to a string.
Definition: IString.cpp:226
void exportView()
Saves the scene as a png, jpg, or tif file.
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
double toDouble(const QString &string)
Global function to convert from a string to a double.
Definition: IString.cpp:164
This error is for when a programmer made an API call that was illegal.
Definition: IException.h:162
bool isActive() const
Returns the activeness of this toool.
Definition: MosaicTool.h:50
bool hasObject(const QString &name) const
Returns a boolean value based on whether the object exists in the current PvlObject or not...
Definition: PvlObject.h:335
Move images, one by one, below the immediately-below intersecting image in a scene This workorder is ...
QList< QAction * > supportedActions(ImageList *)
Get a list of actions this scene can perform given "images".
QList< QAction * > supportedActions(Project *project=NULL)
Gets a list of pre-connected actions that have to do with display.
Definition: ImageList.cpp:565
Base class for Map Projections.
Definition: Projection.h:171
void setText(QString text)
Set custom text for this progress bar.
Definition: ProgressBar.cpp:32
This is the configuration dialog for the MosaicSceneWidget&#39;s projection parameters (map file)...
Contains multiple PvlContainers.
Definition: PvlGroup.h:57
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:40
A type of error that could only have occurred due to a mistake on the user&#39;s part (e...
Definition: IException.h:142
Move images on top of all other images in a mosaic scene This workorder is synchronous and undoable...
ImageList selectedImages()
Returns a list of all the cubes selected in the scene.
static PvlGroup radiiGroup(QString target)
Creates a Pvl Group with keywords TargetName, EquitorialRadius, and PolarRadius.
Definition: Target.cpp:393
A single keyword-value pair.
Definition: PvlKeyword.h:98
virtual void pushContentHandler(XmlStackedHandler *newHandler)
Push a contentHandler and maybe continue parsing...
A single control point.
Definition: ControlPoint.h:369
This represents a cube in a project-based GUI interface.
Definition: Image.h:107
ImageDisplayProperties * displayProperties()
Get the display (GUI) properties (information) associated with this image.
Definition: Image.cpp:320
virtual void setData(Context)
Sets the context data for this WorkOrder.
Definition: WorkOrder.cpp:262
void append(Image *const &value)
Appends an image to the image list.
Definition: ImageList.cpp:153
void fromPvl(const PvlObject &)
Call this method after loading any cubes when loading a project.
PvlKeyword & findKeyword(const QString &name)
Find a keyword with a specified name.
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
Provides access to sequential ASCII stream I/O.
Definition: TextFile.h:54
void print() const
Prints a string representation of this exception to stderr.
Definition: IException.cpp:461
This controls the &#39;Find&#39; abilities in the MosaicSceneWidget.
void reprojectItems()
Reprojects all the items in the view.
Isis exception class.
Definition: IException.h:107
Namespace for ISIS/Bullet specific routines.
Definition: Apollo.h:31
A graphics scene with improved user-interaction for use with the MosaicSceneWidget.
Project * project() const
Gets the Project for this directory.
Definition: Directory.cpp:1325
Projection * m_projection
The current projection type.
Handles panning operations for Isis qt apps.
Definition: MosaicPanTool.h:22
This controls the &#39;Grid&#39; abilities in the MosaicSceneWidget.
Contains Pvl Groups and Pvl Objects.
Definition: PvlObject.h:74
A single cube in the mosaic scene.
Manage a stack of content handlers for reading XML files.
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
Move images below all other images in a mosaic scene This workorder is synchronous and undoable...