tile_map_plugin.cpp
Go to the documentation of this file.
00001 // *****************************************************************************
00002 //
00003 // Copyright (c) 2015, Southwest Research Institute® (SwRI®)
00004 // All rights reserved.
00005 //
00006 // Redistribution and use in source and binary forms, with or without
00007 // modification, are permitted provided that the following conditions are met:
00008 //     * Redistributions of source code must retain the above copyright
00009 //       notice, this list of conditions and the following disclaimer.
00010 //     * Redistributions in binary form must reproduce the above copyright
00011 //       notice, this list of conditions and the following disclaimer in the
00012 //       documentation and/or other materials provided with the distribution.
00013 //     * Neither the name of Southwest Research Institute® (SwRI®) nor the
00014 //       names of its contributors may be used to endorse or promote products
00015 //       derived from this software without specific prior written permission.
00016 //
00017 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020 // ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
00021 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00022 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00023 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00024 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00026 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027 //
00028 // *****************************************************************************
00029 
00030 #include <tile_map/tile_map_plugin.h>
00031 
00032 #include <boost/algorithm/string/trim.hpp>
00033 
00034 #include <tile_map/tile_source.h>
00035 #include <tile_map/bing_source.h>
00036 #include <tile_map/wmts_source.h>
00037 
00038 // QT libraries
00039 #include <QGLWidget>
00040 #include <QInputDialog>
00041 #include <QMessageBox>
00042 #include <QPalette>
00043 
00044 // ROS libraries
00045 #include <ros/ros.h>
00046 #include <tf/transform_datatypes.h>
00047 
00048 #include <swri_transform_util/frames.h>
00049 #include <swri_yaml_util/yaml_util.h>
00050 
00051 // Declare plugin
00052 #include <pluginlib/class_list_macros.h>
00053 PLUGINLIB_EXPORT_CLASS(tile_map::TileMapPlugin, mapviz::MapvizPlugin)
00054 
00055 namespace tile_map
00056 {
00057   std::string TileMapPlugin::BASE_URL_KEY = "base_url";
00058   std::string TileMapPlugin::BING_API_KEY = "bing_api_key";
00059   std::string TileMapPlugin::CUSTOM_SOURCES_KEY = "custom_sources";
00060   std::string TileMapPlugin::MAX_ZOOM_KEY = "max_zoom";
00061   std::string TileMapPlugin::NAME_KEY = "name";
00062   std::string TileMapPlugin::SOURCE_KEY = "source";
00063   std::string TileMapPlugin::TYPE_KEY = "type";
00064   QString TileMapPlugin::BING_NAME = "Bing Maps (terrain)";
00065   QString TileMapPlugin::STAMEN_TERRAIN_NAME = "Stamen (terrain)";
00066   QString TileMapPlugin::STAMEN_TONER_NAME = "Stamen (toner)";
00067   QString TileMapPlugin::STAMEN_WATERCOLOR_NAME = "Stamen (watercolor)";
00068 
00069   TileMapPlugin::TileMapPlugin() :
00070     config_widget_(new QWidget()),
00071     transformed_(false),
00072     last_center_x_(0.0),
00073     last_center_y_(0.0),
00074     last_scale_(0.0),
00075     last_height_(0),
00076     last_width_(0)
00077   {
00078     ui_.setupUi(config_widget_);
00079 
00080     tile_sources_[STAMEN_TERRAIN_NAME] =
00081         boost::make_shared<WmtsSource>(STAMEN_TERRAIN_NAME,
00082                                        "http://tile.stamen.com/terrain/{level}/{x}/{y}.png",
00083                                        false,
00084                                        15);
00085     tile_sources_[STAMEN_TONER_NAME] =
00086         boost::make_shared<WmtsSource>(STAMEN_TONER_NAME,
00087                                        "http://tile.stamen.com/toner/{level}/{x}/{y}.png",
00088                                        false,
00089                                        19);
00090     tile_sources_[STAMEN_WATERCOLOR_NAME] =
00091         boost::make_shared<WmtsSource>(STAMEN_WATERCOLOR_NAME,
00092                                        "http://tile.stamen.com/watercolor/{level}/{x}/{y}.jpg",
00093                                        false,
00094                                        19);
00095     boost::shared_ptr<BingSource> bing = boost::make_shared<BingSource>(BING_NAME);
00096     tile_sources_[BING_NAME] = bing;
00097 
00098     QPalette p(config_widget_->palette());
00099     p.setColor(QPalette::Background, Qt::white);
00100     config_widget_->setPalette(p);
00101 
00102     QPalette p2(ui_.status->palette());
00103     p2.setColor(QPalette::Text, Qt::red);
00104     ui_.status->setPalette(p2);
00105 
00106     source_frame_ = swri_transform_util::_wgs84_frame;
00107 
00108     QObject::connect(bing.get(), SIGNAL(ErrorMessage(const std::string&)),
00109                      this, SLOT(PrintError(const std::string&)));
00110     QObject::connect(bing.get(), SIGNAL(InfoMessage(const std::string&)),
00111                      this, SLOT(PrintInfo(const std::string&)));
00112     QObject::connect(ui_.delete_button, SIGNAL(clicked()), this, SLOT(DeleteTileSource()));
00113     QObject::connect(ui_.source_combo, SIGNAL(activated(const QString&)), this, SLOT(SelectSource(const QString&)));
00114     QObject::connect(ui_.save_button, SIGNAL(clicked()), this, SLOT(SaveCustomSource()));
00115     QObject::connect(ui_.reset_cache_button, SIGNAL(clicked()), this, SLOT(ResetTileCache()));
00116   }
00117 
00118   TileMapPlugin::~TileMapPlugin()
00119   {
00120   }
00121 
00122   void TileMapPlugin::DeleteTileSource()
00123   {
00124     int source_index = ui_.source_combo->currentIndex();
00125     QString current_name = ui_.source_combo->currentText();
00126 
00127     QMessageBox mbox;
00128     mbox.setText("Are you sure you want to delete the source \"" + current_name + "\"?");
00129     mbox.setIcon(QMessageBox::Warning);
00130     mbox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
00131     mbox.setDefaultButton(QMessageBox::Cancel);
00132     int ret = mbox.exec();
00133 
00134     if (ret == QMessageBox::Ok)
00135     {
00136       ui_.source_combo->removeItem(source_index);
00137       tile_sources_.erase(current_name);
00138       ui_.source_combo->setCurrentIndex(0);
00139       SelectSource(ui_.source_combo->currentText());
00140     }
00141   }
00142 
00143   void TileMapPlugin::SelectSource(const QString& source)
00144   {
00145     if (source == STAMEN_TERRAIN_NAME ||
00146         source == STAMEN_WATERCOLOR_NAME ||
00147         source == STAMEN_TONER_NAME ||
00148         source == BING_NAME)
00149     {
00150       stopCustomEditing();
00151     }
00152     else
00153     {
00154       startCustomEditing();
00155     }
00156 
00157     std::map<QString, boost::shared_ptr<TileSource> >::iterator iter = tile_sources_.find(source);
00158 
00159     // If the previously selected source was Bing, these will have been changed, so
00160     // they should be changed back.  There's not an easy way to know here what the
00161     // previously selected item was, so just always change them.
00162     ui_.url_label->setText("Base URL:");
00163     ui_.save_button->setText("Save...");
00164     if (iter != tile_sources_.end())
00165     {
00166       selectTileSource(iter->second);
00167       initialized_ = true;
00168       // For the Bing map type, change a couple of the fields to have more appropriate
00169       // labels.  There should probably be a cleaner way to do this if we end up adding
00170       // more tile source types....
00171       if (iter->second->GetType() == BingSource::BING_TYPE)
00172       {
00173         ui_.url_label->setText("API Key:");
00174         ui_.save_button->setText("Save");
00175         ui_.base_url_text->setEnabled(true);
00176         ui_.save_button->setEnabled(true);
00177       }
00178     }
00179     else
00180     {
00181       ui_.delete_button->setEnabled(false);
00182     }
00183   }
00184 
00185   void TileMapPlugin::SaveCustomSource()
00186   {
00187     // If the user is editing a custom source, we want to fill in the default
00188     // name for it with its current name.
00189     // Otherwise, they're creating a new custom source, in which case we
00190     // should leave the default blank.
00191     QString current_source = ui_.source_combo->currentText();
00192     QString default_name = "";
00193 
00194     std::map<QString, boost::shared_ptr<TileSource> >::iterator iter = tile_sources_.find(current_source);
00195     if (iter != tile_sources_.end())
00196     {
00197       if (iter->second->IsCustom())
00198       {
00199         default_name = current_source;
00200       }
00201       else if (iter->second->GetType() == BingSource::BING_TYPE)
00202       {
00203         // If the user has picked Bing as they're source, we're not actually
00204         // saving a custom map source, just updating the API key
00205         BingSource* bing_source = static_cast<BingSource*>(iter->second.get());
00206         bing_source->SetApiKey(ui_.base_url_text->text());
00207         return;
00208       }
00209     }
00210 
00211     bool ok;
00212     QString name = QInputDialog::getText(config_widget_,
00213                                          tr("Save New Tile Source"),
00214                                          tr("Tile Source Name:"),
00215                                          QLineEdit::Normal,
00216                                          default_name,
00217                                          &ok);
00218     name = name.trimmed();
00219     if (ok && !name.isEmpty())
00220     {
00221       boost::shared_ptr<WmtsSource> source = boost::make_shared<WmtsSource>(name,
00222                         ui_.base_url_text->text(),
00223                         true,
00224                         ui_.max_zoom_spin_box->value());
00225       int existing_index = ui_.source_combo->findText(name);
00226       if (existing_index != -1)
00227       {
00228         ui_.source_combo->removeItem(existing_index);
00229       }
00230       tile_sources_[name] = source;
00231       ui_.source_combo->addItem(name);
00232       int new_index = ui_.source_combo->findText(name);
00233       ui_.source_combo->setCurrentIndex(new_index);
00234       SelectSource(name);
00235     }
00236   }
00237 
00238   void TileMapPlugin::ResetTileCache()
00239   {
00240     tile_map_.ResetCache();
00241   }
00242 
00243   void TileMapPlugin::PrintError(const std::string& message)
00244   {
00245     if (message == ui_.status->text().toStdString())
00246       return;
00247 
00248     ROS_ERROR("Error: %s", message.c_str());
00249     QPalette p(ui_.status->palette());
00250     p.setColor(QPalette::Text, Qt::red);
00251     ui_.status->setPalette(p);
00252     ui_.status->setText(message.c_str());
00253   }
00254 
00255   void TileMapPlugin::PrintInfo(const std::string& message)
00256   {
00257     if (message == ui_.status->text().toStdString())
00258       return;
00259 
00260     ROS_INFO("%s", message.c_str());
00261     QPalette p(ui_.status->palette());
00262     p.setColor(QPalette::Text, Qt::green);
00263     ui_.status->setPalette(p);
00264     ui_.status->setText(message.c_str());
00265   }
00266 
00267   void TileMapPlugin::PrintWarning(const std::string& message)
00268   {
00269     if (message == ui_.status->text().toStdString())
00270       return;
00271 
00272     ROS_WARN("%s", message.c_str());
00273     QPalette p(ui_.status->palette());
00274     p.setColor(QPalette::Text, Qt::darkYellow);
00275     ui_.status->setPalette(p);
00276     ui_.status->setText(message.c_str());
00277   }
00278 
00279   QWidget* TileMapPlugin::GetConfigWidget(QWidget* parent)
00280   {
00281     config_widget_->setParent(parent);
00282 
00283     return config_widget_;
00284   }
00285 
00286   bool TileMapPlugin::Initialize(QGLWidget* canvas)
00287   {
00288     canvas_ = canvas;
00289 
00290     SelectSource(STAMEN_TERRAIN_NAME);
00291 
00292     return true;
00293   }
00294 
00295   void TileMapPlugin::Draw(double x, double y, double scale)
00296   {
00297     swri_transform_util::Transform to_wgs84;
00298     if (tf_manager_.GetTransform(source_frame_, target_frame_, to_wgs84))
00299     {
00300       tf::Vector3 center(x, y, 0);
00301       center = to_wgs84 * center;
00302       if (center.y() != last_center_y_ ||
00303           center.x() != last_center_x_ ||
00304           scale != last_scale_ ||
00305           canvas_->width() != last_width_ ||
00306           canvas_->height() != last_height_)
00307       {
00308         // Draw() is called very frequently, and SetView is a fairly expensive operation, so we
00309         // can save some CPU time by only calling it when the relevant parameters have changed.
00310         last_center_y_ = center.y();
00311         last_center_x_ = center.x();
00312         last_scale_ = scale;
00313         last_width_ = canvas_->width();
00314         last_height_ = canvas_->height();
00315         tile_map_.SetView(center.y(), center.x(), scale, canvas_->width(), canvas_->height());
00316       }
00317       tile_map_.Draw();
00318     }
00319   }
00320 
00321   void TileMapPlugin::Transform()
00322   {
00323     swri_transform_util::Transform to_target;
00324     if (tf_manager_.GetTransform(target_frame_, source_frame_, to_target))
00325     {
00326       tile_map_.SetTransform(to_target);
00327       PrintInfo("OK");
00328     }
00329     else
00330     {
00331       PrintError("No transform between " + source_frame_ + " and " + target_frame_);
00332     }
00333   }
00334 
00335   void TileMapPlugin::LoadConfig(const YAML::Node& node, const std::string& path)
00336   {
00337     if (swri_yaml_util::FindValue(node, CUSTOM_SOURCES_KEY))
00338     {
00339       const YAML::Node& sources = node[CUSTOM_SOURCES_KEY];
00340       YAML::Node::const_iterator source_iter;
00341       for (source_iter = sources.begin(); source_iter != sources.end(); source_iter++)
00342       {
00343         std::string type = "";
00344         if (swri_yaml_util::FindValue(*source_iter, TYPE_KEY))
00345         {
00346           // If the type isn't set, we'll assume it's WMTS
00347           (*source_iter)[TYPE_KEY] >> type;
00348         }
00349         boost::shared_ptr<TileSource> source;
00350         if (type == "wmts" || type.empty())
00351         {
00352           std::string name;
00353           std::string base_url;
00354           int max_zoom;
00355           (*source_iter)[NAME_KEY] >> name;
00356           (*source_iter)[BASE_URL_KEY] >> base_url;
00357           (*source_iter)[MAX_ZOOM_KEY] >> max_zoom;
00358           source = boost::make_shared<WmtsSource>(
00359               QString::fromStdString(name),
00360               QString::fromStdString(base_url),
00361               true,
00362               max_zoom);
00363         }
00364         else if (type == "bing")
00365         {
00366           std::string name;
00367           (*source_iter)[NAME_KEY] >> name;
00368           source = boost::make_shared<BingSource>(QString::fromStdString(name));
00369         }
00370         tile_sources_[source->GetName()] = source;
00371         ui_.source_combo->addItem(source->GetName());
00372       }
00373     }
00374 
00375     if (swri_yaml_util::FindValue(node, BING_API_KEY))
00376     {
00377       std::string key;
00378       node[BING_API_KEY] >> key;
00379       BingSource* source = static_cast<BingSource*>(tile_sources_[BING_NAME].get());
00380       source->SetApiKey(QString::fromStdString(key));
00381     }
00382     
00383     if (swri_yaml_util::FindValue(node, SOURCE_KEY))
00384     {
00385       std::string source;
00386       node[SOURCE_KEY] >> source;
00387 
00388       int index = ui_.source_combo->findText(QString::fromStdString(source), Qt::MatchExactly);
00389 
00390       if (index >= 0)
00391       {
00392         ui_.source_combo->setCurrentIndex(index);
00393       }
00394       
00395       SelectSource(QString::fromStdString(source));
00396     }
00397   }
00398 
00399   void TileMapPlugin::SaveConfig(YAML::Emitter& emitter, const std::string& path)
00400   {
00401     emitter << YAML::Key << CUSTOM_SOURCES_KEY << YAML::Value << YAML::BeginSeq;
00402 
00403     std::map<QString, boost::shared_ptr<TileSource> >::iterator iter;
00404     for (iter = tile_sources_.begin(); iter != tile_sources_.end(); iter++)
00405     {
00406       if (iter->second->IsCustom())
00407       {
00408         emitter << YAML::BeginMap;
00409         emitter << YAML::Key << BASE_URL_KEY << YAML::Value << iter->second->GetBaseUrl().toStdString();
00410         emitter << YAML::Key << MAX_ZOOM_KEY << YAML::Value << iter->second->GetMaxZoom();
00411         emitter << YAML::Key << NAME_KEY << YAML::Value << iter->second->GetName().toStdString();
00412         emitter << YAML::Key << TYPE_KEY << YAML::Value << iter->second->GetType().toStdString();
00413         emitter << YAML::EndMap;
00414       }
00415     }
00416     emitter << YAML::EndSeq;
00417 
00418     BingSource* bing_source = static_cast<BingSource*>(tile_sources_[BING_NAME].get());
00419     emitter << YAML::Key << BING_API_KEY <<
00420                YAML::Value << boost::trim_copy(bing_source->GetApiKey().toStdString());
00421 
00422     emitter << YAML::Key << SOURCE_KEY <<
00423                YAML::Value << boost::trim_copy(ui_.source_combo->currentText().toStdString());
00424   }
00425 
00426   void TileMapPlugin::selectTileSource(const boost::shared_ptr<TileSource>& tile_source)
00427   {
00428     last_height_ = 0; // This will force us to recalculate our view
00429     tile_map_.SetTileSource(tile_source);
00430     if (tile_source->GetType() == BingSource::BING_TYPE)
00431     {
00432       BingSource* bing_source = static_cast<BingSource*>(tile_source.get());
00433       ui_.base_url_text->setText(bing_source->GetApiKey());
00434     }
00435     else
00436     {
00437       ui_.base_url_text->setText(tile_source->GetBaseUrl());
00438     }
00439     ui_.max_zoom_spin_box->setValue(tile_source->GetMaxZoom());
00440   }
00441 
00442   void TileMapPlugin::startCustomEditing()
00443   {
00444     ui_.base_url_text->setEnabled(true);
00445     ui_.delete_button->setEnabled(true);
00446     ui_.max_zoom_spin_box->setEnabled(true);
00447     ui_.save_button->setEnabled(true);
00448   }
00449 
00450   void TileMapPlugin::stopCustomEditing()
00451   {
00452     ui_.base_url_text->setEnabled(false);
00453     ui_.delete_button->setEnabled(false);
00454     ui_.max_zoom_spin_box->setEnabled(false);
00455     ui_.save_button->setEnabled(false);
00456   }
00457 }
00458 


tile_map
Author(s): Marc Alban
autogenerated on Thu Jun 6 2019 18:51:19