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


tile_map
Author(s): Marc Alban
autogenerated on Thu Aug 24 2017 02:46:24