< Marble
Revision as of 23:30, 23 February 2010 by Earthwings (talk | contribs) (Created page with '{{Template:I18n/Language Navigation Bar|Editing Projects/Marble/LayerInterface}} {{TutorialBrowser| series=Marble C++ Tutorial| name=Custom Layers| pre=[[Projects/Marble/Marbl...')
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Editing Projects/Marble/LayerInterface

Languages: عربي | Asturianu | Català | Česky | Kaszëbsczi | Dansk | Deutsch | English | Esperanto | Español | Eesti | فارسی | Suomi | Français | Galego | Italiano | 日本語 | 한국어 | Norwegian | Polski | Português Brasileiro | Română | Русский | Svenska | Slovenčina | Slovenščina | српски | Türkçe | Tiếng Việt | Українська | 简体中文 | 繁體中文

Custom Layers
Tutorial Series   Marble C++ Tutorial
Previous   Tutorial 2 - Marble's GeoPainter
What's Next   Tutorial 3 - Customize your Marble Widget
Further Reading   n/a

The previous tutorial showed how to override the customPaint() method in MarbleWidget to paint on top of the map. It is also possible to paint at different layers. This way it is possible to paint beyond info boxes or other items on top of the surface. It is even possible to paint behind the map.

To achieve this, we do not derive from MarbleWidget, but create our own Marble::LayerInterface class. After passing it to Marble, it will be included in painting similar to how customPaint() was called. This time however we are able to specify at which layer to paint.

To illustrate the painting in different layers, the code below implements the ability to dynamically switch the layer by pressing '+'. Notice how this changes the window title and results in different effects: When painting in the "STARS" layer, you won't see anything -- we'll paint behind the map. In the "SURFACE" layer, city names and other placemarks will be painted on top of us. In contrast, "ORBIT" will make us paint over placemarks, while float items (info boxes) still paint above us. This will change when we paint in the "USER TOOLS" layer.

  1. include <marble/MarbleWidget.h>
  2. include <marble/MarbleMap.h>
  3. include <marble/MarbleModel.h>
  4. include <marble/GeoPainter.h>
  5. include <marble/LayerInterface.h>
  1. include <QtCore/QTime>
  2. include <QtCore/QTimer>
  3. include <QtGui/QApplication>
  4. include <QtGui/QKeyEvent>

using namespace Marble;

class MyPaintLayer : public QObject, public LayerInterface { public:

   // Constructor
   MyPaintLayer(MarbleWidget* widget);
   // Implemented from LayerInterface
   virtual QStringList renderPosition() const;
   // Implemented from LayerInterface
   virtual bool render( GeoPainter *painter, ViewportParams *viewport,
      const QString& renderPos = "NONE", GeoSceneLayer * layer = 0 );
   // Overriding QObject
   virtual bool eventFilter(QObject *obj, QEvent *event);
   GeoDataCoordinates approximate(const GeoDataCoordinates &base, qreal angle, qreal dist) const;


   MarbleWidget* m_widget;
   int m_index;


MyPaintLayer::MyPaintLayer(MarbleWidget* widget) : m_widget(widget), m_index(0) {

   // nothing to do


QStringList MyPaintLayer::renderPosition() const {

   // We will paint in exactly one of the following layers.
   // The current one can be changed by pressing the '+' key
   QStringList layers = QStringList() << "SURFACE" << "HOVERS_ABOVE_SURFACE";
   layers << "ORBIT" << "USER_TOOLS" << "STARS";
   int index = m_index % layers.size();
   return QStringList() << layers.at(index);


bool MyPaintLayer::eventFilter(QObject *obj, QEvent *event) {

   // Adjust the current layer when '+' is pressed
   if (event->type() == QEvent::KeyPress)
       QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
       if (keyEvent->key() == Qt::Key_Plus) {
           return true;
   return false;


GeoDataCoordinates MyPaintLayer::approximate(const GeoDataCoordinates &base, qreal angle, qreal dist) const {

   // This is just a rough estimation that ignores projections.
   // It only works for short distances. Don't use in real code.
   GeoDataCoordinates::Unit deg = GeoDataCoordinates::Degree;
   return GeoDataCoordinates ( base.longitude(deg) + 1.5 * dist * sin(angle),

base.latitude(deg) + dist * cos(angle), 0.0, deg); }

bool MyPaintLayer::render( GeoPainter *painter, ViewportParams *viewport,

   const QString& renderPos, GeoSceneLayer * layer )


   // Have window title reflect the current paint layer
   GeoDataCoordinates home(8.4, 48.0, 0.0, GeoDataCoordinates::Degree);
   QTime now = QTime::currentTime();
   painter->setRenderHint(QPainter::Antialiasing, true);
   // Large circle built by 60 small circles
   painter->setPen( QPen(QBrush(QColor::fromRgb(255,255,255,200)), 3.0, Qt::SolidLine, Qt::RoundCap ) );
   for (int i=0; i<60; ++i)
       painter->drawEllipse(approximate(home, M_PI * i / 30.0, 1.0), 5, 5);
   // hour, minute, second hand
   painter->drawLine(home, approximate(home, M_PI * now.minute() / 30.0, 0.75));
   painter->drawLine(home, approximate(home, M_PI * now.hour() / 6.0, 0.5));
   painter->setPen(QPen(QBrush(Qt::red), 4.0, Qt::SolidLine, Qt::RoundCap ));
   painter->drawLine(home, approximate(home, M_PI * now.second() / 30.0, 1.0));
   return true;


int main(int argc, char** argv) {

   QApplication app(argc,argv);
   MarbleWidget *mapWidget = new MarbleWidget;
   // Create and register our paint layer
   MyPaintLayer* layer = new MyPaintLayer(mapWidget);
   // Install an event handler: Pressing + will change the layer we paint at
   // Finish widget creation. Update each second to give the clock second resolution
   QTimer seconds;
   QObject::connect(&seconds, SIGNAL(timeout()), mapWidget, SLOT(updateChangedMap()));
   return app.exec();


Save the code above as my_marble.cpp and compile it:

g++ -I /usr/include/qt4/ -o my_marble my_marble.cpp -lmarblewidget -lQtGui

If things go fine, execute ./my_marble and you end up with a globe view similar to this: Marble-paintlayer.png

Content is available under Creative Commons License SA 4.0 unless otherwise noted.