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

U.S. Department of the Interior | U.S. Geological Survey
ISIS | Privacy & Disclaimers | Astrogeology Research Program
To contact us, please post comments and questions on the ISIS Support Center
File Modified: 07/12/2023 23:24:05