object_plugin.cpp
Go to the documentation of this file.
1 // *****************************************************************************
2 //
3 // Copyright (c) 2020, Southwest Research Institute® (SwRI®)
4 // All rights reserved.
5 //
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of Southwest Research Institute® (SwRI®) nor the
14 // names of its contributors may be used to endorse or promote products
15 // derived from this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 // ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
21 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 //
28 // *****************************************************************************
29 
31 
33 
35 
36 #include <boost/algorithm/string.hpp>
37 
38 // Declare plugin
41 
42 namespace mapviz_plugins
43 {
44 #define IS_INSTANCE(msg, type) \
45  (msg->getDataType() == ros::message_traits::datatype<type>())
46 
47  ObjectPlugin::ObjectPlugin() :
48  config_widget_(new QWidget()),
49  connected_(false)
50  {
51  ui_.setupUi(config_widget_);
52 
53  ui_.color->setColor(Qt::red);
54 
55  // Set background white
56  QPalette p(config_widget_->palette());
57  p.setColor(QPalette::Background, Qt::white);
58  config_widget_->setPalette(p);
59 
60  // Set status text red
61  QPalette p3(ui_.status->palette());
62  p3.setColor(QPalette::Text, Qt::red);
63  ui_.status->setPalette(p3);
64 
65  QObject::connect(ui_.selecttopic, SIGNAL(clicked()), this, SLOT(SelectTopic()));
66  QObject::connect(ui_.topic, SIGNAL(editingFinished()), this, SLOT(TopicEdited()));
67  QObject::connect(ui_.color, SIGNAL(colorEdited(const QColor&)), this,
68  SLOT(SetColor(const QColor&)));
69 
70  startTimer(1000);
71  }
72 
74  {
75  }
76 
77  void ObjectPlugin::SetColor(const QColor& color)
78  {
79  color_ = color;
80  }
81 
83  {
84  objects_.clear();
85  }
86 
88  {
90  "marti_nav_msgs/TrackedObjectArray",
91  "marti_nav_msgs/ObstacleArray");
92 
93  if (topic.name.empty())
94  {
95  return;
96  }
97 
98  ui_.topic->setText(QString::fromStdString(topic.name));
99  TopicEdited();
100  }
101 
103  {
104  std::string topic = ui_.topic->text().trimmed().toStdString();
105  if (topic != topic_)
106  {
107  initialized_ = false;
108  objects_.clear();
109  has_message_ = false;
110  PrintWarning("No messages received.");
111 
113  connected_ = false;
114 
115  topic_ = topic;
116  if (!topic.empty())
117  {
119  topic_, 100, &ObjectPlugin::handleMessage, this);
120 
121  ROS_INFO("Subscribing to %s", topic_.c_str());
122  }
123  }
124  }
125 
127  {
128  connected_ = true;
129  if (IS_INSTANCE(msg, marti_nav_msgs::TrackedObjectArray))
130  {
131  objects_.clear();
132 
133  auto objs = msg->instantiate<marti_nav_msgs::TrackedObjectArray>();
134  objects_.reserve(objs->objects.size());
135  for (const auto& obj: objs->objects)
136  {
137  handleTrack(obj);
138  }
139  }
140  else if (IS_INSTANCE(msg, marti_nav_msgs::ObstacleArray))
141  {
142  objects_.clear();
143 
144  auto objs = msg->instantiate<marti_nav_msgs::ObstacleArray>();
145  objects_.reserve(objs->obstacles.size());
146  for (const auto& obj: objs->obstacles)
147  {
148  handleObstacle(obj, objs->header);
149  }
150  }
151  else
152  {
153  PrintError("Unknown message type: " + msg->getDataType());
154  }
155  }
156 
157  void ObjectPlugin::handleObstacle(const marti_nav_msgs::Obstacle& obj, const std_msgs::Header& header)
158  {
159  if (!has_message_)
160  {
161  initialized_ = true;
162  has_message_ = true;
163  }
164 
165  // Since orientation was not implemented, many markers publish
166  // invalid all-zero orientations, so we need to check for this
167  // and provide a default identity transform.
168  tf::Quaternion orientation(0.0, 0.0, 0.0, 1.0);
169  if (obj.pose.orientation.x ||
170  obj.pose.orientation.y ||
171  obj.pose.orientation.z ||
172  obj.pose.orientation.w)
173  {
174  orientation = tf::Quaternion(obj.pose.orientation.x,
175  obj.pose.orientation.y,
176  obj.pose.orientation.z,
177  obj.pose.orientation.w);
178  }
179 
183  orientation,
184  tf::Vector3(obj.pose.position.x,
185  obj.pose.position.y,
186  obj.pose.position.z)));
187  data.source_frame = header.frame_id;
188  data.id = obj.id;
189  data.stamp = header.stamp;
190  data.transformed = false;
191 
193  if (GetTransform(data.source_frame, data.stamp, transform))
194  {
195  data.transformed = true;
196  }
197 
198  data.polygon.reserve(obj.polygon.size()+1);
199  for (auto& point: obj.polygon)
200  {
201  tf::Vector3 pt(point.x, point.y, point.z);
202  pt = data.local_transform*pt;
203  tf::Vector3 transformed = transform *pt;
204  data.polygon.push_back({pt, transformed});
205  }
206  if (data.polygon.size())
207  {
208  data.polygon.push_back(data.polygon.front());
209  }
210  objects_.push_back(data);
211  }
212 
213  void ObjectPlugin::handleTrack(const marti_nav_msgs::TrackedObject &obj)
214  {
215  if (!has_message_)
216  {
217  initialized_ = true;
218  has_message_ = true;
219  }
220 
221  // Since orientation was not implemented, many markers publish
222  // invalid all-zero orientations, so we need to check for this
223  // and provide a default identity transform.
224  tf::Quaternion orientation(0.0, 0.0, 0.0, 1.0);
225  if (obj.pose.pose.orientation.x ||
226  obj.pose.pose.orientation.y ||
227  obj.pose.pose.orientation.z ||
228  obj.pose.pose.orientation.w)
229  {
230  orientation = tf::Quaternion(obj.pose.pose.orientation.x,
231  obj.pose.pose.orientation.y,
232  obj.pose.pose.orientation.z,
233  obj.pose.pose.orientation.w);
234  }
235 
239  orientation,
240  tf::Vector3(obj.pose.pose.position.x,
241  obj.pose.pose.position.y,
242  obj.pose.pose.position.z)));
243  data.stamp = obj.header.stamp;
244  data.source_frame = obj.header.frame_id;
245  data.id = std::to_string(obj.id);
246  data.transformed = false;
247 
249  if (GetTransform(data.source_frame, data.stamp, transform))
250  {
251  data.transformed = true;
252  }
253 
254  data.polygon.reserve(obj.polygon.size()+1);
255  for (auto& point: obj.polygon)
256  {
257  tf::Vector3 pt(point.x, point.y, point.z);
258  pt = data.local_transform*pt;
259  tf::Vector3 transformed = transform *pt;
260  data.polygon.push_back({pt, transformed});
261  }
262  if (data.polygon.size())
263  {
264  data.polygon.push_back(data.polygon.front());
265  }
266  objects_.push_back(data);
267  }
268 
269  void ObjectPlugin::PrintError(const std::string& message)
270  {
271  PrintErrorHelper(ui_.status, message);
272  }
273 
274  void ObjectPlugin::PrintInfo(const std::string& message)
275  {
276  PrintInfoHelper(ui_.status, message);
277  }
278 
279  void ObjectPlugin::PrintWarning(const std::string& message)
280  {
281  PrintWarningHelper(ui_.status, message);
282  }
283 
284  QWidget* ObjectPlugin::GetConfigWidget(QWidget* parent)
285  {
286  config_widget_->setParent(parent);
287 
288  return config_widget_;
289  }
290 
291  bool ObjectPlugin::Initialize(QGLWidget* canvas)
292  {
293  canvas_ = canvas;
294  SetColor(ui_.color->color());
295 
296  return true;
297  }
298 
299  void ObjectPlugin::Draw(double x, double y, double scale)
300  {
301  for (const auto& obj: objects_)
302  {
303  if (!obj.transformed)
304  {
305  continue;
306  }
307 
308  glColor4f(color_.redF(), color_.greenF(), color_.blueF(), 1.0);
309 
310  glLineWidth(3.0);
311  glBegin(GL_LINE_STRIP);
312 
313  for (const auto &point : obj.polygon) {
314  glVertex2d(
315  point.transformed_point.getX(),
316  point.transformed_point.getY());
317  }
318 
319  glEnd();
320 
321  PrintInfo("OK");
322  }
323  }
324 
325  void ObjectPlugin::Paint(QPainter* painter, double x, double y, double scale)
326  {
327  if (!ui_.show_ids->isChecked())
328  {
329  return;
330  }
331 
332  // Most of the marker drawing is done using OpenGL commands, but text labels
333  // are rendered using a QPainter. This is intended primarily as an example
334  // of how the QPainter works.
335  ros::Time now = ros::Time::now();
336 
337  // We don't want the text to be rotated or scaled, but we do want it to be
338  // translated appropriately. So, we save off the current world transform
339  // and reset it; when we actually draw the text, we'll manually translate
340  // it to the right place.
341  QTransform tf = painter->worldTransform();
342  QFont font("Helvetica", 10);
343  painter->setFont(font);
344  painter->save();
345  painter->resetTransform();
346 
347  for (const auto& obj: objects_)
348  {
349  if (!obj.transformed)
350  {
351  continue;
352  }
353 
354  QPen pen(QBrush(QColor::fromRgbF(0, 0, 0, 1)), 1);
355  painter->setPen(pen);
356 
357  const StampedPoint& rosPoint = obj.polygon.front();
358  QPointF point = tf.map(QPointF(rosPoint.transformed_point.x(),
359  rosPoint.transformed_point.y()));
360 
361  auto text = QString::fromStdString(obj.id);
362  // Get bounding rectangle
363  QRectF rect(point, QSizeF(10,10));
364  rect = painter->boundingRect(rect, Qt::AlignLeft | Qt::AlignHCenter, text);
365  painter->drawText(rect, text);
366 
367  PrintInfo("OK");
368  }
369 
370  painter->restore();
371  }
372 
374  {
375  for (auto& obj: objects_)
376  {
378  if (GetTransform(obj.source_frame, obj.stamp, transform))
379  {
380  obj.transformed = true;
381 
382  for (auto &point : obj.polygon)
383  {
384  point.transformed_point = transform * point.point;
385  }
386  }
387  else
388  {
389  obj.transformed = false;
390  }
391  }
392  }
393 
394  void ObjectPlugin::LoadConfig(const YAML::Node& node, const std::string& path)
395  {
396  if (node["topic"])
397  {
398  std::string topic;
399  node["topic"] >> topic;
400  ui_.topic->setText(boost::trim_copy(topic).c_str());
401 
402  TopicEdited();
403  }
404 
405  if (node["color"])
406  {
407  std::string color;
408  node["color"] >> color;
409  QColor qcolor(color.c_str());
410  SetColor(qcolor);
411  ui_.color->setColor(qcolor);
412  }
413 
414  if (node["show_ids"])
415  {
416  bool checked;
417  node["show_ids"] >> checked;
418  ui_.show_ids->setChecked( checked );
419  }
420  }
421 
422  void ObjectPlugin::SaveConfig(YAML::Emitter& emitter, const std::string& path)
423  {
424  emitter << YAML::Key << "topic" << YAML::Value << boost::trim_copy(ui_.topic->text().toStdString());
425 
426  emitter << YAML::Key << "color" << YAML::Value
427  << ui_.color->color().name().toStdString();
428 
429  emitter << YAML::Key << "show_ids" << YAML::Value << ui_.show_ids->isChecked();
430  }
431 
432  void ObjectPlugin::timerEvent(QTimerEvent *event)
433  {
434  bool new_connected = (object_sub_.getNumPublishers() > 0);
435  if (connected_ && !new_connected)
436  {
438  if (!topic_.empty())
439  {
441  topic_, 100, &ObjectPlugin::handleMessage, this);
442  }
443  }
444  connected_ = new_connected;
445  }
446 }
447 
bool GetTransform(const ros::Time &stamp, swri_transform_util::Transform &transform, bool use_latest_transforms=true)
static ros::master::TopicInfo selectTopic(const std::string &datatype, QWidget *parent=0)
void timerEvent(QTimerEvent *)
ros::NodeHandle node_
void transform(Route &route, const swri_transform_util::Transform &transform, const std::string &target_frame)
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
std::vector< ObjectData > objects_
void Draw(double x, double y, double scale)
void handleMessage(const topic_tools::ShapeShifter::ConstPtr &msg)
bool Initialize(QGLWidget *canvas)
static void PrintWarningHelper(QLabel *status_label, const std::string &message, double throttle=0.0)
void Paint(QPainter *painter, double x, double y, double scale)
static void PrintErrorHelper(QLabel *status_label, const std::string &message, double throttle=0.0)
data
void SaveConfig(YAML::Emitter &emitter, const std::string &path)
void PrintWarning(const std::string &message)
static void PrintInfoHelper(QLabel *status_label, const std::string &message, double throttle=0.0)
QWidget * GetConfigWidget(QWidget *parent)
void SetColor(const QColor &color)
std::vector< StampedPoint > polygon
TFSIMD_FORCE_INLINE const tfScalar & x() const
void LoadConfig(const YAML::Node &node, const std::string &path)
#define ROS_INFO(...)
uint32_t getNumPublishers() const
TFSIMD_FORCE_INLINE const tfScalar & y() const
void handleObstacle(const marti_nav_msgs::Obstacle &obj, const std_msgs::Header &header)
void handleTrack(const marti_nav_msgs::TrackedObject &obj)
void PrintInfo(const std::string &message)
static Time now()
swri_transform_util::Transform local_transform
#define PLUGINLIB_EXPORT_CLASS(class_type, base_class_type)
void PrintError(const std::string &message)
#define IS_INSTANCE(msg, type)


mapviz_plugins
Author(s): Marc Alban
autogenerated on Fri Mar 19 2021 02:44:32