GraphViewer.cpp
Go to the documentation of this file.
1 /*
2 Copyright (c) 2010-2016, Mathieu Labbe - IntRoLab - Universite de Sherbrooke
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7  * Redistributions of source code must retain the above copyright
8  notice, this list of conditions and the following disclaimer.
9  * Redistributions in binary form must reproduce the above copyright
10  notice, this list of conditions and the following disclaimer in the
11  documentation and/or other materials provided with the distribution.
12  * Neither the name of the Universite de Sherbrooke nor the
13  names of its contributors may be used to endorse or promote products
14  derived from this software without specific prior written permission.
15 
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 
29 
30 #include <QGraphicsView>
31 #include <QVBoxLayout>
32 #include <QGraphicsScene>
33 #include <QGraphicsEllipseItem>
34 #include <QtGui/QWheelEvent>
35 #include <QGraphicsSceneHoverEvent>
36 #include <QMenu>
37 #include <QtGui/QDesktopServices>
38 #include <QtGui/QContextMenuEvent>
39 #include <QColorDialog>
40 #include <QPrinter>
41 #include <QFileDialog>
42 #ifdef QT_SVG_LIB
43 #include <QtSvg/QSvgGenerator>
44 #endif
45 #include <QInputDialog>
46 #include <QMessageBox>
47 
48 #include <QtCore/QDir>
49 #include <QtCore/QDateTime>
50 #include <QtCore/QUrl>
51 
52 #include <rtabmap/core/util3d.h>
54 #include <rtabmap/utilite/UCv2Qt.h>
55 #include <rtabmap/utilite/UStl.h>
57 #include <rtabmap/utilite/UTimer.h>
58 
59 #include <QtGlobal>
60 #if QT_VERSION >= 0x050000
61  #include <QStandardPaths>
62 #endif
63 
64 namespace rtabmap {
65 
66 class NodeItem: public QGraphicsEllipseItem
67 {
68 public:
69  // in meter
70  NodeItem(int id, int mapId, const Transform & pose, float radius, int weight=-1) :
71  QGraphicsEllipseItem(QRectF(-radius*100.0f,-radius*100.0f,radius*100.0f*2.0f,radius*100.0f*2.0f)),
72  _id(id),
73  _mapId(mapId),
74  _weight(weight),
75  _pose(pose),
76  _line(0)
77  {
78  this->setPos(-pose.y()*100.0f,-pose.x()*100.0f);
79  this->setBrush(pen().color());
80  this->setAcceptHoverEvents(true);
81  float r,p,yaw;
82  pose.getEulerAngles(r, p, yaw);
83  radius*=100.0f;
84  _line = new QGraphicsLineItem(0,0,-radius*sin(yaw),-radius*cos(yaw), this);
85  }
86  virtual ~NodeItem() {}
87 
88  void setColor(const QColor & color)
89  {
90  QPen p = this->pen();
91  p.setColor(color);
92  this->setPen(p);
93  QBrush b = this->brush();
94  b.setColor(color);
95  this->setBrush(b);
96 
97  _line->setPen(QPen(QColor(255-color.red(), 255-color.green(), 255-color.blue())));
98  }
99 
100  void setRadius(float radius)
101  {
102  float r,p,yaw;
103  _pose.getEulerAngles(r, p, yaw);
104  radius*=100.0f;
105  this->setRect(-radius, -radius, radius*2.0f, radius*2.0f);
106  _line->setLine(0,0,-radius*sin(yaw),-radius*cos(yaw));
107  }
108 
109  int id() const {return _id;};
110  int mapId() const {return _mapId;}
111  const Transform & pose() const {return _pose;}
112  void setPose(const Transform & pose) {this->setPos(-pose.y()*100.0f,-pose.x()*100.0f); _pose=pose;}
113 
114 protected:
115  virtual void hoverEnterEvent ( QGraphicsSceneHoverEvent * event )
116  {
117  if(_weight>=0)
118  {
119  this->setToolTip(QString("%1 [map=%2, w=%3] %4").arg(_id).arg(_mapId).arg(_weight).arg(_pose.prettyPrint().c_str()));
120  }
121  else
122  {
123  this->setToolTip(QString("%1 [map=%2] %3").arg(_id).arg(_mapId).arg(_pose.prettyPrint().c_str()));
124  }
125  this->setScale(2);
126  QGraphicsEllipseItem::hoverEnterEvent(event);
127  }
128 
129  virtual void hoverLeaveEvent ( QGraphicsSceneHoverEvent * event )
130  {
131  this->setScale(1);
132  QGraphicsEllipseItem::hoverEnterEvent(event);
133  }
134 
135 private:
136  int _id;
137  int _mapId;
138  int _weight;
140  QGraphicsLineItem * _line;
141 };
142 
143 class NodeGPSItem: public NodeItem
144 {
145 public:
146  NodeGPSItem(int id, int mapId, const Transform & pose, float radius, const GPS & gps) :
147  NodeItem(id, mapId, pose, radius),
148  _gps(gps)
149  {
150  }
151  virtual ~NodeGPSItem() {}
152 protected:
153  virtual void hoverEnterEvent ( QGraphicsSceneHoverEvent * event )
154  {
155  this->setToolTip(QString("%1 [%2] %3\n"
156  "longitude=%4 latitude=%5 altitude=%6m error=%7m bearing=%8deg")
157  .arg(id()).arg(mapId()).arg(pose().prettyPrint().c_str())
158  .arg(_gps.longitude()).arg(_gps.latitude()).arg(_gps.altitude()).arg(_gps.error()).arg(_gps.bearing()));
159  this->setScale(2);
160  QGraphicsEllipseItem::hoverEnterEvent(event);
161  }
162 private:
164 };
165 
166 class LinkItem: public QGraphicsLineItem
167 {
168 public:
169  // in meter
170  LinkItem(int from, int to, const Transform & poseA, const Transform & poseB, const Link & link, bool interSessionClosure) :
171  QGraphicsLineItem(-poseA.y()*100.0f, -poseA.x()*100.0f, -poseB.y()*100.0f, -poseB.x()*100.0f),
172  _from(from),
173  _to(to),
174  _poseA(poseA),
175  _poseB(poseB),
176  _link(link),
177  _interSession(interSessionClosure)
178  {
179  this->setAcceptHoverEvents(true);
180  }
181  virtual ~LinkItem() {}
182 
183  void setColor(const QColor & color)
184  {
185  QPen p = this->pen();
186  p.setColor(color);
187  this->setPen(p);
188  }
189 
190  void setPoses(const Transform & poseA, const Transform & poseB)
191  {
192  this->setLine(-poseA.y()*100.0f, -poseA.x()*100.0f, -poseB.y()*100.0f, -poseB.x()*100.0f);
193  _poseA = poseA;
194  _poseB = poseB;
195  }
196 
197  const Transform & getPoseA() const
198  {
199  return _poseA;
200  }
201  const Transform & getPoseB() const
202  {
203  return _poseB;
204  }
205 
206  Link::Type linkType() const {return _link.type();}
207  bool isInterSession() const {return _interSession;}
208  int from() const {return _from;}
209  int to() const {return _to;}
210 
211 protected:
212  virtual void hoverEnterEvent ( QGraphicsSceneHoverEvent * event )
213  {
214  QString str = QString("%1->%2 (%3 m)").arg(_from).arg(_to).arg(_poseA.getDistance(_poseB));
215  if(!_link.transform().isNull())
216  {
217  str.append(QString("\n%1\n%2 %3").arg(_link.transform().prettyPrint().c_str()).arg(_link.transVariance()).arg(_link.rotVariance()));
218  }
219  this->setToolTip(str);
220  QPen pen = this->pen();
221  pen.setWidthF(pen.widthF()+2);
222  this->setPen(pen);
223  QGraphicsLineItem::hoverEnterEvent(event);
224  }
225 
226  virtual void hoverLeaveEvent ( QGraphicsSceneHoverEvent * event )
227  {
228  QPen pen = this->pen();
229  pen.setWidthF(pen.widthF()-2);
230  this->setPen(pen);
231  QGraphicsLineItem::hoverEnterEvent(event);
232  }
233 
234 private:
235  int _from;
236  int _to;
241 };
242 
243 GraphViewer::GraphViewer(QWidget * parent) :
244  QGraphicsView(parent),
245  _nodeColor(Qt::blue),
246  _currentGoalColor(Qt::darkMagenta),
247  _neighborColor(Qt::blue),
248  _loopClosureColor(Qt::red),
249  _loopClosureLocalColor(Qt::yellow),
250  _loopClosureUserColor(Qt::red),
251  _loopClosureVirtualColor(Qt::magenta),
252  _neighborMergedColor(QColor(255,170,0)),
253  _landmarkColor(Qt::darkGreen),
254  _loopClosureRejectedColor(Qt::black),
255  _localPathColor(Qt::cyan),
256  _globalPathColor(Qt::darkMagenta),
257  _gtPathColor(Qt::gray),
258  _gpsPathColor(Qt::darkCyan),
259  _loopIntraSessionColor(Qt::red),
260  _loopInterSessionColor(Qt::green),
261  _intraInterSessionColors(false),
262  _worldMapRotation(0.0f),
263  _world(0),
264  _root(0),
265  _graphRoot(0),
266  _globalPathRoot(0),
267  _nodeVisible(true),
268  _nodeRadius(0.01f),
269  _linkWidth(0),
270  _gridMap(0),
271  _referential(0),
272  _originReferential(0),
273  _gridCellSize(0.0f),
274  _localRadius(0),
275  _loopClosureOutlierThr(0),
276  _maxLinkLength(0.02f),
277  _orientationENU(false)
278 {
279  this->setScene(new QGraphicsScene(this));
280  this->setDragMode(QGraphicsView::ScrollHandDrag);
281  _workingDirectory = QDir::homePath();
282 
283  this->scene()->clear();
284  _world = (QGraphicsItem *)this->scene()->addEllipse(QRectF(-0.0001,-0.0001,0.0001,0.0001));
285  _root = (QGraphicsItem *)this->scene()->addEllipse(QRectF(-0.0001,-0.0001,0.0001,0.0001));
286  _root->setParentItem(_world);
287 
288  // add referential
289  _originReferential = new QGraphicsItemGroup();
290  this->scene()->addItem(_originReferential); // ownership transfered
291  QGraphicsLineItem * item = this->scene()->addLine(0,0,0,-100, QPen(QBrush(Qt::red), _linkWidth));
292  item->setZValue(1);
293  item->setParentItem(_root);
294  _originReferential->addToGroup(item);
295  item = this->scene()->addLine(0,0,-100,0, QPen(QBrush(Qt::green), _linkWidth));
296  item->setZValue(1);
297  item->setParentItem(_root);
298  _originReferential->addToGroup(item);
299  _originReferential->setZValue(1);
300  _originReferential->setParentItem(_root);
301 
302  // current pose
303  _referential = new QGraphicsItemGroup();
304  this->scene()->addItem(_referential); // ownership transfered
305  item = this->scene()->addLine(0,0,0,-50, QPen(QBrush(Qt::red), _linkWidth));
306  item->setZValue(100);
307  item->setParentItem(_root);
308  _referential->addToGroup(item);
309  item = this->scene()->addLine(0,0,-50,0, QPen(QBrush(Qt::green), _linkWidth));
310  item->setZValue(100);
311  item->setParentItem(_root);
312  _referential->addToGroup(item);
313  _referential->setZValue(100);
314  _referential->setParentItem(_root);
315 
316  _localRadius = this->scene()->addEllipse(-0.0001,-0.0001,0.0001,0.0001);
317  _localRadius->setZValue(1);
318  _localRadius->setParentItem(_root);
319  _localRadius->setVisible(false);
320  _localRadius->setPen(QPen(Qt::DashLine));
321 
322  _gridMap = this->scene()->addPixmap(QPixmap());
323  _gridMap->setZValue(0);
324  _gridMap->setParentItem(_root);
325 
326  _graphRoot = (QGraphicsItem *)this->scene()->addEllipse(QRectF(-0.0001,-0.0001,0.0001,0.0001));
327  _graphRoot->setZValue(4);
328  _graphRoot->setParentItem(_root);
329 
330  _globalPathRoot = (QGraphicsItem *)this->scene()->addEllipse(QRectF(-0.0001,-0.0001,0.0001,0.0001));
331  _globalPathRoot->setZValue(8);
332  _globalPathRoot->setParentItem(_root);
333 
334  _localPathRoot = (QGraphicsItem *)this->scene()->addEllipse(QRectF(-0.0001,-0.0001,0.0001,0.0001));
335  _localPathRoot->setZValue(9);
336  _localPathRoot->setParentItem(_root);
337 
338  _gtGraphRoot = (QGraphicsItem *)this->scene()->addEllipse(QRectF(-0.0001,-0.0001,0.0001,0.0001));
339  _gtGraphRoot->setZValue(2);
340  _gtGraphRoot->setParentItem(_root);
341 
342  _gpsGraphRoot = (QGraphicsItem *)this->scene()->addEllipse(QRectF(-0.0001,-0.0001,0.0001,0.0001));
343  _gpsGraphRoot->setZValue(3);
344  _gpsGraphRoot->setParentItem(_root);
345 
346  this->restoreDefaults();
347 
348  this->fitInView(this->sceneRect(), Qt::KeepAspectRatio);
349 }
350 
352 {
353 }
354 
355 void GraphViewer::setWorldMapRotation(const float & theta)
356 {
357  _worldMapRotation = theta;
359 }
360 
361 void GraphViewer::updateGraph(const std::map<int, Transform> & poses,
362  const std::multimap<int, Link> & constraints,
363  const std::map<int, int> & mapIds,
364  const std::map<int, int> & weights)
365 {
366  UTimer timer;
367  bool wasVisible = _graphRoot->isVisible();
368  _graphRoot->show();
369 
370  bool wasEmpty = _nodeItems.size() == 0 && _linkItems.size() == 0;
371  UDEBUG("poses=%d constraints=%d", (int)poses.size(), (int)constraints.size());
372  //Hide nodes and links
373  for(QMap<int, NodeItem*>::iterator iter = _nodeItems.begin(); iter!=_nodeItems.end(); ++iter)
374  {
375  iter.value()->hide();
376  iter.value()->setColor(iter.key()<0?QColor(255-_nodeColor.red(), 255-_nodeColor.green(), 255-_nodeColor.blue()):_nodeColor); // reset color
377  iter.value()->setZValue(iter.key()<0?21:20);
378  }
379  for(QMultiMap<int, LinkItem*>::iterator iter = _linkItems.begin(); iter!=_linkItems.end(); ++iter)
380  {
381  iter.value()->hide();
382  }
383 
384  for(std::map<int, Transform>::const_iterator iter=poses.begin(); iter!=poses.end(); ++iter)
385  {
386  if(!iter->second.isNull())
387  {
388  QMap<int, NodeItem*>::iterator itemIter = _nodeItems.find(iter->first);
389  if(itemIter != _nodeItems.end())
390  {
391  itemIter.value()->setPose(iter->second);
392  itemIter.value()->show();
393  }
394  else
395  {
396  // create node item
397  const Transform & pose = iter->second;
398  NodeItem * item = new NodeItem(iter->first, uContains(mapIds, iter->first)?mapIds.at(iter->first):-1, pose, _nodeRadius, uContains(weights, iter->first)?weights.at(iter->first):-1);
399  this->scene()->addItem(item);
400  item->setZValue(iter->first<0?21:20);
401  item->setColor(iter->first<0?QColor(255-_nodeColor.red(), 255-_nodeColor.green(), 255-_nodeColor.blue()):_nodeColor);
402  item->setParentItem(_graphRoot);
403  item->setVisible(_nodeVisible);
404  _nodeItems.insert(iter->first, item);
405  }
406  }
407  }
408 
409  for(std::multimap<int, Link>::const_iterator iter=constraints.begin(); iter!=constraints.end(); ++iter)
410  {
411  // make the first id the smallest one
412  int idFrom = iter->first<iter->second.to()?iter->first:iter->second.to();
413  int idTo = iter->first<iter->second.to()?iter->second.to():iter->first;
414 
415  std::map<int, Transform>::const_iterator jterA = poses.find(idFrom);
416  std::map<int, Transform>::const_iterator jterB = poses.find(idTo);
417  LinkItem * linkItem = 0;
418  if(jterA != poses.end() && jterB != poses.end() &&
419  _nodeItems.contains(iter->first) && _nodeItems.contains(idTo))
420  {
421  const Transform & poseA = jterA->second;
422  const Transform & poseB = jterB->second;
423 
424  QMultiMap<int, LinkItem*>::iterator itemIter = _linkItems.end();
425  if(_linkItems.contains(idFrom))
426  {
427  itemIter = _linkItems.find(iter->first);
428  while(itemIter.key() == idFrom && itemIter != _linkItems.end())
429  {
430  if(itemIter.value()->to() == idTo)
431  {
432  itemIter.value()->setPoses(poseA, poseB);
433  itemIter.value()->show();
434  linkItem = itemIter.value();
435  break;
436  }
437  ++itemIter;
438  }
439  }
440 
441  bool interSessionClosure = false;
442  if(uContains(mapIds, jterA->first) && uContains(mapIds, jterB->first))
443  {
444  interSessionClosure = mapIds.at(jterA->first) != mapIds.at(jterB->first);
445  }
446 
447  if(poseA.getDistance(poseB) > _maxLinkLength)
448  {
449  if(linkItem == 0)
450  {
451  //create a link item
452  linkItem = new LinkItem(idFrom, idTo, poseA, poseB, iter->second, interSessionClosure);
453  QPen p = linkItem->pen();
454  p.setWidthF(_linkWidth*100.0f);
455  linkItem->setPen(p);
456  linkItem->setZValue(10);
457  this->scene()->addItem(linkItem);
458  linkItem->setParentItem(_graphRoot);
459  _linkItems.insert(idFrom, linkItem);
460  }
461  }
462  else if(linkItem && itemIter != _linkItems.end())
463  {
464  // erase small links
465  _linkItems.erase(itemIter);
466  delete linkItem;
467  linkItem = 0;
468  }
469 
470  if(linkItem)
471  {
472  //update color
473  if(iter->second.type() == Link::kNeighbor)
474  {
475  linkItem->setColor(_neighborColor);
476  }
477  else if(iter->second.type() == Link::kVirtualClosure)
478  {
480  }
481  else if(iter->second.type() == Link::kNeighborMerged)
482  {
483  linkItem->setColor(_neighborMergedColor);
484  }
485  else if(iter->second.type() == Link::kUserClosure)
486  {
487  linkItem->setColor(_loopClosureUserColor);
488  }
489  else if(iter->second.type() == Link::kLandmark)
490  {
491  linkItem->setColor(_landmarkColor);
492  }
493  else if(iter->second.type() == Link::kLocalSpaceClosure || iter->second.type() == Link::kLocalTimeClosure)
494  {
496  {
497  linkItem->setColor(interSessionClosure?_loopInterSessionColor:_loopIntraSessionColor);
498  linkItem->setZValue(interSessionClosure?6:7);
499  }
500  else
501  {
502  linkItem->setColor(_loopClosureLocalColor);
503  linkItem->setZValue(7);
504  }
505  }
506  else
507  {
509  {
510  linkItem->setColor(interSessionClosure?_loopInterSessionColor:_loopIntraSessionColor);
511  linkItem->setZValue(interSessionClosure?8:9);
512  }
513  else
514  {
515  linkItem->setColor(_loopClosureColor);
516  linkItem->setZValue(9);
517  }
518  }
519 
520  //rejected loop closures
521  if(_loopClosureOutlierThr > 0.0f)
522  {
523  Transform t = poseA.inverse()*poseB;
524  if(iter->second.to() != idTo)
525  {
526  t = t.inverse();
527  }
528  if(iter->second.type() != Link::kNeighbor &&
529  iter->second.type() != Link::kNeighborMerged)
530  {
531  float linearError = fabs(iter->second.transform().getNorm() - t.getNorm());
532  if(linearError > _loopClosureOutlierThr)
533  {
535  }
536  }
537  }
538  }
539  }
540  }
541 
542  //remove not used nodes and links
543  for(QMap<int, NodeItem*>::iterator iter = _nodeItems.begin(); iter!=_nodeItems.end();)
544  {
545  if(!iter.value()->isVisible())
546  {
547  delete iter.value();
548  iter = _nodeItems.erase(iter);
549  }
550  else
551  {
552  ++iter;
553  }
554  }
555  for(QMultiMap<int, LinkItem*>::iterator iter = _linkItems.begin(); iter!=_linkItems.end();)
556  {
557  if(!iter.value()->isVisible())
558  {
559  delete iter.value();
560  iter = _linkItems.erase(iter);
561  }
562  else
563  {
564  ++iter;
565  }
566  }
567 
568  if(_nodeItems.size())
569  {
570  (--_nodeItems.end()).value()->setColor(Qt::green);
571  }
572 
573  this->scene()->setSceneRect(this->scene()->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
574 
575  if(wasEmpty)
576  {
577  QRectF rect = this->scene()->itemsBoundingRect();
578  this->fitInView(rect.adjusted(-rect.width()/2.0f, -rect.height()/2.0f, rect.width()/2.0f, rect.height()/2.0f), Qt::KeepAspectRatio);
579  }
580 
581  _graphRoot->setVisible(wasVisible);
582 
583  UDEBUG("_nodeItems=%d, _linkItems=%d, timer=%fs", _nodeItems.size(), _linkItems.size(), timer.ticks());
584 }
585 
586 void GraphViewer::updateGTGraph(const std::map<int, Transform> & poses)
587 {
588  UTimer timer;
589  bool wasVisible = _gtGraphRoot->isVisible();
590  _gtGraphRoot->show();
591  bool wasEmpty = _gtNodeItems.size() == 0 && _gtLinkItems.size() == 0;
592  UDEBUG("poses=%d", (int)poses.size());
593  //Hide nodes and links
594  for(QMap<int, NodeItem*>::iterator iter = _gtNodeItems.begin(); iter!=_gtNodeItems.end(); ++iter)
595  {
596  iter.value()->hide();
597  iter.value()->setColor(_gtPathColor); // reset color
598  }
599  for(QMultiMap<int, LinkItem*>::iterator iter = _gtLinkItems.begin(); iter!=_gtLinkItems.end(); ++iter)
600  {
601  iter.value()->hide();
602  }
603 
604  for(std::map<int, Transform>::const_iterator iter=poses.begin(); iter!=poses.end(); ++iter)
605  {
606  if(!iter->second.isNull())
607  {
608  QMap<int, NodeItem*>::iterator itemIter = _gtNodeItems.find(iter->first);
609  if(itemIter != _gtNodeItems.end())
610  {
611  itemIter.value()->setPose(iter->second);
612  itemIter.value()->show();
613  }
614  else
615  {
616  // create node item
617  const Transform & pose = iter->second;
618  NodeItem * item = new NodeItem(iter->first, -1, pose, _nodeRadius);
619  this->scene()->addItem(item);
620  item->setZValue(20);
621  item->setColor(_gtPathColor);
622  item->setParentItem(_gtGraphRoot);
623  item->setVisible(_nodeVisible);
624  _gtNodeItems.insert(iter->first, item);
625  }
626 
627  if(iter!=poses.begin())
628  {
629  std::map<int, Transform>::const_iterator iterPrevious = iter;
630  --iterPrevious;
631  Transform previousPose = iterPrevious->second;
632  Transform currentPose = iter->second;
633 
634  LinkItem * linkItem = 0;
635  QMultiMap<int, LinkItem*>::iterator linkIter = _gtLinkItems.end();
636  if(_gtLinkItems.contains(iterPrevious->first))
637  {
638  linkIter = _gtLinkItems.find(iter->first);
639  while(linkIter.key() == iterPrevious->first && linkIter != _gtLinkItems.end())
640  {
641  if(linkIter.value()->to() == iter->first)
642  {
643  linkIter.value()->setPoses(previousPose, currentPose);
644  linkIter.value()->show();
645  linkItem = linkIter.value();
646  break;
647  }
648  ++linkIter;
649  }
650  }
651  if(linkItem == 0)
652  {
653  //create a link item
654  linkItem = new LinkItem(iterPrevious->first, iter->first, previousPose, currentPose, Link(), 1);
655  QPen p = linkItem->pen();
656  p.setWidthF(_linkWidth*100.0f);
657  linkItem->setPen(p);
658  linkItem->setZValue(10);
659  this->scene()->addItem(linkItem);
660  linkItem->setParentItem(_gtGraphRoot);
661  _gtLinkItems.insert(iterPrevious->first, linkItem);
662  }
663  if(linkItem)
664  {
665  linkItem->setColor(_gtPathColor);
666  }
667  }
668  }
669  }
670 
671  //remove not used nodes and links
672  for(QMap<int, NodeItem*>::iterator iter = _gtNodeItems.begin(); iter!=_gtNodeItems.end();)
673  {
674  if(!iter.value()->isVisible())
675  {
676  delete iter.value();
677  iter = _gtNodeItems.erase(iter);
678  }
679  else
680  {
681  ++iter;
682  }
683  }
684  for(QMultiMap<int, LinkItem*>::iterator iter = _gtLinkItems.begin(); iter!=_gtLinkItems.end();)
685  {
686  if(!iter.value()->isVisible())
687  {
688  delete iter.value();
689  iter = _gtLinkItems.erase(iter);
690  }
691  else
692  {
693  ++iter;
694  }
695  }
696 
697  if(_gtNodeItems.size() || _gtLinkItems.size())
698  {
699  this->scene()->setSceneRect(this->scene()->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
700 
701  if(wasEmpty)
702  {
703  QRectF rect = this->scene()->itemsBoundingRect();
704  this->fitInView(rect.adjusted(-rect.width()/2.0f, -rect.height()/2.0f, rect.width()/2.0f, rect.height()/2.0f), Qt::KeepAspectRatio);
705  }
706  }
707 
708  _gtGraphRoot->setVisible(wasVisible);
709 
710  UDEBUG("_gtNodeItems=%d, _gtLinkItems=%d timer=%fs", _gtNodeItems.size(), _gtLinkItems.size(), timer.ticks());
711 }
712 
714  const std::map<int, Transform> & poses,
715  const std::map<int, GPS> & gpsValues)
716 {
717  UTimer timer;
718  bool wasVisible = _gpsGraphRoot->isVisible();
719  _gpsGraphRoot->show();
720  bool wasEmpty = _gpsNodeItems.size() == 0 && _gpsNodeItems.size() == 0;
721  UDEBUG("poses=%d", (int)poses.size());
722  //Hide nodes and links
723  for(QMap<int, NodeItem*>::iterator iter = _gpsNodeItems.begin(); iter!=_gpsNodeItems.end(); ++iter)
724  {
725  iter.value()->hide();
726  iter.value()->setColor(_gpsPathColor); // reset color
727  }
728  for(QMultiMap<int, LinkItem*>::iterator iter = _gpsLinkItems.begin(); iter!=_gpsLinkItems.end(); ++iter)
729  {
730  iter.value()->hide();
731  }
732 
733  for(std::map<int, Transform>::const_iterator iter=poses.begin(); iter!=poses.end(); ++iter)
734  {
735  if(!iter->second.isNull())
736  {
737  QMap<int, NodeItem*>::iterator itemIter = _gpsNodeItems.find(iter->first);
738  if(itemIter != _gpsNodeItems.end())
739  {
740  itemIter.value()->setPose(iter->second);
741  itemIter.value()->show();
742  }
743  else
744  {
745  // create node item
746  const Transform & pose = iter->second;
747  UASSERT(gpsValues.find(iter->first) != gpsValues.end());
748  NodeItem * item = new NodeGPSItem(iter->first, -1, pose, _nodeRadius, gpsValues.at(iter->first));
749  this->scene()->addItem(item);
750  item->setZValue(20);
751  item->setColor(_gpsPathColor);
752  item->setParentItem(_gpsGraphRoot);
753  item->setVisible(_nodeVisible);
754  _gpsNodeItems.insert(iter->first, item);
755  }
756 
757  if(iter!=poses.begin())
758  {
759  std::map<int, Transform>::const_iterator iterPrevious = iter;
760  --iterPrevious;
761  Transform previousPose = iterPrevious->second;
762  Transform currentPose = iter->second;
763 
764  LinkItem * linkItem = 0;
765  QMultiMap<int, LinkItem*>::iterator linkIter = _gpsLinkItems.end();
766  if(_gpsLinkItems.contains(iterPrevious->first))
767  {
768  linkIter = _gpsLinkItems.find(iter->first);
769  while(linkIter.key() == iterPrevious->first && linkIter != _gpsLinkItems.end())
770  {
771  if(linkIter.value()->to() == iter->first)
772  {
773  linkIter.value()->setPoses(previousPose, currentPose);
774  linkIter.value()->show();
775  linkItem = linkIter.value();
776  break;
777  }
778  ++linkIter;
779  }
780  }
781  if(linkItem == 0)
782  {
783  //create a link item
784  linkItem = new LinkItem(iterPrevious->first, iter->first, previousPose, currentPose, Link(), 1);
785  QPen p = linkItem->pen();
786  p.setWidthF(_linkWidth*100.0f);
787  linkItem->setPen(p);
788  linkItem->setZValue(10);
789  this->scene()->addItem(linkItem);
790  linkItem->setParentItem(_gpsGraphRoot);
791  _gpsLinkItems.insert(iterPrevious->first, linkItem);
792  }
793  if(linkItem)
794  {
795  linkItem->setColor(_gpsPathColor);
796  }
797  }
798  }
799  }
800 
801  //remove not used nodes and links
802  for(QMap<int, NodeItem*>::iterator iter = _gpsNodeItems.begin(); iter!=_gpsNodeItems.end();)
803  {
804  if(!iter.value()->isVisible())
805  {
806  delete iter.value();
807  iter = _gpsNodeItems.erase(iter);
808  }
809  else
810  {
811  ++iter;
812  }
813  }
814  for(QMultiMap<int, LinkItem*>::iterator iter = _gpsLinkItems.begin(); iter!=_gpsLinkItems.end();)
815  {
816  if(!iter.value()->isVisible())
817  {
818  delete iter.value();
819  iter = _gpsLinkItems.erase(iter);
820  }
821  else
822  {
823  ++iter;
824  }
825  }
826 
827  if(_gpsNodeItems.size() || _gpsLinkItems.size())
828  {
829  this->scene()->setSceneRect(this->scene()->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
830 
831  if(wasEmpty)
832  {
833  QRectF rect = this->scene()->itemsBoundingRect();
834  this->fitInView(rect.adjusted(-rect.width()/2.0f, -rect.height()/2.0f, rect.width()/2.0f, rect.height()/2.0f), Qt::KeepAspectRatio);
835  }
836  }
837 
838  _gpsGraphRoot->setVisible(wasVisible);
839 
840  UDEBUG("_gpsNodeItems=%d, _gpsLinkItems=%d timer=%fs", _gpsNodeItems.size(), _gpsLinkItems.size(), timer.ticks());
841 }
842 
844 {
845  QTransform qt;
846  qt.translate(-t.o24()*100.0f, -t.o14()*100.0f);
847  qt.rotateRadians(-t.theta());
848 
849  _referential->setTransform(qt);
850  _localRadius->setTransform(qt);
851 
852  this->ensureVisible(_referential);
853  if(_localRadius->isVisible())
854  {
855  this->ensureVisible(_localRadius, 0, 0);
856  }
857 }
858 
859 void GraphViewer::updateMap(const cv::Mat & map8U, float resolution, float xMin, float yMin)
860 {
861  UASSERT(map8U.empty() || (!map8U.empty() && resolution > 0.0f));
862  if(!map8U.empty())
863  {
864  _gridCellSize = resolution;
865  QImage image = uCvMat2QImage(map8U, false);
866  _gridMap->resetTransform();
867  _gridMap->setTransform(QTransform::fromScale(resolution*100.0f, -resolution*100.0f), true);
868  _gridMap->setRotation(90);
869  _gridMap->setPixmap(QPixmap::fromImage(image));
870  _gridMap->setPos(-yMin*100.0f, -xMin*100.0f);
871  this->scene()->setSceneRect(this->scene()->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
872  }
873  else
874  {
875  this->clearMap();
876  }
877 }
878 
879 void GraphViewer::updatePosterior(const std::map<int, float> & posterior, float max, int zValueOffset)
880 {
881  //find max
882  if(max <= 0.0f)
883  {
884  for(std::map<int, float>::const_iterator iter = posterior.begin(); iter!=posterior.end(); ++iter)
885  {
886  if(iter->first > 0 && iter->second>max)
887  {
888  max = iter->second;
889  }
890  }
891  }
892  if(max > 0.0f)
893  {
894  for(QMap<int, NodeItem*>::iterator iter = _nodeItems.begin(); iter!=_nodeItems.end(); ++iter)
895  {
896  std::map<int,float>::const_iterator jter = posterior.find(iter.key());
897  if(jter != posterior.end())
898  {
899  float v = jter->second>max?max:jter->second;
900  iter.value()->setColor(QColor::fromHsvF((1-v/max)*240.0f/360.0f, 1, 1, 1)); //0=red 240=blue
901  iter.value()->setZValue(iter.value()->zValue()+zValueOffset);
902  }
903  else if(iter.key() > 0)
904  {
905  iter.value()->setColor(QColor::fromHsvF(240.0f/360.0f, 1, 1, 1)); // blue
906  }
907  }
908  }
909 }
910 
911 void GraphViewer::setGlobalPath(const std::vector<std::pair<int, Transform> > & globalPath)
912 {
913  UDEBUG("Set global path size=%d", (int)globalPath.size());
914  qDeleteAll(_globalPathLinkItems);
915  _globalPathLinkItems.clear();
916 
917  if(globalPath.size() >= 2)
918  {
919  for(unsigned int i=0; i<globalPath.size()-1; ++i)
920  {
921  //create a link item
922  int idFrom = globalPath[i].first;
923  int idTo = globalPath[i+1].first;
924  LinkItem * item = new LinkItem(idFrom, idTo, globalPath[i].second, globalPath[i+1].second, Link(), false);
925  QPen p = item->pen();
926  p.setWidthF(_linkWidth*100.0f);
927  item->setPen(p);
928  item->setColor(_globalPathColor);
929  this->scene()->addItem(item);
930  item->setZValue(15);
931  item->setParentItem(_globalPathRoot);
932  _globalPathLinkItems.insert(idFrom, item);
933  }
934  }
935 }
936 
937 void GraphViewer::setCurrentGoalID(int id, const Transform & pose)
938 {
939  NodeItem * node = _nodeItems.value(id, 0);
940  if(node)
941  {
943  }
944  else
945  {
946  UWARN("Current goal %d not found in the graph", id);
947  }
948 
949  if(!pose.isNull() && _globalPathLinkItems.size() && _globalPathLinkItems.contains(id))
950  {
951  // transform the global path in the goal referential
952  const LinkItem * oldPose = _globalPathLinkItems.value(id);
953  Transform t = pose * oldPose->getPoseA().inverse();
954  for(QMultiMap<int, LinkItem*>::iterator iter=_globalPathLinkItems.begin(); iter!=_globalPathLinkItems.end(); ++iter)
955  {
956  iter.value()->setPoses(t*iter.value()->getPoseA(), t*iter.value()->getPoseB());
957  }
958  }
959 }
960 
961 void GraphViewer::setLocalRadius(float radius)
962 {
963  _localRadius->setRect(-radius*100, -radius*100, radius*200, radius*200);
964 }
965 
966 void GraphViewer::updateLocalPath(const std::vector<int> & localPath)
967 {
968  bool wasVisible = _localPathRoot->isVisible();
969  _localPathRoot->show();
970 
971  for(QMultiMap<int, LinkItem*>::iterator iter = _localPathLinkItems.begin(); iter!=_localPathLinkItems.end(); ++iter)
972  {
973  iter.value()->hide();
974  }
975 
976  if(localPath.size() > 1)
977  {
978  for(unsigned int i=0; i<localPath.size()-1; ++i)
979  {
980  int idFrom = localPath[i]<localPath[i+1]?localPath[i]:localPath[i+1];
981  int idTo = localPath[i]<localPath[i+1]?localPath[i+1]:localPath[i];
982  if(_nodeItems.contains(idFrom) && _nodeItems.contains(idTo))
983  {
984  bool updated = false;
985  if(_localPathLinkItems.contains(idFrom))
986  {
987  QMultiMap<int, LinkItem*>::iterator itemIter = _localPathLinkItems.find(idFrom);
988  while(itemIter.key() == idFrom && itemIter != _localPathLinkItems.end())
989  {
990  if(itemIter.value()->to() == idTo)
991  {
992  itemIter.value()->setPoses(_nodeItems.value(idFrom)->pose(), _nodeItems.value(idTo)->pose());
993  itemIter.value()->show();
994  updated = true;
995  break;
996  }
997  ++itemIter;
998  }
999  }
1000  if(!updated)
1001  {
1002  //create a link item
1003  LinkItem * item = new LinkItem(idFrom, idTo, _nodeItems.value(idFrom)->pose(), _nodeItems.value(idTo)->pose(), Link(), false);
1004  QPen p = item->pen();
1005  p.setWidthF(_linkWidth*100.0f);
1006  item->setPen(p);
1007  item->setColor(_localPathColor);
1008  this->scene()->addItem(item);
1009  item->setZValue(16); // just over the global path
1010  item->setParentItem(_localPathRoot);
1011  _localPathLinkItems.insert(idFrom, item);
1012  }
1013  }
1014  }
1015  }
1016 
1017  // remove not used links
1018  for(QMultiMap<int, LinkItem*>::iterator iter = _localPathLinkItems.begin(); iter!=_localPathLinkItems.end();)
1019  {
1020  if(!iter.value()->isVisible())
1021  {
1022  delete iter.value();
1023  iter = _localPathLinkItems.erase(iter);
1024  }
1025  else
1026  {
1027  ++iter;
1028  }
1029  }
1030  _localPathRoot->setVisible(wasVisible);
1031 }
1032 
1034 {
1035  qDeleteAll(_nodeItems);
1036  _nodeItems.clear();
1037  qDeleteAll(_linkItems);
1038  _linkItems.clear();
1039  qDeleteAll(_localPathLinkItems);
1040  _localPathLinkItems.clear();
1041  qDeleteAll(_globalPathLinkItems);
1042  _globalPathLinkItems.clear();
1043  qDeleteAll(_gtNodeItems);
1044  _gtNodeItems.clear();
1045  qDeleteAll(_gtLinkItems);
1046  _gtLinkItems.clear();
1047  qDeleteAll(_gpsNodeItems);
1048  _gpsNodeItems.clear();
1049  qDeleteAll(_gpsLinkItems);
1050  _gpsLinkItems.clear();
1051 
1052  _root->resetTransform();
1053  _worldMapRotation = 0.0f;
1054  _referential->resetTransform();
1055  _localRadius->resetTransform();
1056  this->scene()->setSceneRect(this->scene()->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
1057 }
1058 
1060 {
1061  _gridMap->setPixmap(QPixmap());
1062  _gridCellSize = 0.0f;
1063  this->scene()->setSceneRect(this->scene()->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
1064 }
1065 
1067 {
1068  for(QMap<int, NodeItem*>::iterator iter = _nodeItems.begin(); iter!=_nodeItems.end(); ++iter)
1069  {
1070  iter.value()->setColor(Qt::blue); // blue
1071  }
1072 }
1073 
1075 {
1076  clearMap();
1077  clearGraph();
1078 }
1079 
1080 void GraphViewer::saveSettings(QSettings & settings, const QString & group) const
1081 {
1082  if(!group.isEmpty())
1083  {
1084  settings.beginGroup(group);
1085  }
1086  settings.setValue("node_radius", (double)this->getNodeRadius());
1087  settings.setValue("link_width", (double)this->getLinkWidth());
1088  settings.setValue("node_color", this->getNodeColor());
1089  settings.setValue("current_goal_color", this->getCurrentGoalColor());
1090  settings.setValue("neighbor_color", this->getNeighborColor());
1091  settings.setValue("global_color", this->getGlobalLoopClosureColor());
1092  settings.setValue("local_color", this->getLocalLoopClosureColor());
1093  settings.setValue("user_color", this->getUserLoopClosureColor());
1094  settings.setValue("virtual_color", this->getVirtualLoopClosureColor());
1095  settings.setValue("neighbor_merged_color", this->getNeighborMergedColor());
1096  settings.setValue("rejected_color", this->getRejectedLoopClosureColor());
1097  settings.setValue("local_path_color", this->getLocalPathColor());
1098  settings.setValue("global_path_color", this->getGlobalPathColor());
1099  settings.setValue("gt_color", this->getGTColor());
1100  settings.setValue("gps_color", this->getGPSColor());
1101  settings.setValue("intra_session_color", this->getIntraSessionLoopColor());
1102  settings.setValue("inter_session_color", this->getInterSessionLoopColor());
1103  settings.setValue("intra_inter_session_colors_enabled", this->isIntraInterSessionColorsEnabled());
1104  settings.setValue("grid_visible", this->isGridMapVisible());
1105  settings.setValue("origin_visible", this->isOriginVisible());
1106  settings.setValue("referential_visible", this->isReferentialVisible());
1107  settings.setValue("local_radius_visible", this->isLocalRadiusVisible());
1108  settings.setValue("loop_closure_outlier_thr", this->getLoopClosureOutlierThr());
1109  settings.setValue("max_link_length", this->getMaxLinkLength());
1110  settings.setValue("graph_visible", this->isGraphVisible());
1111  settings.setValue("global_path_visible", this->isGlobalPathVisible());
1112  settings.setValue("local_path_visible", this->isLocalPathVisible());
1113  settings.setValue("gt_graph_visible", this->isGtGraphVisible());
1114  settings.setValue("gps_graph_visible", this->isGPSGraphVisible());
1115  settings.setValue("orientation_ENU", this->isOrientationENU());
1116  if(!group.isEmpty())
1117  {
1118  settings.endGroup();
1119  }
1120 }
1121 
1122 void GraphViewer::loadSettings(QSettings & settings, const QString & group)
1123 {
1124  if(!group.isEmpty())
1125  {
1126  settings.beginGroup(group);
1127  }
1128  this->setNodeRadius(settings.value("node_radius", this->getNodeRadius()).toDouble());
1129  this->setLinkWidth(settings.value("link_width", this->getLinkWidth()).toDouble());
1130  this->setNodeColor(settings.value("node_color", this->getNodeColor()).value<QColor>());
1131  this->setCurrentGoalColor(settings.value("current_goal_color", this->getCurrentGoalColor()).value<QColor>());
1132  this->setNeighborColor(settings.value("neighbor_color", this->getNeighborColor()).value<QColor>());
1133  this->setGlobalLoopClosureColor(settings.value("global_color", this->getGlobalLoopClosureColor()).value<QColor>());
1134  this->setLocalLoopClosureColor(settings.value("local_color", this->getLocalLoopClosureColor()).value<QColor>());
1135  this->setUserLoopClosureColor(settings.value("user_color", this->getUserLoopClosureColor()).value<QColor>());
1136  this->setVirtualLoopClosureColor(settings.value("virtual_color", this->getVirtualLoopClosureColor()).value<QColor>());
1137  this->setNeighborMergedColor(settings.value("neighbor_merged_color", this->getNeighborMergedColor()).value<QColor>());
1138  this->setRejectedLoopClosureColor(settings.value("rejected_color", this->getRejectedLoopClosureColor()).value<QColor>());
1139  this->setLocalPathColor(settings.value("local_path_color", this->getLocalPathColor()).value<QColor>());
1140  this->setGlobalPathColor(settings.value("global_path_color", this->getGlobalPathColor()).value<QColor>());
1141  this->setGTColor(settings.value("gt_color", this->getGTColor()).value<QColor>());
1142  this->setGPSColor(settings.value("gps_color", this->getGPSColor()).value<QColor>());
1143  this->setIntraSessionLoopColor(settings.value("intra_session_color", this->getIntraSessionLoopColor()).value<QColor>());
1144  this->setInterSessionLoopColor(settings.value("inter_session_color", this->getInterSessionLoopColor()).value<QColor>());
1145  this->setGridMapVisible(settings.value("grid_visible", this->isGridMapVisible()).toBool());
1146  this->setOriginVisible(settings.value("origin_visible", this->isOriginVisible()).toBool());
1147  this->setReferentialVisible(settings.value("referential_visible", this->isReferentialVisible()).toBool());
1148  this->setLocalRadiusVisible(settings.value("local_radius_visible", this->isLocalRadiusVisible()).toBool());
1149  this->setIntraInterSessionColorsEnabled(settings.value("intra_inter_session_colors_enabled", this->isIntraInterSessionColorsEnabled()).toBool());
1150  this->setLoopClosureOutlierThr(settings.value("loop_closure_outlier_thr", this->getLoopClosureOutlierThr()).toDouble());
1151  this->setMaxLinkLength(settings.value("max_link_length", this->getMaxLinkLength()).toDouble());
1152  this->setGraphVisible(settings.value("graph_visible", this->isGraphVisible()).toBool());
1153  this->setGlobalPathVisible(settings.value("global_path_visible", this->isGlobalPathVisible()).toBool());
1154  this->setLocalPathVisible(settings.value("local_path_visible", this->isLocalPathVisible()).toBool());
1155  this->setGtGraphVisible(settings.value("gt_graph_visible", this->isGtGraphVisible()).toBool());
1156  this->setGPSGraphVisible(settings.value("gps_graph_visible", this->isGPSGraphVisible()).toBool());
1157  this->setOrientationENU(settings.value("orientation_ENU", this->isOrientationENU()).toBool());
1158  if(!group.isEmpty())
1159  {
1160  settings.endGroup();
1161  }
1162 }
1163 
1165 {
1166  return _gridMap->isVisible();
1167 }
1169 {
1170  return _originReferential->isVisible();
1171 }
1173 {
1174  return _referential->isVisible();
1175 }
1177 {
1178  return _localRadius->isVisible();
1179 }
1181 {
1182  return _graphRoot->isVisible();
1183 }
1185 {
1186  return _globalPathRoot->isVisible();
1187 }
1189 {
1190  return _localPathRoot->isVisible();
1191 }
1193 {
1194  return _gtGraphRoot->isVisible();
1195 }
1197 {
1198  return _gpsGraphRoot->isVisible();
1199 }
1201 {
1202  return _orientationENU;
1203 }
1204 
1205 void GraphViewer::setWorkingDirectory(const QString & path)
1206 {
1207  _workingDirectory = path;
1208 }
1210 {
1211  _nodeVisible = visible;
1212  for(QMap<int, NodeItem*>::iterator iter=_nodeItems.begin(); iter!=_nodeItems.end(); ++iter)
1213  {
1214  iter.value()->setVisible(_nodeVisible);
1215  }
1216  for(QMap<int, NodeItem*>::iterator iter=_gtNodeItems.begin(); iter!=_gtNodeItems.end(); ++iter)
1217  {
1218  iter.value()->setVisible(_nodeVisible);
1219  }
1220  for(QMap<int, NodeItem*>::iterator iter=_gpsNodeItems.begin(); iter!=_gpsNodeItems.end(); ++iter)
1221  {
1222  iter.value()->setVisible(_nodeVisible);
1223  }
1224 }
1225 void GraphViewer::setNodeRadius(float radius)
1226 {
1227  _nodeRadius = radius;
1228  for(QMap<int, NodeItem*>::iterator iter=_nodeItems.begin(); iter!=_nodeItems.end(); ++iter)
1229  {
1230  iter.value()->setRadius(_nodeRadius);
1231  }
1232  for(QMap<int, NodeItem*>::iterator iter=_gtNodeItems.begin(); iter!=_gtNodeItems.end(); ++iter)
1233  {
1234  iter.value()->setRadius(_nodeRadius);
1235  }
1236  for(QMap<int, NodeItem*>::iterator iter=_gpsNodeItems.begin(); iter!=_gpsNodeItems.end(); ++iter)
1237  {
1238  iter.value()->setRadius(_nodeRadius);
1239  }
1240 }
1241 void GraphViewer::setLinkWidth(float width)
1242 {
1243  _linkWidth = width;
1244  QList<QGraphicsItem*> items = this->scene()->items();
1245  for(int i=0; i<items.size(); ++i)
1246  {
1247  QGraphicsLineItem * line = qgraphicsitem_cast<QGraphicsLineItem *>(items[i]);
1248  if(line)
1249  {
1250  QPen pen = line->pen();
1251  pen.setWidthF(_linkWidth*100.0f);
1252  line->setPen(pen);
1253  }
1254  }
1255 }
1256 void GraphViewer::setNodeColor(const QColor & color)
1257 {
1258  _nodeColor = color;
1259  for(QMap<int, NodeItem*>::iterator iter=_nodeItems.begin(); iter!=_nodeItems.end(); ++iter)
1260  {
1261  iter.value()->setColor(_nodeColor);
1262  }
1263 }
1264 void GraphViewer::setCurrentGoalColor(const QColor & color)
1265 {
1266  _currentGoalColor = color;
1267 }
1268 void GraphViewer::setNeighborColor(const QColor & color)
1269 {
1270  _neighborColor = color;
1271  for(QMultiMap<int, LinkItem*>::iterator iter=_linkItems.begin(); iter!=_linkItems.end(); ++iter)
1272  {
1273  if(iter.value()->linkType() == Link::kNeighbor)
1274  {
1275  iter.value()->setColor(_neighborColor);
1276  }
1277  }
1278 }
1279 void GraphViewer::setGlobalLoopClosureColor(const QColor & color)
1280 {
1281  _loopClosureColor = color;
1283  {
1284  for(QMultiMap<int, LinkItem*>::iterator iter=_linkItems.begin(); iter!=_linkItems.end(); ++iter)
1285  {
1286  if(iter.value()->linkType() == Link::kGlobalClosure)
1287  {
1288  iter.value()->setColor(_loopClosureColor);
1289  iter.value()->setZValue(10);
1290  }
1291  }
1292  }
1293 }
1294 void GraphViewer::setLocalLoopClosureColor(const QColor & color)
1295 {
1296  _loopClosureLocalColor = color;
1298  {
1299  for(QMultiMap<int, LinkItem*>::iterator iter=_linkItems.begin(); iter!=_linkItems.end(); ++iter)
1300  {
1301  if(iter.value()->linkType() == Link::kLocalSpaceClosure ||
1302  iter.value()->linkType() == Link::kLocalTimeClosure)
1303  {
1304  iter.value()->setColor(_loopClosureLocalColor);
1305  iter.value()->setZValue(10);
1306  }
1307  }
1308  }
1309 }
1310 void GraphViewer::setUserLoopClosureColor(const QColor & color)
1311 {
1312  _loopClosureUserColor = color;
1313  for(QMultiMap<int, LinkItem*>::iterator iter=_linkItems.begin(); iter!=_linkItems.end(); ++iter)
1314  {
1315  if(iter.value()->linkType() == Link::kUserClosure)
1316  {
1317  iter.value()->setColor(_loopClosureUserColor);
1318  }
1319  }
1320 }
1322 {
1323  _loopClosureVirtualColor = color;
1324  for(QMultiMap<int, LinkItem*>::iterator iter=_linkItems.begin(); iter!=_linkItems.end(); ++iter)
1325  {
1326  if(iter.value()->linkType() == Link::kVirtualClosure)
1327  {
1328  iter.value()->setColor(_loopClosureVirtualColor);
1329  }
1330  }
1331 }
1332 void GraphViewer::setNeighborMergedColor(const QColor & color)
1333 {
1334  _neighborMergedColor = color;
1335  for(QMultiMap<int, LinkItem*>::iterator iter=_linkItems.begin(); iter!=_linkItems.end(); ++iter)
1336  {
1337  if(iter.value()->linkType() == Link::kNeighborMerged)
1338  {
1339  iter.value()->setColor(_neighborMergedColor);
1340  }
1341  }
1342 }
1343 void GraphViewer::setLandmarkColor(const QColor & color)
1344 {
1345  _landmarkColor = color;
1346  for(QMultiMap<int, LinkItem*>::iterator iter=_linkItems.begin(); iter!=_linkItems.end(); ++iter)
1347  {
1348  if(iter.value()->linkType() == Link::kLandmark)
1349  {
1350  iter.value()->setColor(_landmarkColor);
1351  }
1352  }
1353 }
1355 {
1356  _loopClosureRejectedColor = color;
1357 }
1358 void GraphViewer::setLocalPathColor(const QColor & color)
1359 {
1360  _localPathColor = color;
1361 }
1362 void GraphViewer::setGlobalPathColor(const QColor & color)
1363 {
1364  _globalPathColor = color;
1365 }
1366 void GraphViewer::setGTColor(const QColor & color)
1367 {
1368  _gtPathColor = color;
1369  for(QMap<int, NodeItem*>::iterator iter=_gtNodeItems.begin(); iter!=_gtNodeItems.end(); ++iter)
1370  {
1371  iter.value()->setColor(_gtPathColor);
1372  }
1373  for(QMultiMap<int, LinkItem*>::iterator iter=_gtLinkItems.begin(); iter!=_gtLinkItems.end(); ++iter)
1374  {
1375  iter.value()->setColor(_gtPathColor);
1376  }
1377 }
1378 void GraphViewer::setGPSColor(const QColor & color)
1379 {
1380  _gpsPathColor = color;
1381  for(QMap<int, NodeItem*>::iterator iter=_gpsNodeItems.begin(); iter!=_gpsNodeItems.end(); ++iter)
1382  {
1383  iter.value()->setColor(_gpsPathColor);
1384  }
1385  for(QMultiMap<int, LinkItem*>::iterator iter=_gpsLinkItems.begin(); iter!=_gpsLinkItems.end(); ++iter)
1386  {
1387  iter.value()->setColor(_gpsPathColor);
1388  }
1389 }
1390 void GraphViewer::setIntraSessionLoopColor(const QColor & color)
1391 {
1392  _loopIntraSessionColor = color;
1394  {
1395  for(QMultiMap<int, LinkItem*>::iterator iter=_linkItems.begin(); iter!=_linkItems.end(); ++iter)
1396  {
1397  if((iter.value()->linkType() == Link::kGlobalClosure ||
1398  iter.value()->linkType() == Link::kLocalSpaceClosure ||
1399  iter.value()->linkType() == Link::kLocalTimeClosure) &&
1400  !iter.value()->isInterSession())
1401  {
1402  iter.value()->setColor(_loopIntraSessionColor);
1403  iter.value()->setZValue(9);
1404  }
1405  }
1406  }
1407 }
1408 void GraphViewer::setInterSessionLoopColor(const QColor & color)
1409 {
1410  _loopInterSessionColor = color;
1412  {
1413  for(QMultiMap<int, LinkItem*>::iterator iter=_linkItems.begin(); iter!=_linkItems.end(); ++iter)
1414  {
1415  if((iter.value()->linkType() == Link::kGlobalClosure ||
1416  iter.value()->linkType() == Link::kLocalSpaceClosure ||
1417  iter.value()->linkType() == Link::kLocalTimeClosure) &&
1418  iter.value()->isInterSession())
1419  {
1420  iter.value()->setColor(_loopInterSessionColor);
1421  iter.value()->setZValue(8);
1422  }
1423  }
1424  }
1425 }
1426 
1428 {
1429  _intraInterSessionColors = enabled;
1431  {
1434  }
1435  else
1436  {
1439  }
1440 }
1441 
1443 {
1444  _gridMap->setVisible(visible);
1445 }
1447 {
1448  _originReferential->setVisible(visible);
1449 }
1451 {
1452  _referential->setVisible(visible);
1453 }
1455 {
1456  _localRadius->setVisible(visible);
1457 }
1459 {
1460  _loopClosureOutlierThr = value;
1461 }
1463 {
1464  _maxLinkLength = value;
1465 }
1467 {
1468  _graphRoot->setVisible(visible);
1469 }
1471 {
1472  _globalPathRoot->setVisible(visible);
1473 }
1475 {
1476  _localPathRoot->setVisible(visible);
1477 }
1479 {
1480  _gtGraphRoot->setVisible(visible);
1481 }
1483 {
1484  _gpsGraphRoot->setVisible(visible);
1485 }
1487 {
1488  if(_orientationENU!=enabled)
1489  {
1490  _orientationENU = enabled;
1491  this->rotate(_orientationENU?90:270);
1492  }
1493  if(_orientationENU)
1494  {
1495  QTransform t;
1496  t.rotateRadians(_worldMapRotation);
1497  _root->setTransform(t);
1498  }
1499  else
1500  {
1501  _root->resetTransform();
1502  }
1503  if(_nodeItems.size() || _linkItems.size())
1504  {
1505  this->scene()->setSceneRect(this->scene()->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
1506  }
1507 }
1508 
1510 {
1511  setNodeRadius(0.01f);
1512  setLinkWidth(0.0f);
1513  setNodeColor(Qt::blue);
1514  setNeighborColor(Qt::blue);
1515  setGlobalLoopClosureColor(Qt::red);
1516  setLocalLoopClosureColor(Qt::yellow);
1517  setUserLoopClosureColor(Qt::red);
1518  setVirtualLoopClosureColor(Qt::magenta);
1519  setNeighborMergedColor(QColor(255,170,0));
1520  setLandmarkColor(Qt::darkGreen);
1521  setGridMapVisible(true);
1522  setGraphVisible(true);
1523  setGlobalPathVisible(true);
1524  setLocalPathVisible(true);
1525  setGtGraphVisible(true);
1526 }
1527 
1528 void GraphViewer::wheelEvent ( QWheelEvent * event )
1529 {
1530  if(event->delta() < 0)
1531  {
1532  this->scale(0.95, 0.95);
1533  }
1534  else
1535  {
1536  this->scale(1.05, 1.05);
1537  }
1538 }
1539 
1540 QIcon createIcon(const QColor & color)
1541 {
1542  QPixmap pixmap(50, 50);
1543  pixmap.fill(color);
1544  return QIcon(pixmap);
1545 }
1546 
1547 void GraphViewer::contextMenuEvent(QContextMenuEvent * event)
1548 {
1549  QMenu menu;
1550  QAction * aScreenShot = menu.addAction(tr("Take a screenshot..."));
1551  menu.addSeparator();
1552 
1553  QAction * aChangeNodeColor = menu.addAction(createIcon(_nodeColor), tr("Set node color..."));
1554  QAction * aChangeCurrentGoalColor = menu.addAction(createIcon(_currentGoalColor), tr("Set current goal color..."));
1555  aChangeNodeColor->setIconVisibleInMenu(true);
1556  aChangeCurrentGoalColor->setIconVisibleInMenu(true);
1557 
1558  // Links
1559  QMenu * menuLink = menu.addMenu(tr("Set link color..."));
1560  QAction * aChangeNeighborColor = menuLink->addAction(tr("Neighbor"));
1561  QAction * aChangeGlobalLoopColor = menuLink->addAction(tr("Global loop closure"));
1562  QAction * aChangeLocalLoopColor = menuLink->addAction(tr("Local loop closure"));
1563  QAction * aChangeUserLoopColor = menuLink->addAction(tr("User loop closure"));
1564  QAction * aChangeVirtualLoopColor = menuLink->addAction(tr("Virtual loop closure"));
1565  QAction * aChangeNeighborMergedColor = menuLink->addAction(tr("Neighbor merged"));
1566  QAction * aChangeLandmarkColor = menuLink->addAction(tr("Landmark"));
1567  QAction * aChangeRejectedLoopColor = menuLink->addAction(tr("Outlier loop closure"));
1568  QAction * aChangeRejectedLoopThr = menuLink->addAction(tr("Set outlier threshold..."));
1569  QAction * aChangeLocalPathColor = menuLink->addAction(tr("Local path"));
1570  QAction * aChangeGlobalPathColor = menuLink->addAction(tr("Global path"));
1571  QAction * aChangeGTColor = menuLink->addAction(tr("Ground truth"));
1572  QAction * aChangeGPSColor = menuLink->addAction(tr("GPS"));
1573  menuLink->addSeparator();
1574  QAction * aSetIntraInterSessionColors = menuLink->addAction(tr("Enable intra/inter-session colors"));
1575  QAction * aChangeIntraSessionLoopColor = menuLink->addAction(tr("Intra-session loop closure"));
1576  QAction * aChangeInterSessionLoopColor = menuLink->addAction(tr("Inter-session loop closure"));
1577  aChangeNeighborColor->setIcon(createIcon(_neighborColor));
1578  aChangeGlobalLoopColor->setIcon(createIcon(_loopClosureColor));
1579  aChangeLocalLoopColor->setIcon(createIcon(_loopClosureLocalColor));
1580  aChangeUserLoopColor->setIcon(createIcon(_loopClosureUserColor));
1581  aChangeVirtualLoopColor->setIcon(createIcon(_loopClosureVirtualColor));
1582  aChangeNeighborMergedColor->setIcon(createIcon(_neighborMergedColor));
1583  aChangeLandmarkColor->setIcon(createIcon(_landmarkColor));
1584  aChangeRejectedLoopColor->setIcon(createIcon(_loopClosureRejectedColor));
1585  aChangeLocalPathColor->setIcon(createIcon(_localPathColor));
1586  aChangeGlobalPathColor->setIcon(createIcon(_globalPathColor));
1587  aChangeGTColor->setIcon(createIcon(_gtPathColor));
1588  aChangeGPSColor->setIcon(createIcon(_gpsPathColor));;
1589  aChangeIntraSessionLoopColor->setIcon(createIcon(_loopIntraSessionColor));
1590  aChangeInterSessionLoopColor->setIcon(createIcon(_loopInterSessionColor));
1591  aChangeNeighborColor->setIconVisibleInMenu(true);
1592  aChangeGlobalLoopColor->setIconVisibleInMenu(true);
1593  aChangeLocalLoopColor->setIconVisibleInMenu(true);
1594  aChangeUserLoopColor->setIconVisibleInMenu(true);
1595  aChangeVirtualLoopColor->setIconVisibleInMenu(true);
1596  aChangeNeighborMergedColor->setIconVisibleInMenu(true);
1597  aChangeRejectedLoopColor->setIconVisibleInMenu(true);
1598  aChangeLocalPathColor->setIconVisibleInMenu(true);
1599  aChangeGlobalPathColor->setIconVisibleInMenu(true);
1600  aChangeGTColor->setIconVisibleInMenu(true);
1601  aChangeGPSColor->setIconVisibleInMenu(true);
1602  aChangeIntraSessionLoopColor->setIconVisibleInMenu(true);
1603  aChangeInterSessionLoopColor->setIconVisibleInMenu(true);
1604  aSetIntraInterSessionColors->setCheckable(true);
1605  aSetIntraInterSessionColors->setChecked(_intraInterSessionColors);
1606 
1607  menu.addSeparator();
1608  QAction * aSetNodeSize = menu.addAction(tr("Set node radius..."));
1609  QAction * aSetLinkSize = menu.addAction(tr("Set link width..."));
1610  QAction * aChangeMaxLinkLength = menu.addAction(tr("Set maximum link length..."));
1611  menu.addSeparator();
1612  QAction * aShowHideGridMap;
1613  QAction * aShowHideGraph;
1614  QAction * aShowHideGraphNodes;
1615  QAction * aShowHideOrigin;
1616  QAction * aShowHideReferential;
1617  QAction * aShowHideLocalRadius;
1618  QAction * aShowHideGlobalPath;
1619  QAction * aShowHideLocalPath;
1620  QAction * aShowHideGtGraph;
1621  QAction * aShowHideGPSGraph;
1622  QAction * aOrientationENU;
1623  if(_gridMap->isVisible())
1624  {
1625  aShowHideGridMap = menu.addAction(tr("Hide grid map"));
1626  }
1627  else
1628  {
1629  aShowHideGridMap = menu.addAction(tr("Show grid map"));
1630  }
1631  if(_originReferential->isVisible())
1632  {
1633  aShowHideOrigin = menu.addAction(tr("Hide origin referential"));
1634  }
1635  else
1636  {
1637  aShowHideOrigin = menu.addAction(tr("Show origin referential"));
1638  }
1639  if(_referential->isVisible())
1640  {
1641  aShowHideReferential = menu.addAction(tr("Hide current referential"));
1642  }
1643  else
1644  {
1645  aShowHideReferential = menu.addAction(tr("Show current referential"));
1646  }
1647  if(_localRadius->isVisible())
1648  {
1649  aShowHideLocalRadius = menu.addAction(tr("Hide local radius"));
1650  }
1651  else
1652  {
1653  aShowHideLocalRadius = menu.addAction(tr("Show local radius"));
1654  }
1655  if(_graphRoot->isVisible())
1656  {
1657  aShowHideGraph = menu.addAction(tr("Hide graph"));
1658  }
1659  else
1660  {
1661  aShowHideGraph = menu.addAction(tr("Show graph"));
1662  }
1663  if(_nodeVisible)
1664  {
1665  aShowHideGraphNodes = menu.addAction(tr("Hide graph nodes"));
1666  }
1667  else
1668  {
1669  aShowHideGraphNodes = menu.addAction(tr("Show graph nodes"));
1670  }
1671  if(_globalPathRoot->isVisible())
1672  {
1673  aShowHideGlobalPath = menu.addAction(tr("Hide global path"));
1674  }
1675  else
1676  {
1677  aShowHideGlobalPath = menu.addAction(tr("Show global path"));
1678  }
1679  if(_localPathRoot->isVisible())
1680  {
1681  aShowHideLocalPath = menu.addAction(tr("Hide local path"));
1682  }
1683  else
1684  {
1685  aShowHideLocalPath = menu.addAction(tr("Show local path"));
1686  }
1687  if(_gtGraphRoot->isVisible())
1688  {
1689  aShowHideGtGraph = menu.addAction(tr("Hide ground truth graph"));
1690  }
1691  else
1692  {
1693  aShowHideGtGraph = menu.addAction(tr("Show ground truth graph"));
1694  }
1695  if(_gpsGraphRoot->isVisible())
1696  {
1697  aShowHideGPSGraph = menu.addAction(tr("Hide GPS graph"));
1698  }
1699  else
1700  {
1701  aShowHideGPSGraph = menu.addAction(tr("Show GPS graph"));
1702  }
1703  aOrientationENU = menu.addAction(tr("ENU Orientation"));
1704  aOrientationENU->setCheckable(true);
1705  aOrientationENU->setChecked(_orientationENU);
1706  aShowHideGraph->setEnabled(_nodeItems.size());
1707  aShowHideGraphNodes->setEnabled(_nodeItems.size() && _graphRoot->isVisible());
1708  aShowHideGlobalPath->setEnabled(_globalPathLinkItems.size());
1709  aShowHideLocalPath->setEnabled(_localPathLinkItems.size());
1710  aShowHideGtGraph->setEnabled(_gtNodeItems.size());
1711  aShowHideGPSGraph->setEnabled(_gpsNodeItems.size());
1712  menu.addSeparator();
1713  QAction * aRestoreDefaults = menu.addAction(tr("Restore defaults"));
1714 
1715  QAction * r = menu.exec(event->globalPos());
1716  if(r == aScreenShot)
1717  {
1718  if(_root)
1719  {
1720  QString filePath;
1721 #if QT_VERSION >= 0x050000
1722  filePath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
1723 #endif
1724  QDir dir;
1725  if(!dir.exists(filePath))
1726  {
1727  filePath = QDir::homePath();
1728  }
1729  filePath += "/graph.png";
1730 
1731 #ifdef QT_SVG_LIB
1732  filePath = QFileDialog::getSaveFileName(this, tr("Save figure to ..."), filePath, "*.png *.xpm *.jpg *.pdf *.svg");
1733 #else
1734  filePath = QFileDialog::getSaveFileName(this, tr("Save figure to ..."), filePath, "*.png *.xpm *.jpg *.pdf");
1735 #endif
1736  if(!filePath.isEmpty())
1737  {
1738  if(QFileInfo(filePath).suffix() == "")
1739  {
1740  //use png by default
1741  filePath += ".png";
1742  }
1743 
1744  if(_gridCellSize)
1745  {
1746  _root->setScale(1.0f/(_gridCellSize*100.0f)); // grid map precision (for 5cm grid cell, x20 to have 1pix/5cm)
1747  }
1748  else
1749  {
1750  _root->setScale(this->transform().m11()); // current view
1751  }
1752 
1753  this->scene()->clearSelection(); // Selections would also render to the file
1754  this->scene()->setSceneRect(this->scene()->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
1755  QSize sceneSize = this->scene()->sceneRect().size().toSize();
1756 
1757  if(QFileInfo(filePath).suffix().compare("pdf") == 0)
1758  {
1759  QPrinter printer(QPrinter::HighResolution);
1760  printer.setOrientation(QPrinter::Portrait);
1761  printer.setOutputFileName( filePath );
1762  QPainter p(&printer);
1763  scene()->render(&p);
1764  p.end();
1765  }
1766  else if(QFileInfo(filePath).suffix().compare("svg") == 0)
1767  {
1768 #ifdef QT_SVG_LIB
1769  QSvgGenerator svgGen;
1770 
1771  svgGen.setFileName( filePath );
1772  svgGen.setSize(sceneSize);
1773  // add 1% border to make sure values are not cropped
1774  int borderH = sceneSize.width()/100;
1775  int borderV = sceneSize.height()/100;
1776  svgGen.setViewBox(QRect(-borderH, -borderV, sceneSize.width()+borderH*2, sceneSize.height()+borderV*2));
1777  svgGen.setTitle(tr("RTAB-Map graph"));
1778  svgGen.setDescription(tr("RTAB-Map map and graph"));
1779 
1780  QPainter painter( &svgGen );
1781 
1782  this->scene()->render(&painter);
1783 #else
1784  UERROR("RTAB-MAp is not built with Qt's SVG library, cannot save picture in svg format.");
1785 #endif
1786  }
1787  else
1788  {
1789  QImage image(sceneSize, QImage::Format_ARGB32); // Create the image with the exact size of the shrunk scene
1790  image.fill(Qt::transparent); // Start all pixels transparent
1791  QPainter painter(&image);
1792 
1793  this->scene()->render(&painter);
1794  if(!image.isNull())
1795  {
1796  image.save(filePath);
1797  }
1798  else
1799  {
1800  QMessageBox::warning(this,
1801  tr("Save PNG"),
1802  tr("Could not export in PNG (the scene may be too large %1x%2), try saving in SVG.").arg(sceneSize.width()).arg(sceneSize.height()));
1803  }
1804  }
1805 
1806  //reset scale
1807  _root->setScale(1.0f);
1808  this->scene()->setSceneRect(this->scene()->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
1809 
1810 
1811  QDesktopServices::openUrl(QUrl::fromLocalFile(filePath));
1812  }
1813  }
1814  return; // without emitting configChanged
1815  }
1816  else if(r == aSetIntraInterSessionColors)
1817  {
1818  setIntraInterSessionColorsEnabled(aSetIntraInterSessionColors->isChecked());
1819  }
1820  else if(r == aChangeRejectedLoopThr)
1821  {
1822  bool ok;
1823  double value = QInputDialog::getDouble(this, tr("Loop closure outlier threshold"), tr("Value (m)"), _loopClosureOutlierThr, 0.0, 1000.0, 2, &ok);
1824  if(ok)
1825  {
1826  setLoopClosureOutlierThr(value);
1827  }
1828  }
1829  else if(r == aChangeMaxLinkLength)
1830  {
1831  bool ok;
1832  double value = QInputDialog::getDouble(this, tr("Maximum link length to be shown"), tr("Value (m)"), _maxLinkLength, 0.0, 1000.0, 3, &ok);
1833  if(ok)
1834  {
1835  setMaxLinkLength(value);
1836  }
1837  }
1838  else if(r == aChangeNodeColor ||
1839  r == aChangeCurrentGoalColor ||
1840  r == aChangeNeighborColor ||
1841  r == aChangeGlobalLoopColor ||
1842  r == aChangeLocalLoopColor ||
1843  r == aChangeUserLoopColor ||
1844  r == aChangeVirtualLoopColor ||
1845  r == aChangeNeighborMergedColor ||
1846  r == aChangeRejectedLoopColor ||
1847  r == aChangeLocalPathColor ||
1848  r == aChangeGlobalPathColor ||
1849  r == aChangeGTColor ||
1850  r == aChangeGPSColor ||
1851  r == aChangeIntraSessionLoopColor ||
1852  r == aChangeInterSessionLoopColor)
1853  {
1854  QColor color;
1855  if(r == aChangeNodeColor)
1856  {
1857  color = _nodeColor;
1858  }
1859  else if(r == aChangeCurrentGoalColor)
1860  {
1861  color = _currentGoalColor;
1862  }
1863  else if(r == aChangeGlobalLoopColor)
1864  {
1865  color = _loopClosureColor;
1866  }
1867  else if(r == aChangeLocalLoopColor)
1868  {
1869  color = _loopClosureLocalColor;
1870  }
1871  else if(r == aChangeUserLoopColor)
1872  {
1873  color = _loopClosureUserColor;
1874  }
1875  else if(r == aChangeVirtualLoopColor)
1876  {
1877  color = _loopClosureVirtualColor;
1878  }
1879  else if(r == aChangeNeighborMergedColor)
1880  {
1881  color = _neighborMergedColor;
1882  }
1883  else if(r == aChangeLandmarkColor)
1884  {
1885  color = _landmarkColor;
1886  }
1887  else if(r == aChangeRejectedLoopColor)
1888  {
1889  color = _loopClosureRejectedColor;
1890  }
1891  else if(r == aChangeLocalPathColor)
1892  {
1893  color = _localPathColor;
1894  }
1895  else if(r == aChangeGlobalPathColor)
1896  {
1897  color = _globalPathColor;
1898  }
1899  else if(r == aChangeGTColor)
1900  {
1901  color = _gtPathColor;
1902  }
1903  else if(r == aChangeGPSColor)
1904  {
1905  color = _gpsPathColor;
1906  }
1907  else if(r == aChangeIntraSessionLoopColor)
1908  {
1909  color = _loopIntraSessionColor;
1910  }
1911  else if(r == aChangeInterSessionLoopColor)
1912  {
1913  color = _loopInterSessionColor;
1914  }
1915  else //if(r == aChangeNeighborColor)
1916  {
1917  color = _neighborColor;
1918  }
1919  color = QColorDialog::getColor(color, this);
1920  if(color.isValid())
1921  {
1922 
1923  if(r == aChangeNodeColor)
1924  {
1925  this->setNodeColor(color);
1926  }
1927  else if(r == aChangeCurrentGoalColor)
1928  {
1929  this->setCurrentGoalColor(color);
1930  }
1931  else if(r == aChangeGlobalLoopColor)
1932  {
1933  this->setGlobalLoopClosureColor(color);
1934  }
1935  else if(r == aChangeLocalLoopColor)
1936  {
1937  this->setLocalLoopClosureColor(color);
1938  }
1939  else if(r == aChangeUserLoopColor)
1940  {
1941  this->setUserLoopClosureColor(color);
1942  }
1943  else if(r == aChangeVirtualLoopColor)
1944  {
1945  this->setVirtualLoopClosureColor(color);
1946  }
1947  else if(r == aChangeNeighborMergedColor)
1948  {
1949  this->setNeighborMergedColor(color);
1950  }
1951  else if(r == aChangeLandmarkColor)
1952  {
1953  this->setLandmarkColor(color);
1954  }
1955  else if(r == aChangeRejectedLoopColor)
1956  {
1957  this->setRejectedLoopClosureColor(color);
1958  }
1959  else if(r == aChangeLocalPathColor)
1960  {
1961  this->setLocalPathColor(color);
1962  }
1963  else if(r == aChangeGlobalPathColor)
1964  {
1965  this->setGlobalPathColor(color);
1966  }
1967  else if(r == aChangeGTColor)
1968  {
1969  this->setGTColor(color);
1970  }
1971  else if(r == aChangeGPSColor)
1972  {
1973  this->setGPSColor(color);
1974  }
1975  else if(r == aChangeIntraSessionLoopColor)
1976  {
1977  this->setIntraSessionLoopColor(color);
1978  }
1979  else if(r == aChangeInterSessionLoopColor)
1980  {
1981  this->setInterSessionLoopColor(color);
1982  }
1983  else //if(r == aChangeNeighborColor)
1984  {
1985  this->setNeighborColor(color);
1986  }
1987  }
1988  else
1989  {
1990  return; // without emitting configChanged
1991  }
1992  }
1993  else if(r == aSetNodeSize)
1994  {
1995  bool ok;
1996  double value = QInputDialog::getDouble(this, tr("Node radius"), tr("Radius (m)"), _nodeRadius, 0.001, 100, 3, &ok);
1997  if(ok)
1998  {
1999  setNodeRadius(value);
2000  }
2001  }
2002  else if(r == aSetLinkSize)
2003  {
2004  bool ok;
2005  double value = QInputDialog::getDouble(this, tr("Link width"), tr("Width (m)"), _linkWidth, 0, 100, 2, &ok);
2006  if(ok)
2007  {
2008  setLinkWidth(value);
2009  }
2010  }
2011  else if(r == aShowHideGridMap)
2012  {
2013  this->setGridMapVisible(!this->isGridMapVisible());
2014  if(_gridMap->isVisible())
2015  {
2017  }
2018  }
2019  else if(r == aShowHideOrigin)
2020  {
2021  this->setOriginVisible(!this->isOriginVisible());
2022  }
2023  else if(r == aShowHideReferential)
2024  {
2026  }
2027  else if(r == aShowHideLocalRadius)
2028  {
2030  }
2031  else if(r == aRestoreDefaults)
2032  {
2033  this->restoreDefaults();
2034  }
2035  else if(r == aShowHideGraph)
2036  {
2037  this->setGraphVisible(!this->isGraphVisible());
2038  }
2039  else if(r == aShowHideGraphNodes)
2040  {
2041  this->setNodeVisible(!_nodeVisible);
2042  }
2043  else if(r == aShowHideGlobalPath)
2044  {
2045  this->setGlobalPathVisible(!this->isGlobalPathVisible());
2046  }
2047  else if(r == aShowHideLocalPath)
2048  {
2049  this->setLocalPathVisible(!this->isLocalPathVisible());
2050  }
2051  else if(r == aShowHideGtGraph)
2052  {
2053  this->setGtGraphVisible(!this->isGtGraphVisible());
2054  }
2055  else if(r == aShowHideGPSGraph)
2056  {
2057  this->setGPSGraphVisible(!this->isGPSGraphVisible());
2058  }
2059  else if(r == aOrientationENU)
2060  {
2061  this->setOrientationENU(!this->isOrientationENU());
2062  }
2063 
2064  if(r)
2065  {
2066  Q_EMIT configChanged();
2067  }
2068 }
2069 
2070 } /* namespace rtabmap */
void setPoses(const Transform &poseA, const Transform &poseB)
void setLoopClosureOutlierThr(float value)
virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *event)
QGraphicsItem * _gpsGraphRoot
Definition: GraphViewer.h:190
QColor _loopClosureLocalColor
Definition: GraphViewer.h:170
int to() const
QColor _loopClosureVirtualColor
Definition: GraphViewer.h:172
Definition: UTimer.h:46
void setGlobalPathColor(const QColor &color)
const QColor & getVirtualLoopClosureColor() const
Definition: GraphViewer.h:95
virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
float getLinkWidth() const
Definition: GraphViewer.h:88
QGraphicsItem * _localPathRoot
Definition: GraphViewer.h:188
void setCurrentGoalID(int id, const Transform &pose=Transform())
std::string prettyPrint() const
Definition: Transform.cpp:295
void setGridMapVisible(bool visible)
const QColor & getInterSessionLoopColor() const
Definition: GraphViewer.h:103
void setMaxLinkLength(float value)
QGraphicsEllipseItem * _localRadius
Definition: GraphViewer.h:206
const Transform & pose() const
QGraphicsItemGroup * _referential
Definition: GraphViewer.h:203
const Transform & getPoseA() const
virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
f
virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *event)
void setRadius(float radius)
void setWorkingDirectory(const QString &path)
NodeGPSItem(int id, int mapId, const Transform &pose, float radius, const GPS &gps)
void setColor(const QColor &color)
void setIntraInterSessionColorsEnabled(bool enabled)
void saveSettings(QSettings &settings, const QString &group="") const
void updateReferentialPosition(const Transform &t)
void setGlobalPathVisible(bool visible)
void setNeighborColor(const QColor &color)
float getNorm() const
Definition: Transform.cpp:252
GLM_FUNC_DECL detail::tmat4x4< T, P > scale(detail::tmat4x4< T, P > const &m, detail::tvec3< T, P > const &v)
const QColor & getGPSColor() const
Definition: GraphViewer.h:101
const QColor & getRejectedLoopClosureColor() const
Definition: GraphViewer.h:97
float getNodeRadius() const
Definition: GraphViewer.h:87
void setLandmarkColor(const QColor &color)
bool isInterSession() const
virtual ~NodeItem()
Definition: GraphViewer.cpp:86
bool isGlobalPathVisible() const
virtual void wheelEvent(QWheelEvent *event)
QGraphicsPixmapItem * _gridMap
Definition: GraphViewer.h:202
void updateLocalPath(const std::vector< int > &localPath)
void setGPSGraphVisible(bool visible)
void setReferentialVisible(bool visible)
void setGPSColor(const QColor &color)
void setGlobalPath(const std::vector< std::pair< int, Transform > > &globalPath)
QMultiMap< int, LinkItem * > _gpsLinkItems
Definition: GraphViewer.h:196
QGraphicsItem * _graphRoot
Definition: GraphViewer.h:186
NodeItem(int id, int mapId, const Transform &pose, float radius, int weight=-1)
Definition: GraphViewer.cpp:70
QGraphicsItem * _root
Definition: GraphViewer.h:185
const QColor & getGlobalLoopClosureColor() const
Definition: GraphViewer.h:92
bool isGPSGraphVisible() const
int id() const
bool isGraphVisible() const
QColor _loopInterSessionColor
Definition: GraphViewer.h:181
const QColor & getGlobalPathColor() const
Definition: GraphViewer.h:99
#define UASSERT(condition)
GLM_FUNC_DECL genType cos(genType const &angle)
Wrappers of STL for convenient functions.
const QColor & getLocalPathColor() const
Definition: GraphViewer.h:98
QColor _loopClosureRejectedColor
Definition: GraphViewer.h:175
float theta() const
Definition: Transform.cpp:162
GLM_FUNC_DECL genType sin(genType const &angle)
void setGtGraphVisible(bool visible)
float getLoopClosureOutlierThr() const
Definition: GraphViewer.h:109
void setColor(const QColor &color)
Definition: GraphViewer.cpp:88
QColor _loopIntraSessionColor
Definition: GraphViewer.h:180
#define true
Definition: ConvertUTF.c:57
QMultiMap< int, LinkItem * > _globalPathLinkItems
Definition: GraphViewer.h:198
bool isNull() const
Definition: Transform.cpp:107
void setPose(const Transform &pose)
float o14() const
Definition: Transform.h:72
void setNeighborMergedColor(const QColor &color)
QImage uCvMat2QImage(const cv::Mat &image, bool isBgr=true, uCvQtDepthColorMap colorMap=uCvQtDepthWhiteToBlack)
Definition: UCv2Qt.h:44
QGraphicsItemGroup * _originReferential
Definition: GraphViewer.h:204
virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *event)
const QColor & getNodeColor() const
Definition: GraphViewer.h:89
void getEulerAngles(float &roll, float &pitch, float &yaw) const
Definition: Transform.cpp:232
float getMaxLinkLength() const
Definition: GraphViewer.h:110
bool isOrientationENU() const
int from() const
Link::Type linkType() const
void setLocalLoopClosureColor(const QColor &color)
QMultiMap< int, LinkItem * > _gtLinkItems
Definition: GraphViewer.h:195
const QColor & getIntraSessionLoopColor() const
Definition: GraphViewer.h:102
QGraphicsItem * _gtGraphRoot
Definition: GraphViewer.h:189
GraphViewer(QWidget *parent=0)
bool isGtGraphVisible() const
void setInterSessionLoopColor(const QColor &color)
void setUserLoopClosureColor(const QColor &color)
void setOrientationENU(bool enabled)
void updateMap(const cv::Mat &map8U, float resolution, float xMin, float yMin)
bool uContains(const std::list< V > &list, const V &value)
Definition: UStl.h:409
void setGlobalLoopClosureColor(const QColor &color)
bool isLocalRadiusVisible() const
const QColor & getLocalLoopClosureColor() const
Definition: GraphViewer.h:93
const QColor & getCurrentGoalColor() const
Definition: GraphViewer.h:90
void setLocalPathColor(const QColor &color)
#define false
Definition: ConvertUTF.c:56
LinkItem(int from, int to, const Transform &poseA, const Transform &poseB, const Link &link, bool interSessionClosure)
bool isGridMapVisible() const
QMultiMap< int, LinkItem * > _localPathLinkItems
Definition: GraphViewer.h:197
bool isIntraInterSessionColorsEnabled() const
Definition: GraphViewer.h:104
bool isReferentialVisible() const
QGraphicsItem * _globalPathRoot
Definition: GraphViewer.h:187
float getDistance(const Transform &t) const
Definition: Transform.cpp:262
QMap< int, NodeItem * > _gpsNodeItems
Definition: GraphViewer.h:194
void setVirtualLoopClosureColor(const QColor &color)
void setWorldMapRotation(const float &theta)
#define UDEBUG(...)
GLM_FUNC_DECL genType max(genType const &x, genType const &y)
void setIntraSessionLoopColor(const QColor &color)
void setNodeRadius(float radius)
QIcon createIcon(const QColor &color)
QColor _loopClosureUserColor
Definition: GraphViewer.h:171
void updateGTGraph(const std::map< int, Transform > &poses)
void setGTColor(const QColor &color)
bool isOriginVisible() const
#define UERROR(...)
void setGraphVisible(bool visible)
const Transform & getPoseB() const
void setNodeColor(const QColor &color)
const QColor & getUserLoopClosureColor() const
Definition: GraphViewer.h:94
void setNodeVisible(bool visible)
ULogger class and convenient macros.
#define UWARN(...)
QMultiMap< int, LinkItem * > _linkItems
Definition: GraphViewer.h:192
double ticks()
Definition: UTimer.cpp:117
bool isLocalPathVisible() const
QGraphicsItem * _world
Definition: GraphViewer.h:184
GLM_FUNC_DECL T yaw(detail::tquat< T, P > const &x)
const QColor & getNeighborMergedColor() const
Definition: GraphViewer.h:96
void updateGPSGraph(const std::map< int, Transform > &gpsMapPoses, const std::map< int, GPS > &gpsValues)
void loadSettings(QSettings &settings, const QString &group="")
void updatePosterior(const std::map< int, float > &posterior, float fixedMax=0.0f, int zValueOffset=0)
Transform inverse() const
Definition: Transform.cpp:178
void setLocalRadius(float radius)
void updateGraph(const std::map< int, Transform > &poses, const std::multimap< int, Link > &constraints, const std::map< int, int > &mapIds, const std::map< int, int > &weights=std::map< int, int >())
const QColor & getGTColor() const
Definition: GraphViewer.h:100
virtual void contextMenuEvent(QContextMenuEvent *event)
QMap< int, NodeItem * > _nodeItems
Definition: GraphViewer.h:191
QGraphicsLineItem * _line
QMap< int, NodeItem * > _gtNodeItems
Definition: GraphViewer.h:193
const QColor & getNeighborColor() const
Definition: GraphViewer.h:91
float o24() const
Definition: Transform.h:73
void setLocalPathVisible(bool visible)
int mapId() const
void setOriginVisible(bool visible)
void setCurrentGoalColor(const QColor &color)
void setLocalRadiusVisible(bool visible)
void setRejectedLoopClosureColor(const QColor &color)
void setLinkWidth(float width)
GLM_FUNC_DECL detail::tmat4x4< T, P > rotate(detail::tmat4x4< T, P > const &m, T const &angle, detail::tvec3< T, P > const &axis)


rtabmap
Author(s): Mathieu Labbe
autogenerated on Mon Dec 14 2020 03:34:58