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


tile_map
Author(s): Marc Alban
autogenerated on Thu Jun 6 2019 19:25:35