tile_map_plugin.cpp
Go to the documentation of this file.
1 // *****************************************************************************
2 //
3 // Copyright (c) 2015, 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 #include <tile_map/tile_source.h>
32 #include <tile_map/bing_source.h>
33 #include <tile_map/wmts_source.h>
34 
35 #include <boost/algorithm/string/trim.hpp>
36 
37 #include <tile_map/tile_source.h>
38 #include <tile_map/bing_source.h>
39 #include <tile_map/wmts_source.h>
40 
41 // QT libraries
42 #include <QGLWidget>
43 #include <QInputDialog>
44 #include <QMessageBox>
45 #include <QPalette>
46 
47 // ROS libraries
48 #include <ros/ros.h>
49 #include <tf/transform_datatypes.h>
50 
53 
54 // Declare plugin
57 
58 namespace tile_map
59 {
60  std::string TileMapPlugin::BASE_URL_KEY = "base_url";
61  std::string TileMapPlugin::BING_API_KEY = "bing_api_key";
62  std::string TileMapPlugin::CUSTOM_SOURCES_KEY = "custom_sources";
63  std::string TileMapPlugin::MAX_ZOOM_KEY = "max_zoom";
64  std::string TileMapPlugin::NAME_KEY = "name";
65  std::string TileMapPlugin::SOURCE_KEY = "source";
66  std::string TileMapPlugin::TYPE_KEY = "type";
67  QString TileMapPlugin::BING_NAME = "Bing Maps (terrain)";
68  QString TileMapPlugin::STAMEN_TERRAIN_NAME = "Stamen (terrain)";
69  QString TileMapPlugin::STAMEN_TONER_NAME = "Stamen (toner)";
70  QString TileMapPlugin::STAMEN_WATERCOLOR_NAME = "Stamen (watercolor)";
71 
72  TileMapPlugin::TileMapPlugin() :
73  config_widget_(new QWidget()),
74  transformed_(false),
75  last_center_x_(0.0),
76  last_center_y_(0.0),
77  last_scale_(0.0),
78  last_height_(0),
79  last_width_(0)
80  {
81  ui_.setupUi(config_widget_);
82 
84  boost::make_shared<WmtsSource>(STAMEN_TERRAIN_NAME,
85  "http://tile.stamen.com/terrain/{level}/{x}/{y}.png",
86  false,
87  15);
89  boost::make_shared<WmtsSource>(STAMEN_TONER_NAME,
90  "http://tile.stamen.com/toner/{level}/{x}/{y}.png",
91  false,
92  19);
93  tile_sources_[STAMEN_WATERCOLOR_NAME] =
94  boost::make_shared<WmtsSource>(STAMEN_WATERCOLOR_NAME,
95  "http://tile.stamen.com/watercolor/{level}/{x}/{y}.jpg",
96  false,
97  19);
98  boost::shared_ptr<BingSource> bing = boost::make_shared<BingSource>(BING_NAME);
99  tile_sources_[BING_NAME] = bing;
100 
101  QPalette p(config_widget_->palette());
102  p.setColor(QPalette::Background, Qt::white);
103  config_widget_->setPalette(p);
104 
105  QPalette p2(ui_.status->palette());
106  p2.setColor(QPalette::Text, Qt::red);
107  ui_.status->setPalette(p2);
108 
110 
111  QObject::connect(bing.get(), SIGNAL(ErrorMessage(const std::string&)),
112  this, SLOT(PrintError(const std::string&)));
113  QObject::connect(bing.get(), SIGNAL(InfoMessage(const std::string&)),
114  this, SLOT(PrintInfo(const std::string&)));
115  QObject::connect(ui_.delete_button, SIGNAL(clicked()), this, SLOT(DeleteTileSource()));
116  QObject::connect(ui_.source_combo, SIGNAL(activated(const QString&)), this, SLOT(SelectSource(const QString&)));
117  QObject::connect(ui_.save_button, SIGNAL(clicked()), this, SLOT(SaveCustomSource()));
118  QObject::connect(ui_.reset_cache_button, SIGNAL(clicked()), this, SLOT(ResetTileCache()));
119  }
120 
122  {
123  }
124 
126  {
127  int source_index = ui_.source_combo->currentIndex();
128  QString current_name = ui_.source_combo->currentText();
129 
130  QMessageBox mbox;
131  mbox.setText("Are you sure you want to delete the source \"" + current_name + "\"?");
132  mbox.setIcon(QMessageBox::Warning);
133  mbox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
134  mbox.setDefaultButton(QMessageBox::Cancel);
135  int ret = mbox.exec();
136 
137  if (ret == QMessageBox::Ok)
138  {
139  ui_.source_combo->removeItem(source_index);
140  tile_sources_.erase(current_name);
141  ui_.source_combo->setCurrentIndex(0);
142  SelectSource(ui_.source_combo->currentText());
143  }
144  }
145 
146  void TileMapPlugin::SelectSource(const QString& source)
147  {
148  if (source == STAMEN_TERRAIN_NAME ||
149  source == STAMEN_WATERCOLOR_NAME ||
150  source == STAMEN_TONER_NAME ||
151  source == BING_NAME)
152  {
154  }
155  else
156  {
158  }
159 
160  std::map<QString, boost::shared_ptr<TileSource> >::iterator iter = tile_sources_.find(source);
161 
162  // If the previously selected source was Bing, these will have been changed, so
163  // they should be changed back. There's not an easy way to know here what the
164  // previously selected item was, so just always change them.
165  ui_.url_label->setText("Base URL:");
166  ui_.save_button->setText("Save...");
167  if (iter != tile_sources_.end())
168  {
169  selectTileSource(iter->second);
170  initialized_ = true;
171  // For the Bing map type, change a couple of the fields to have more appropriate
172  // labels. There should probably be a cleaner way to do this if we end up adding
173  // more tile source types....
174  if (iter->second->GetType() == BingSource::BING_TYPE)
175  {
176  ui_.url_label->setText("API Key:");
177  ui_.save_button->setText("Save");
178  ui_.base_url_text->setEnabled(true);
179  ui_.save_button->setEnabled(true);
180  }
181  }
182  else
183  {
184  ui_.delete_button->setEnabled(false);
185  }
186  }
187 
189  {
190  // If the user is editing a custom source, we want to fill in the default
191  // name for it with its current name.
192  // Otherwise, they're creating a new custom source, in which case we
193  // should leave the default blank.
194  QString current_source = ui_.source_combo->currentText();
195  QString default_name = "";
196 
197  std::map<QString, boost::shared_ptr<TileSource> >::iterator iter = tile_sources_.find(current_source);
198  if (iter != tile_sources_.end())
199  {
200  if (iter->second->IsCustom())
201  {
202  default_name = current_source;
203  }
204  else if (iter->second->GetType() == BingSource::BING_TYPE)
205  {
206  // If the user has picked Bing as they're source, we're not actually
207  // saving a custom map source, just updating the API key
208  BingSource* bing_source = static_cast<BingSource*>(iter->second.get());
209  bing_source->SetApiKey(ui_.base_url_text->text());
210  return;
211  }
212  }
213 
214  bool ok;
215  QString name = QInputDialog::getText(config_widget_,
216  tr("Save New Tile Source"),
217  tr("Tile Source Name:"),
218  QLineEdit::Normal,
219  default_name,
220  &ok);
221  name = name.trimmed();
222  if (ok && !name.isEmpty())
223  {
224  boost::shared_ptr<WmtsSource> source = boost::make_shared<WmtsSource>(name,
225  ui_.base_url_text->text(),
226  true,
227  ui_.max_zoom_spin_box->value());
228  int existing_index = ui_.source_combo->findText(name);
229  if (existing_index != -1)
230  {
231  ui_.source_combo->removeItem(existing_index);
232  }
233  tile_sources_[name] = source;
234  ui_.source_combo->addItem(name);
235  int new_index = ui_.source_combo->findText(name);
236  ui_.source_combo->setCurrentIndex(new_index);
237  SelectSource(name);
238  }
239  }
240 
242  {
244  }
245 
246  void TileMapPlugin::PrintError(const std::string& message)
247  {
248  if (message == ui_.status->text().toStdString())
249  return;
250 
251  ROS_ERROR("Error: %s", message.c_str());
252  QPalette p(ui_.status->palette());
253  p.setColor(QPalette::Text, Qt::red);
254  ui_.status->setPalette(p);
255  ui_.status->setText(message.c_str());
256  }
257 
258  void TileMapPlugin::PrintInfo(const std::string& message)
259  {
260  if (message == ui_.status->text().toStdString())
261  return;
262 
263  ROS_INFO("%s", message.c_str());
264  QPalette p(ui_.status->palette());
265  p.setColor(QPalette::Text, Qt::green);
266  ui_.status->setPalette(p);
267  ui_.status->setText(message.c_str());
268  }
269 
270  void TileMapPlugin::PrintWarning(const std::string& message)
271  {
272  if (message == ui_.status->text().toStdString())
273  return;
274 
275  ROS_WARN("%s", message.c_str());
276  QPalette p(ui_.status->palette());
277  p.setColor(QPalette::Text, Qt::darkYellow);
278  ui_.status->setPalette(p);
279  ui_.status->setText(message.c_str());
280  }
281 
282  QWidget* TileMapPlugin::GetConfigWidget(QWidget* parent)
283  {
284  config_widget_->setParent(parent);
285 
286  return config_widget_;
287  }
288 
289  bool TileMapPlugin::Initialize(QGLWidget* canvas)
290  {
291  canvas_ = canvas;
292 
294 
295  return true;
296  }
297 
298  void TileMapPlugin::Draw(double x, double y, double scale)
299  {
300  if (!tile_map_.IsReady())
301  {
302  return;
303  }
304 
306  if (tf_manager_->GetTransform(source_frame_, target_frame_, to_wgs84))
307  {
308  tf::Vector3 center(x, y, 0);
309  center = to_wgs84 * center;
310 
311  if (center.y() != last_center_y_ ||
312  center.x() != last_center_x_ ||
313  scale != last_scale_ ||
314  canvas_->width() != last_width_ ||
315  canvas_->height() != last_height_)
316  {
317  // Draw() is called very frequently, and SetView is a fairly expensive operation, so we
318  // can save some CPU time by only calling it when the relevant parameters have changed.
319  last_center_y_ = center.y();
320  last_center_x_ = center.x();
321  last_scale_ = scale;
322  last_width_ = canvas_->width();
323  last_height_ = canvas_->height();
324  tile_map_.SetView(center.y(), center.x(), scale, canvas_->width(), canvas_->height());
325  ROS_DEBUG("TileMapPlugin::Draw: Successfully set view");
326  }
327  tile_map_.Draw();
328  }
329  }
330 
332  {
334  if (tf_manager_->GetTransform(target_frame_, source_frame_, to_target))
335  {
336  tile_map_.SetTransform(to_target);
337  PrintInfo("OK");
338  }
339  else
340  {
341  PrintError("No transform between " + source_frame_ + " and " + target_frame_);
342  }
343  }
344 
345  void TileMapPlugin::LoadConfig(const YAML::Node& node, const std::string& path)
346  {
348  {
349  const YAML::Node& sources = node[CUSTOM_SOURCES_KEY];
350  YAML::Node::const_iterator source_iter;
351  for (source_iter = sources.begin(); source_iter != sources.end(); source_iter++)
352  {
353  std::string type = "";
354  if (swri_yaml_util::FindValue(*source_iter, TYPE_KEY))
355  {
356  // If the type isn't set, we'll assume it's WMTS
357  (*source_iter)[TYPE_KEY] >> type;
358  }
360  if (type == "wmts" || type.empty())
361  {
362  std::string name;
363  std::string base_url;
364  int max_zoom;
365  (*source_iter)[NAME_KEY] >> name;
366  (*source_iter)[BASE_URL_KEY] >> base_url;
367  (*source_iter)[MAX_ZOOM_KEY] >> max_zoom;
368  source = boost::make_shared<WmtsSource>(
369  QString::fromStdString(name),
370  QString::fromStdString(base_url),
371  true,
372  max_zoom);
373  }
374  else if (type == "bing")
375  {
376  std::string name;
377  (*source_iter)[NAME_KEY] >> name;
378  source = boost::make_shared<BingSource>(QString::fromStdString(name));
379  }
380  tile_sources_[source->GetName()] = source;
381  ui_.source_combo->addItem(source->GetName());
382  }
383  }
384 
386  {
387  std::string key;
388  node[BING_API_KEY] >> key;
389  BingSource* source = static_cast<BingSource*>(tile_sources_[BING_NAME].get());
390  source->SetApiKey(QString::fromStdString(key));
391  }
392 
394  {
395  std::string source;
396  node[SOURCE_KEY] >> source;
397 
398  int index = ui_.source_combo->findText(QString::fromStdString(source), Qt::MatchExactly);
399 
400  if (index >= 0)
401  {
402  ui_.source_combo->setCurrentIndex(index);
403  }
404 
405  SelectSource(QString::fromStdString(source));
406  }
407  }
408 
409  void TileMapPlugin::SaveConfig(YAML::Emitter& emitter, const std::string& path)
410  {
411  emitter << YAML::Key << CUSTOM_SOURCES_KEY << YAML::Value << YAML::BeginSeq;
412 
413  std::map<QString, boost::shared_ptr<TileSource> >::iterator iter;
414  for (iter = tile_sources_.begin(); iter != tile_sources_.end(); iter++)
415  {
416  if (iter->second->IsCustom())
417  {
418  emitter << YAML::BeginMap;
419  emitter << YAML::Key << BASE_URL_KEY << YAML::Value << iter->second->GetBaseUrl().toStdString();
420  emitter << YAML::Key << MAX_ZOOM_KEY << YAML::Value << iter->second->GetMaxZoom();
421  emitter << YAML::Key << NAME_KEY << YAML::Value << iter->second->GetName().toStdString();
422  emitter << YAML::Key << TYPE_KEY << YAML::Value << iter->second->GetType().toStdString();
423  emitter << YAML::EndMap;
424  }
425  }
426  emitter << YAML::EndSeq;
427 
428  BingSource* bing_source = static_cast<BingSource*>(tile_sources_[BING_NAME].get());
429  emitter << YAML::Key << BING_API_KEY <<
430  YAML::Value << boost::trim_copy(bing_source->GetApiKey().toStdString());
431 
432  emitter << YAML::Key << SOURCE_KEY <<
433  YAML::Value << boost::trim_copy(ui_.source_combo->currentText().toStdString());
434  }
435 
437  {
438  last_height_ = 0; // This will force us to recalculate our view
439  tile_map_.SetTileSource(tile_source);
440  if (tile_source->GetType() == BingSource::BING_TYPE)
441  {
442  BingSource* bing_source = static_cast<BingSource*>(tile_source.get());
443  ui_.base_url_text->setText(bing_source->GetApiKey());
444  }
445  else
446  {
447  ui_.base_url_text->setText(tile_source->GetBaseUrl());
448  }
449  ui_.max_zoom_spin_box->setValue(tile_source->GetMaxZoom());
450  }
451 
453  {
454  ui_.base_url_text->setEnabled(true);
455  ui_.delete_button->setEnabled(true);
456  ui_.max_zoom_spin_box->setEnabled(true);
457  ui_.save_button->setEnabled(true);
458  }
459 
461  {
462  ui_.base_url_text->setEnabled(false);
463  ui_.delete_button->setEnabled(false);
464  ui_.max_zoom_spin_box->setEnabled(false);
465  ui_.save_button->setEnabled(false);
466  }
467 }
468 
void SetTileSource(const boost::shared_ptr< TileSource > &tile_source)
string name
static QString STAMEN_TONER_NAME
static QString STAMEN_TERRAIN_NAME
Ui::tile_map_config ui_
static const std::string _wgs84_frame
static std::string SOURCE_KEY
std::map< QString, boost::shared_ptr< TileSource > > tile_sources_
void SaveConfig(YAML::Emitter &emitter, const std::string &path)
static const QString BING_TYPE
Definition: bing_source.h:107
QString GetApiKey() const
Definition: bing_source.cpp:87
std::string target_frame_
static std::string CUSTOM_SOURCES_KEY
#define ROS_WARN(...)
static std::string TYPE_KEY
std::string source_frame_
void Draw(double x, double y, double scale)
void PrintError(const std::string &message)
static QString STAMEN_WATERCOLOR_NAME
static std::string NAME_KEY
static std::string BASE_URL_KEY
bool FindValue(const YAML::Node &node, const std::string &name)
#define ROS_INFO(...)
void LoadConfig(const YAML::Node &node, const std::string &path)
void SetView(double latitude, double longitude, double scale, int32_t width, int32_t height)
static std::string MAX_ZOOM_KEY
static std::string BING_API_KEY
void PrintWarning(const std::string &message)
void SelectSource(const QString &source_name)
void selectTileSource(const boost::shared_ptr< TileSource > &tile_source)
void SetApiKey(const QString &api_key)
Definition: bing_source.cpp:92
void SetTransform(const swri_transform_util::Transform &transform)
bool Initialize(QGLWidget *canvas)
void PrintInfo(const std::string &message)
QWidget * GetConfigWidget(QWidget *parent)
swri_transform_util::TransformManagerPtr tf_manager_
#define PLUGINLIB_EXPORT_CLASS(class_type, base_class_type)
#define ROS_ERROR(...)
#define ROS_DEBUG(...)


tile_map
Author(s): Marc Alban
autogenerated on Fri Mar 19 2021 02:44:47