[Marble-devel] RFC: refactoring of tile loading code

Jens-Michael Hoffmann jensmh at gmx.de
Fri Feb 5 19:44:07 CET 2010


-------------- next part --------------
commit ef1a0883c822d4aff8c5002a3a26c130f8c9e83b
Author: Jens-Michael Hoffmann <jensmh at gmx.de>
Date:   Fri Feb 5 19:40:08 2010 +0100

    Squashed commit of the following:
    
    commit 55496ce15bc8af2d5b312258d6726cc5a6a3c4c7
    Merge: b3df354 c11d59e
    Author: Jens-Michael Hoffmann <jensmh at gmx.de>
    Date:   Fri Feb 5 19:02:45 2010 +0100
    
        Merge branch 'master' into tileloader-refactoring
    
    commit b3df354fe8894a4ab80a58db641378604d05724a
    Author: Jens-Michael Hoffmann <jensmh at gmx.de>
    Date:   Fri Feb 5 18:59:10 2010 +0100
    
        Refactoring of tile loading.
    
        new classes:
        - SimpleTextureTile
        - SimpleTileLoader
    
        AbstractTile:
        - m_created removed, value is derived from the SimpleTextureTiles inside
          TextureTile
        - setState removed, value is derived from the states of the SimpleTextureTiles
    
        MarbleMap:
        - remove handling of clouds on/off, unclear where this should go, was in
          MergedLayerDecorator before
    
        MergedLayerDecorator:
        - remove handling of clouds, this is now done in a generic way inside
          with SimpleTextureTile/TextureTile/TileLoader
        - uses now TileLoader instead of instantiating a TextureTile "manually"
    
        TextureTile:
        - loadDataset removed
        - setImage removed
        - now consists of an array of base tiles, which are merged in a result tile (formerly known as rawtile)
        - m_forMergedLayerDecorator added
    
        TileLoader:
        - loadTile gets special parameter forMergedLayerDecorator
        - uses SimpleTileLoader to load the "base" tiles
    
        remaining issues:
        - where should "clouds visible" property go?
          MarbleModel comes to mind as this is one level above MergeLayerDecorator
        - instead of TileLoader::loadTile having the forMergedLayerDecorator parameter
          one could think of using SimpleTileLoader inside MergedLayerDecorator
        - extend DGML texture element or below with "mergeRule" possible values: Copy, Multiply
    
    commit abb42222ffd1efe93ed9ee22cbe81b5292ab4eab
    Author: Jens-Michael Hoffmann <jensmh at gmx.de>
    Date:   Thu Feb 4 21:12:13 2010 +0100
    
        GeoSceneTexture: add method hasMaximumTileLevel().
    
    commit ab099bd50d404ec5dd643c1e29bbe4eb5400454d
    Author: Jens-Michael Hoffmann <jensmh at gmx.de>
    Date:   Wed Feb 3 22:43:27 2010 +0100
    
        TileLoaderHelper: we can pass a pointer to a const GeoSceneTexture here.
    
    commit d906b1ee5bf38611f537f18b64a49801d18fc40f
    Author: Jens-Michael Hoffmann <jensmh at gmx.de>
    Date:   Wed Feb 3 21:08:53 2010 +0100
    
        TileLoader: remove old updateTile slot, in which the qimage data was read
        from the filesystem again instead of using the in-memory copy.
    
    commit be1d1662eda275b2bd1fbc6d60daf6027c784d4a
    Author: Jens-Michael Hoffmann <jensmh at gmx.de>
    Date:   Wed Feb 3 20:51:09 2010 +0100
    
        Fix compilation after constness fixes in geodata/scene.
    
    commit 08671eabc1dd48112f409b6bb182dba379c2d8f0
    Author: Jens-Michael Hoffmann <jensmh at gmx.de>
    Date:   Wed Feb 3 20:04:34 2010 +0100
    
        Re-add one patch that was committed to master by mistake and then reverted
        and now after merging master into this branch, the change was gone here, too.
    
    commit 5731ff84262800a257b788aaf2ce5314e3b153c4
    Merge: 35fb787 b72f3ee
    Author: Jens-Michael Hoffmann <jensmh at gmx.de>
    Date:   Wed Feb 3 19:50:57 2010 +0100
    
        Merge branch 'master' into tileloader-refactoring
    
    commit b72f3ee374c89cad2e9fe89dac27a9d23467b70d
    Author: Jens-Michael Hoffmann <jensmh at gmx.de>
    Date:   Mon Feb 1 15:43:31 2010 +0100
    
        Revert "TileLoader: add MapThemeManager parameter to constructor."
    
        This reverts commit d550f4a1257d7e0351f51b6e5e4f21faef077304,
        because it was committed to the wrong branch.
    
    commit 74c04275c68a43f9a7e63365ad1df6bab562c4be
    Author: Jens-Michael Hoffmann <jensmh at gmx.de>
    Date:   Mon Feb 1 15:35:10 2010 +0100
    
        TileLoader: add MapThemeManager parameter to constructor.
    
    commit 35fb787bcd3c27924f94336d99324a8b7c815e13
    Author: Jens-Michael Hoffmann <jensmh at gmx.de>
    Date:   Mon Feb 1 15:43:31 2010 +0100
    
        Revert "TileLoader: add MapThemeManager parameter to constructor."
    
        This reverts commit d550f4a1257d7e0351f51b6e5e4f21faef077304,
        because it was committed to the wrong branch.
    
    commit 51992ff7cccf6332d9dde8de57c4c4836cf2f9f2
    Author: Jens-Michael Hoffmann <jensmh at gmx.de>
    Date:   Wed Feb 3 18:18:52 2010 +0100
    
        MergedLayerDecorator: use TileLoader instead of maunally instanciating a
        TextureTile and then calling textureTile::loadDataset.
        In order to prevent infinite recursion, there is a (hard coded) check in
        TileLoader::mergeDecorations to prevent this (half heartedly).
        Loading/displaying of clouds layer if cloud tiles are not available locally
        remains broken.
    
    commit 753571e6708892a867ce0d69f6ac3267ad2ebc81
    Author: Jens-Michael Hoffmann <jensmh at gmx.de>
    Date:   Mon Feb 1 22:03:46 2010 +0100
    
        TileLoader::initTextureLayers: find really all texture layers.
    
        A map theme may have (and in fact some do) several texture layers, so
        we have to iterate through all layers to find them.
    
    commit 5f7b580041141d9add5921e091f57151caced8ad
    Author: Jens-Michael Hoffmann <jensmh at gmx.de>
    Date:   Mon Feb 1 20:58:06 2010 +0100
    
        TileLoader: remove setLayer method and corresponding data member.
    
    commit 1f3bb64dda957e6a1d1ec859d05adb1ba98e0aa2
    Author: Jens-Michael Hoffmann <jensmh at gmx.de>
    Date:   Mon Feb 1 20:50:03 2010 +0100
    
        TileLoader: make maximumTileLevel() static again and add texture layer parameter.
    
        This way the texture layer can be removed from TileLoader's state.
        The parameter of TileLoaderHelp::themeStr() had to be constified for this to compile.
    
    commit 6d6cce8233fe23cff423a05f94e937b7140de5d8
    Author: Jens-Michael Hoffmann <jensmh at gmx.de>
    Date:   Mon Feb 1 20:24:34 2010 +0100
    
        TileLoader: remove methods tileWidth() and tileHeight().
    
        The result of these methods depend on a given texture layer. As TileLoader's
        state should not contain a texture layer, move this to
        AbstractScanlineTextureMapper. A different possibility would have been to
        add a GeoSceneTexture parameter, but since these methods are not part of
        the tile loading core business, but more convenience methods for
        the texture mappers, I like it more this way.
    
    commit d29a8f124b037d36e3a2d3b6c085a0383d26a0a3
    Author: Jens-Michael Hoffmann <jensmh at gmx.de>
    Date:   Mon Feb 1 19:29:31 2010 +0100
    
        TileLoader: remove unused signal paintTile.
    
    commit e64b2c3f723a8b7bf34afe29800a02e387c5a5af
    Author: Jens-Michael Hoffmann <jensmh at gmx.de>
    Date:   Mon Feb 1 19:12:20 2010 +0100
    
        TileLoader: switch updateTile methods from using the current texture layer
        over to the hash of texture layers and use the given TileId to select.
    
    commit 19eb773a47e07f8845d166f6e35b9e9d2b5dcaac
    Author: Jens-Michael Hoffmann <jensmh at gmx.de>
    Date:   Mon Feb 1 19:00:22 2010 +0100
    
        TileLoader: remove methods globalWidth() and globalHeight().
    
        As these methods depend on thew current texture layer and
        AbstractScanlineTextureMapper was the only user of these methods,
        move them there.
    
    commit 0b809a47b1f98e8772e36bdb125ee93a46db54ec
    Author: Jens-Michael Hoffmann <jensmh at gmx.de>
    Date:   Mon Feb 1 18:17:57 2010 +0100
    
        TileLoader: remove method layer() which returns the current texture layer.
    
        The current texture layer should not be part of TileLoader's state. So
        as a step towards the removal of this, remove method layer() which returns
        it and switch users of TileLoader::layer() over to alternatives.
    
    commit 80ff4a725341e4affe790733f41b3602f91eb3b4
    Author: Jens-Michael Hoffmann <jensmh at gmx.de>
    Date:   Mon Feb 1 17:26:05 2010 +0100
    
        TileLoader::loadTile: switch over to new internal hash of texture layers.
    
    commit bb84daf3df1bd29f7c5f4b9a849da5daa31224d8
    Author: Jens-Michael Hoffmann <jensmh at gmx.de>
    Date:   Mon Feb 1 16:37:34 2010 +0100
    
        TileLoader: add hash with texture layers.
    
    commit 0652a52d7a544b6ce848f716dba13a1fc7239df7
    Author: Jens-Michael Hoffmann <jensmh at gmx.de>
    Date:   Mon Feb 1 15:47:12 2010 +0100
    
        Remove extra empty line.
    
    commit a5ab1f1d5f3d17594c228812d6464906eebcb5c6
    Author: Jens-Michael Hoffmann <jensmh at gmx.de>
    Date:   Mon Feb 1 15:35:10 2010 +0100
    
        TileLoader: add MapThemeManager parameter to constructor.
    
    commit a9356a9450d6d26a91c5e2910315f305ce1dd7b8
    Author: Jens-Michael Hoffmann <jensmh at gmx.de>
    Date:   Mon Feb 1 15:21:00 2010 +0100
    
        MapThemeManager: add method mapThemes() which returns all map themes.
    
    commit 34e629a4f4854550b173c2115fe78b18711bd8cc
    Author: Jens-Michael Hoffmann <jensmh at gmx.de>
    Date:   Mon Feb 1 13:24:00 2010 +0100
    
        TileId: add map theme id, needed do identify tiles across different map themes.
    
        This change breaks the cloudes layer again, this time it is because clouds
        rendering relies on TileLoader::updateTile triggering MarbleModel::paintTile,
        which is only done if the tile is found to be currently displayed, which is
        true for the underlying tile of the bluemarble theme, but not for the
        cloud tile. Now that TileId contains the map theme id, the cloud tile
        is no longer recognized as the bluemarble tile.

diff --git a/marble/src/lib/AbstractScanlineTextureMapper.cpp b/marble/src/lib/AbstractScanlineTextureMapper.cpp
index 5f59286..849f56b 100644
--- a/marble/src/lib/AbstractScanlineTextureMapper.cpp
+++ b/marble/src/lib/AbstractScanlineTextureMapper.cpp
@@ -10,6 +10,8 @@
 
 #include "AbstractScanlineTextureMapper.h"
 
+#include <QtGui/QImage>
+
 #include "MarbleDebug.h"
 #include "TextureTile.h"
 #include "TileId.h"
@@ -20,7 +22,9 @@
 
 using namespace Marble;
 
-AbstractScanlineTextureMapper::AbstractScanlineTextureMapper( TileLoader *tileLoader, QObject * parent )
+AbstractScanlineTextureMapper::AbstractScanlineTextureMapper( GeoSceneTexture *textureLayer,
+                                                              TileLoader *tileLoader,
+                                                              QObject *parent )
     : QObject( parent ),
       m_interpolate( false ),
       m_maxGlobalX( 0 ),
@@ -32,6 +36,7 @@ AbstractScanlineTextureMapper::AbstractScanlineTextureMapper( TileLoader *tileLo
       m_toTileCoordinatesLon( 0.0 ),
       m_toTileCoordinatesLat( 0.0 ),
       m_interlaced( false ),
+      m_textureLayer( textureLayer ),
       m_tileLoader( tileLoader ),
       m_tile( 0 ),
       m_tileLevel( 0 ),
@@ -42,42 +47,34 @@ AbstractScanlineTextureMapper::AbstractScanlineTextureMapper( TileLoader *tileLo
       m_globalWidth( 0 ),
       m_globalHeight( 0 ),
       m_normGlobalWidth( 0.0 ),
-      m_normGlobalHeight( 0.0 )
+      m_normGlobalHeight( 0.0 ),
+      m_mapThemeIdHash( textureLayer ? qHash( textureLayer->sourceDir() ) : 0 )
 {
-    GeoSceneTexture * texture = 0;
-
-    if ( tileLoader ) {
-        GeoSceneLayer * layer = tileLoader->layer();
-        if ( layer ) {
-            texture = static_cast<GeoSceneTexture *>( layer->groundDataset() );
-        }
-    }
-
-    m_tileProjection = tileLoader && texture
-                        ? texture->projection()
-                        : GeoSceneTexture::Equirectangular;
+    m_tileProjection = textureLayer ? textureLayer->projection()
+        : GeoSceneTexture::Equirectangular;
 
     connect( m_tileLoader, SIGNAL( tileUpdateAvailable() ), 
              this,         SLOT( notifyMapChanged() ) );
 
     detectMaxTileLevel();
+    initTileSize();
 }
 
 
 AbstractScanlineTextureMapper::~AbstractScanlineTextureMapper()
 {
       m_tileLoader->disconnect();
-//      delete m_tileLoader;
 }
 
 
 void AbstractScanlineTextureMapper::setLayer( GeoSceneLayer * layer )
 {
-    m_tileLoader->setLayer( layer );
-    GeoSceneTexture * texture = static_cast<GeoSceneTexture *>( layer->groundDataset() );
-    m_tileProjection = texture->projection();
+    m_textureLayer = static_cast<GeoSceneTexture *>( layer->groundDataset() );
+    m_tileProjection = m_textureLayer->projection();
+    m_mapThemeIdHash = qHash( m_textureLayer->sourceDir() );
     m_tileLevel = -1;
     detectMaxTileLevel();
+    initTileSize();
 }
 
 
@@ -89,7 +86,7 @@ void AbstractScanlineTextureMapper::selectTileLevel( ViewParams* viewParams )
     // the tile level from tilesize and the globe radius via log(2)
 
     qreal  linearLevel = ( 2.0 * (qreal)( radius )
-			    / (qreal) ( m_tileLoader->tileWidth() ) );
+			    / (qreal) ( m_tileSize.width() ) );
     int     tileLevel   = 0;
 
     if ( linearLevel < 1.0 )
@@ -115,9 +112,9 @@ void AbstractScanlineTextureMapper::tileLevelInit( int tileLevel )
     //    mDebug() << "Texture Level was set to: " << tileLevel;
     m_tileLevel = tileLevel;
 
-    m_globalWidth = m_tileLoader->globalWidth( m_tileLevel );
+    initGlobalWidth();
     m_normGlobalWidth = (qreal)( m_globalWidth / ( 2 * M_PI ) );
-    m_globalHeight = m_tileLoader->globalHeight( m_tileLevel );
+    initGlobalHeight();
     m_normGlobalHeight = (qreal)( m_globalHeight /  M_PI );
 
     m_maxGlobalX = m_globalWidth  - 1;
@@ -170,9 +167,9 @@ void AbstractScanlineTextureMapper::pixelValueF(qreal lon,
     // same tile. However at the tile border we might "fall off". If that 
     // happens we need to find out the next tile that needs to be loaded.
 
-    if ( posX  >= (qreal)( m_tileLoader->tileWidth() ) 
+    if ( posX  >= (qreal)( m_tileSize.width() ) 
          || posX < 0.0
-         || posY >= (qreal)( m_tileLoader->tileHeight() )
+         || posY >= (qreal)( m_tileSize.height() )
          || posY < 0.0 )
     {
         nextTile( posX, posY );
@@ -203,9 +200,9 @@ void AbstractScanlineTextureMapper::pixelValue(qreal lon,
     // same tile. However at the tile border we might "fall off". If that 
     // happens we need to find out the next tile that needs to be loaded.
 
-    if ( iPosX  >= m_tileLoader->tileWidth() 
+    if ( iPosX  >= m_tileSize.width() 
          || iPosX < 0
-         || iPosY >= m_tileLoader->tileHeight()
+         || iPosY >= m_tileSize.height()
          || iPosY < 0 )
     {
         nextTile( iPosX, iPosY );
@@ -257,8 +254,8 @@ void AbstractScanlineTextureMapper::pixelValueApproxF(const qreal& lon,
         qreal itLon = m_prevLon + m_toTileCoordinatesLon;
         qreal itLat = m_prevLat + m_toTileCoordinatesLat;
 
-        const int tileWidth = m_tileLoader->tileWidth();
-        const int tileHeight = m_tileLoader->tileHeight();
+        const int tileWidth = m_tileSize.width();
+        const int tileHeight = m_tileSize.height();
 
         // int oldR = 0;
         // int oldG = 0;
@@ -401,8 +398,8 @@ void AbstractScanlineTextureMapper::pixelValueApprox(const qreal& lon,
         int itLon = (int)( ( m_prevLon + m_toTileCoordinatesLon ) * 128.0 );
         int itLat = (int)( ( m_prevLat + m_toTileCoordinatesLat ) * 128.0 );
 
-        const int tileWidth = m_tileLoader->tileWidth();
-        const int tileHeight = m_tileLoader->tileHeight();
+        const int tileWidth = m_tileSize.width();
+        const int tileHeight = m_tileSize.height();
 
         const bool alwaysCheckTileRange =
                 isOutOfTileRange( itLon, itLat, itStepLon, itStepLat,
@@ -529,21 +526,21 @@ void AbstractScanlineTextureMapper::nextTile( int &posX, int &posY )
     // tileCol counts the tile columns left from the current tile.
     // tileRow counts the tile rows on the top from the current tile.
 
-    int tileCol = lon / m_tileLoader->tileWidth();
-    int tileRow = lat / m_tileLoader->tileHeight();
+    int tileCol = lon / m_tileSize.width();
+    int tileRow = lat / m_tileSize.height();
 
-    m_tile = m_tileLoader->loadTile( TileId( m_tileLevel, tileCol, tileRow ));
+    m_tile = m_tileLoader->loadTile( TileId( m_mapThemeIdHash, m_tileLevel, tileCol, tileRow ));
 
     // Update position variables:
     // m_tilePosX/Y stores the position of the tiles in 
     // global texture coordinates 
     // ( origin upper left, measured in pixels )
 
-    m_tilePosX = tileCol * m_tileLoader->tileWidth();
+    m_tilePosX = tileCol * m_tileSize.width();
     m_toTileCoordinatesLon = (qreal)(m_globalWidth / 2 - m_tilePosX);
     posX = lon - m_tilePosX;
 
-    m_tilePosY = tileRow * m_tileLoader->tileHeight();
+    m_tilePosY = tileRow * m_tileSize.height();
     m_toTileCoordinatesLat = (qreal)(m_globalHeight / 2 - m_tilePosY);
     posY = lat - m_tilePosY;
 }
@@ -564,21 +561,21 @@ void AbstractScanlineTextureMapper::nextTile( qreal &posX, qreal &posY )
     // tileCol counts the tile columns left from the current tile.
     // tileRow counts the tile rows on the top from the current tile.
 
-    int tileCol = lon / m_tileLoader->tileWidth();
-    int tileRow = lat / m_tileLoader->tileHeight();
+    int tileCol = lon / m_tileSize.width();
+    int tileRow = lat / m_tileSize.height();
 
-    m_tile = m_tileLoader->loadTile( TileId( m_tileLevel, tileCol, tileRow ));
+    m_tile = m_tileLoader->loadTile( TileId( m_mapThemeIdHash, m_tileLevel, tileCol, tileRow ));
 
     // Update position variables:
     // m_tilePosX/Y stores the position of the tiles in 
     // global texture coordinates 
     // ( origin upper left, measured in pixels )
 
-    m_tilePosX = tileCol * m_tileLoader->tileWidth();
+    m_tilePosX = tileCol * m_tileSize.width();
     m_toTileCoordinatesLon = (qreal)(m_globalWidth / 2 - m_tilePosX);
     posX = lon - m_tilePosX;
 
-    m_tilePosY = tileRow * m_tileLoader->tileHeight();
+    m_tilePosY = tileRow * m_tileSize.height();
     m_toTileCoordinatesLat = (qreal)(m_globalHeight / 2 - m_tilePosY);
     posY = lat - m_tilePosY;
 }
@@ -586,14 +583,39 @@ void AbstractScanlineTextureMapper::nextTile( qreal &posX, qreal &posY )
 void AbstractScanlineTextureMapper::notifyMapChanged()
 {
     detectMaxTileLevel();
-//    mDebug() << "MAPCHANGED";
+    //mDebug() << "AbstractScanlineTextureMapper: emitting mapChanged";
     emit mapChanged();
 }
 
 void AbstractScanlineTextureMapper::detectMaxTileLevel()
 {
-    m_maxTileLevel = m_tileLoader->maximumTileLevel();
+    m_maxTileLevel = TileLoader::maximumTileLevel( m_textureLayer );
 //    mDebug() << "MaxTileLevel: " << m_maxTileLevel;
 }
 
+void AbstractScanlineTextureMapper::initGlobalWidth()
+{
+    m_globalWidth = m_tileSize.width()
+        * TileLoaderHelper::levelToColumn( m_textureLayer->levelZeroColumns(), m_tileLevel );
+}
+
+void AbstractScanlineTextureMapper::initGlobalHeight()
+{
+    m_globalHeight = m_tileSize.height()
+        * TileLoaderHelper::levelToRow( m_textureLayer->levelZeroRows(), m_tileLevel );
+}
+
+void AbstractScanlineTextureMapper::initTileSize()
+{
+    if ( !m_textureLayer || !m_tileLoader )
+        return;
+
+    Q_ASSERT( m_textureLayer );
+    Q_ASSERT( m_tileLoader );
+    TileId id( m_textureLayer->sourceDir(), 0, 0, 0 );
+    TextureTile * const testTile = m_tileLoader->loadTile( id );
+    Q_ASSERT( testTile );
+    m_tileSize = testTile->rawtile().size();
+}
+
 #include "AbstractScanlineTextureMapper.moc"
diff --git a/marble/src/lib/AbstractScanlineTextureMapper.h b/marble/src/lib/AbstractScanlineTextureMapper.h
index 81e88f5..1f3e763 100644
--- a/marble/src/lib/AbstractScanlineTextureMapper.h
+++ b/marble/src/lib/AbstractScanlineTextureMapper.h
@@ -13,6 +13,7 @@
 #define ABSTRACTSCANLINETEXTUREMAPPER_H
 
 #include <QtCore/QObject>
+#include <QtCore/QSize>
 #include <QtGui/QColor>
 
 #include <cmath>
@@ -35,7 +36,8 @@ class AbstractScanlineTextureMapper : public QObject
     Q_OBJECT
 
 public:
-    explicit AbstractScanlineTextureMapper( TileLoader *tileLoader, QObject * parent=0 );
+    AbstractScanlineTextureMapper( GeoSceneTexture *textureLayer, TileLoader *tileLoader,
+                                   QObject *parent = 0 );
     ~AbstractScanlineTextureMapper();
 
     virtual void mapTexture( ViewParams *viewParams ) = 0;
@@ -121,6 +123,9 @@ public:
 
     // ------------------------
     // Tile stuff
+    GeoSceneTexture *m_textureLayer;
+    /// size of the tiles of of the current texture layer
+    QSize m_tileSize;
     TileLoader  *m_tileLoader;
     GeoSceneTexture::Projection m_tileProjection;
 
@@ -142,10 +147,15 @@ public:
 
  private:
     Q_DISABLE_COPY( AbstractScanlineTextureMapper )
+    void initGlobalWidth();
+    void initGlobalHeight();
+    void initTileSize();
+
     int         m_globalWidth;
     int         m_globalHeight;
     qreal       m_normGlobalWidth;
     qreal       m_normGlobalHeight;
+    uint        m_mapThemeIdHash;
 };
 
 inline void AbstractScanlineTextureMapper::setMaxTileLevel( int level )
diff --git a/marble/src/lib/AbstractTile.cpp b/marble/src/lib/AbstractTile.cpp
index b38bff3..f0ac99d 100644
--- a/marble/src/lib/AbstractTile.cpp
+++ b/marble/src/lib/AbstractTile.cpp
@@ -13,8 +13,6 @@
 #include "AbstractTile.h"
 #include "AbstractTile_p.h"
 
-#include <QtCore/QDateTime>
-
 #include "TileId.h"
 
 namespace Marble
@@ -23,8 +21,7 @@ namespace Marble
 AbstractTilePrivate::AbstractTilePrivate( const TileId& id )
     : m_id( id ),
       m_used( false ),
-      m_state( AbstractTile::TileEmpty ),
-      m_created( QDateTime::currentDateTime() )
+      m_state( AbstractTile::TileEmpty )
 {
 }
 
@@ -72,24 +69,6 @@ AbstractTile::TileState AbstractTile::state() const
     return d->m_state;
 }
 
-void AbstractTile::setState( TileState state )
-{
-    Q_D( AbstractTile );
-    d->m_state = state;
-}
-
-const QDateTime & AbstractTile::created() const
-{
-    Q_D( const AbstractTile );
-    return d->m_created;
-}
-
-void AbstractTile::setCreated( const QDateTime &created )
-{
-    Q_D( AbstractTile );
-    d->m_created = created;
-}
-
 }
 
 #include "AbstractTile.moc"
diff --git a/marble/src/lib/AbstractTile.h b/marble/src/lib/AbstractTile.h
index a43124a..93275b4 100644
--- a/marble/src/lib/AbstractTile.h
+++ b/marble/src/lib/AbstractTile.h
@@ -56,10 +56,6 @@ class AbstractTile : public QObject
     void setUsed( bool used );
 
     TileState state() const;
-    void setState( TileState state );
-
-    const QDateTime & created() const;
-    void setCreated( const QDateTime &created );
 
 protected:
     AbstractTilePrivate * const d_ptr;
diff --git a/marble/src/lib/AbstractTile_p.h b/marble/src/lib/AbstractTile_p.h
index 40c63dd..787a17c 100644
--- a/marble/src/lib/AbstractTile_p.h
+++ b/marble/src/lib/AbstractTile_p.h
@@ -12,19 +12,14 @@
 // Description: AbstractTile contains the base class for a single quadtile 
 //
 
-
 #ifndef MARBLE_ABSTRACTTILE_P_H
 #define MARBLE_ABSTRACTTILE_P_H
 
-#include <QtCore/QDateTime>
-
 #include "TileId.h"
 
-
 namespace Marble
 {
 
-
 class AbstractTilePrivate
 {
  public:
@@ -35,8 +30,6 @@ class AbstractTilePrivate
     bool      m_used;
     AbstractTile::TileState m_state;
 
-    QDateTime m_created;
-
     AbstractTile * q_ptr;
 };
 
diff --git a/marble/src/lib/CMakeLists.txt b/marble/src/lib/CMakeLists.txt
index 80a7cd9..19abc18 100644
--- a/marble/src/lib/CMakeLists.txt
+++ b/marble/src/lib/CMakeLists.txt
@@ -74,7 +74,8 @@ set(marblewidget_SRCS
     MarbleGeometryModel.cpp
     MarbleDataFacade.cpp
     MarbleDebug.cpp
-
+    SimpleTextureTile.cpp
+    SimpleTileLoader.cpp
     QtMarbleConfigDialog.cpp
     ClipPainter.cpp
     DownloadPolicy.cpp
diff --git a/marble/src/lib/EquirectScanlineTextureMapper.cpp b/marble/src/lib/EquirectScanlineTextureMapper.cpp
index 979272a..7d114ea 100644
--- a/marble/src/lib/EquirectScanlineTextureMapper.cpp
+++ b/marble/src/lib/EquirectScanlineTextureMapper.cpp
@@ -29,9 +29,10 @@
 
 using namespace Marble;
 
-EquirectScanlineTextureMapper::EquirectScanlineTextureMapper( TileLoader *tileLoader,
-                                                              QObject * parent )
-    : AbstractScanlineTextureMapper( tileLoader, parent ),
+EquirectScanlineTextureMapper::EquirectScanlineTextureMapper( GeoSceneTexture *textureLayer,
+                                                              TileLoader *tileLoader,
+                                                              QObject *parent )
+    : AbstractScanlineTextureMapper( textureLayer, tileLoader, parent ),
       m_oldCenterLon( 0.0 ),
       m_oldYPaintedTop( 0 )
 {
diff --git a/marble/src/lib/EquirectScanlineTextureMapper.h b/marble/src/lib/EquirectScanlineTextureMapper.h
index 8d11081..bc13f17 100644
--- a/marble/src/lib/EquirectScanlineTextureMapper.h
+++ b/marble/src/lib/EquirectScanlineTextureMapper.h
@@ -24,8 +24,8 @@ class EquirectScanlineTextureMapper : public AbstractScanlineTextureMapper
     Q_OBJECT
 
  public:
-    explicit EquirectScanlineTextureMapper( TileLoader *tileLoader,
-					    QObject    *parent = 0 );
+    EquirectScanlineTextureMapper( GeoSceneTexture *textureLayer, TileLoader *tileLoader,
+                                   QObject *parent = 0 );
     void mapTexture( ViewParams *viewParams );
 
  private:
diff --git a/marble/src/lib/MapThemeManager.cpp b/marble/src/lib/MapThemeManager.cpp
index 65aad98..27381e8 100644
--- a/marble/src/lib/MapThemeManager.cpp
+++ b/marble/src/lib/MapThemeManager.cpp
@@ -75,6 +75,18 @@ MapThemeManager::~MapThemeManager()
     delete d;
 }
 
+QList<GeoSceneDocument const*> MapThemeManager::mapThemes() const
+{
+    QList<GeoSceneDocument const*> result;
+    const QStringList mapThemes = findMapThemes();
+    QStringList::const_iterator pos = mapThemes.constBegin();
+    QStringList::const_iterator const end = mapThemes.constEnd();
+    for (; pos != end; ++pos ) {
+        result.append( loadMapTheme( *pos ));
+    }
+    return result;
+}
+
 void MapThemeManager::initialize()
 {
     initFileSystemWatcher();
diff --git a/marble/src/lib/MapThemeManager.h b/marble/src/lib/MapThemeManager.h
index c4d433b..3cd0de5 100644
--- a/marble/src/lib/MapThemeManager.h
+++ b/marble/src/lib/MapThemeManager.h
@@ -55,7 +55,12 @@ class MARBLE_EXPORT MapThemeManager : public QObject
  public:
     explicit MapThemeManager(QObject *parent = 0);
     ~MapThemeManager();
-    
+
+    /**
+     * @brief Returns a list of all locally available map themes
+     */
+    QList<GeoSceneDocument const*> mapThemes() const;
+
     /**
      * @brief Provides a model of the locally existing themes. 
      *
diff --git a/marble/src/lib/MarbleMap.cpp b/marble/src/lib/MarbleMap.cpp
index b449bb7..196dbf5 100644
--- a/marble/src/lib/MarbleMap.cpp
+++ b/marble/src/lib/MarbleMap.cpp
@@ -635,7 +635,8 @@ bool MarbleMap::showGrid() const
 
 bool MarbleMap::showClouds() const
 {
-    return d->m_model->layerDecorator()->showClouds();
+    // TODO
+    return true;
 }
 
 bool MarbleMap::showAtmosphere() const
@@ -1034,15 +1035,9 @@ void MarbleMap::setShowCrosshairs( bool visible )
 
 void MarbleMap::setShowClouds( bool visible )
 {
-    bool previousVisible = d->m_model->layerDecorator()->showClouds();
-
-    d->m_model->layerDecorator()->setShowClouds( visible );
-
-    if ( previousVisible != visible ) {
-        mDebug() << "Changing cloud layer";
-        d->m_model->update();
-    }
+    // TODO
 }
+
 void MarbleMap::setShowTileId( bool visible )
 {
 
diff --git a/marble/src/lib/MarbleModel.cpp b/marble/src/lib/MarbleModel.cpp
index 8aff1a0..77528a6 100644
--- a/marble/src/lib/MarbleModel.cpp
+++ b/marble/src/lib/MarbleModel.cpp
@@ -23,6 +23,7 @@
 #include <QtGui/QItemSelectionModel>
 #include <QtGui/QSortFilterProxyModel>
 
+#include "MapThemeManager.h"
 #include "global.h"
 #include "MarbleDebug.h"
 #include "gps/GpsLayer.h"
@@ -86,6 +87,7 @@ class MarbleModelPrivate
         : m_parent( parent ),
           m_dataFacade( 0 ),
           m_pluginManager( new PluginManager( parent ) ),
+          m_mapThemeManager( new MapThemeManager( parent )),
           m_mapTheme( 0 ),
           m_layerManager( 0 ),
           m_downloadManager( new HttpDownloadManager( new FileStoragePolicy(
@@ -109,6 +111,7 @@ class MarbleModelPrivate
     MarbleDataFacade        *m_dataFacade;
 
     PluginManager           *m_pluginManager;
+    MapThemeManager         *m_mapThemeManager;
 
     // View and paint stuff
     GeoSceneDocument        *m_mapTheme;
@@ -161,7 +164,7 @@ MarbleModel::MarbleModel( QObject *parent )
              d->m_downloadManager, SLOT( addJob( QUrl, QString, QString, DownloadUsage )));
     d->m_dataFacade = new MarbleDataFacade( this );
 
-    d->m_tileLoader = new TileLoader( d->m_downloadManager, this );
+    d->m_tileLoader = new TileLoader( d->m_mapThemeManager, d->m_downloadManager, this );
 
     d->m_texmapper = 0;
     
@@ -224,7 +227,7 @@ MarbleModel::MarbleModel( QObject *parent )
     /* Assume we are dealing with the earth */
     d->m_planet = new Planet( "earth" );
     d->m_sunLocator     = new SunLocator( d->m_dateTime, d->m_planet );
-    d->m_layerDecorator = new MergedLayerDecorator( d->m_sunLocator );
+    d->m_layerDecorator = new MergedLayerDecorator( d->m_tileLoader, d->m_sunLocator );
 
     connect(d->m_dateTime,   SIGNAL( timeChanged() ),
             d->m_sunLocator, SLOT( update() ) );
@@ -371,7 +374,6 @@ void MarbleModel::setMapTheme( GeoSceneDocument* mapTheme,
             qDebug("Tile creation completed");
             delete tileCreatorDlg;
         }
-        d->m_tileLoader->setLayer( layer );
     }
     else {
         d->m_tileLoader->flush();
@@ -496,18 +498,20 @@ void MarbleModel::setMapTheme( GeoSceneDocument* mapTheme,
 void MarbleModel::setupTextureMapper( Projection projection )
 {
   // FIXME: replace this with an approach based on the factory method pattern.
-
     delete d->m_texmapper;
 
     switch( projection ) {
         case Spherical:
-            d->m_texmapper = new SphericalScanlineTextureMapper( d->m_tileLoader, this );
+            d->m_texmapper = new SphericalScanlineTextureMapper( textureLayer(), d->m_tileLoader,
+                                                                 this );
             break;
         case Equirectangular:
-            d->m_texmapper = new EquirectScanlineTextureMapper( d->m_tileLoader, this );
+            d->m_texmapper = new EquirectScanlineTextureMapper( textureLayer(), d->m_tileLoader,
+                                                                this );
             break;
         case Mercator:
-            d->m_texmapper = new MercatorScanlineTextureMapper( d->m_tileLoader, this );
+            d->m_texmapper = new MercatorScanlineTextureMapper( textureLayer(), d->m_tileLoader,
+                                                                this );
             break;
     }
 
@@ -838,11 +842,6 @@ void MarbleModel::paintTile( TextureTile* tile, GeoSceneTexture *textureLayer )
 {
 //    mDebug() << "MarbleModel::paintTile: " << "x: " << x << "y:" << y << "level: " << level
 //             << "requestTileUpdate" << requestTileUpdate;
-    
-    if ( d->m_downloadManager != 0 ) {
-        connect( d->m_layerDecorator, SIGNAL( downloadTile( QUrl, QString, QString, DownloadUsage ) ),
-                 d->m_downloadManager, SLOT( addJob( QUrl, QString, QString, DownloadUsage ) ) );
-    }
 
     d->m_layerDecorator->setInfo( tile->id() );
     d->m_layerDecorator->setTile( tile->tile() );
@@ -939,6 +938,23 @@ void MarbleModel::addDownloadPolicies( GeoSceneDocument *mapTheme )
     }
 }
 
+GeoSceneTexture * MarbleModel::textureLayer() const
+{
+    if ( !d->m_mapTheme )
+        return 0;
+    if ( !d->m_mapTheme->map()->hasTextureLayers() )
+        return 0;
+
+    // As long as we don't have an Layer Management Class we just lookup
+    // the name of the layer that has the same name as the theme Id
+    const QString themeId = d->m_mapTheme->head()->theme();
+    GeoSceneLayer * const layer = static_cast<GeoSceneLayer*>( d->m_mapTheme->map()->layer( themeId ));
+    if ( !layer )
+        return 0;
+
+    return static_cast<GeoSceneTexture*>( layer->groundDataset() );
+}
+
 }
 
 #include "MarbleModel.moc"
diff --git a/marble/src/lib/MarbleModel.h b/marble/src/lib/MarbleModel.h
index b4c720d..4888088 100644
--- a/marble/src/lib/MarbleModel.h
+++ b/marble/src/lib/MarbleModel.h
@@ -368,6 +368,7 @@ class MARBLE_EXPORT MarbleModel : public QObject
     Q_PRIVATE_SLOT( d, void notifyModelChanged() )
 
     void addDownloadPolicies( GeoSceneDocument *mapTheme );
+    GeoSceneTexture * textureLayer() const;
 
     MarbleModelPrivate  * const d;
 };
diff --git a/marble/src/lib/MercatorScanlineTextureMapper.cpp b/marble/src/lib/MercatorScanlineTextureMapper.cpp
index deaa1af..27cbf20 100644
--- a/marble/src/lib/MercatorScanlineTextureMapper.cpp
+++ b/marble/src/lib/MercatorScanlineTextureMapper.cpp
@@ -31,9 +31,10 @@
 
 using namespace Marble;
 
-MercatorScanlineTextureMapper::MercatorScanlineTextureMapper( TileLoader *tileLoader,
-                                                              QObject * parent )
-    : AbstractScanlineTextureMapper( tileLoader, parent ),
+MercatorScanlineTextureMapper::MercatorScanlineTextureMapper( GeoSceneTexture *textureLayer,
+                                                              TileLoader *tileLoader,
+                                                              QObject *parent )
+    : AbstractScanlineTextureMapper( textureLayer, tileLoader, parent ),
       m_oldCenterLon( 0.0 ),
       m_oldYPaintedTop( 0 )
 {
diff --git a/marble/src/lib/MercatorScanlineTextureMapper.h b/marble/src/lib/MercatorScanlineTextureMapper.h
index 8661c43..15e26c5 100644
--- a/marble/src/lib/MercatorScanlineTextureMapper.h
+++ b/marble/src/lib/MercatorScanlineTextureMapper.h
@@ -24,8 +24,8 @@ class MercatorScanlineTextureMapper : public AbstractScanlineTextureMapper
     Q_OBJECT
 
  public:
-    explicit MercatorScanlineTextureMapper( TileLoader *tileLoader,
-					    QObject    *parent = 0 );
+    MercatorScanlineTextureMapper( GeoSceneTexture *textureLayer, TileLoader *tileLoader,
+                                   QObject *parent = 0 );
     void mapTexture( ViewParams *viewParams );
 
  private:
diff --git a/marble/src/lib/MergedLayerDecorator.cpp b/marble/src/lib/MergedLayerDecorator.cpp
index 2ae96d8..18f5d23 100644
--- a/marble/src/lib/MergedLayerDecorator.cpp
+++ b/marble/src/lib/MergedLayerDecorator.cpp
@@ -20,6 +20,7 @@
 #include <QtGui/QPainter>
 
 #include "SunLocator.h"
+#include "TileLoader.h"
 #include "global.h"
 #include "MarbleDebug.h"
 #include "GeoSceneDocument.h"
@@ -34,34 +35,17 @@
 
 using namespace Marble;
 
-MergedLayerDecorator::MergedLayerDecorator(SunLocator* sunLocator)
-    : m_tile( 0 ),
+MergedLayerDecorator::MergedLayerDecorator( TileLoader * const tileLoader, SunLocator* sunLocator )
+    : m_tileLoader( tileLoader ),
+      m_tile( 0 ),
       m_id(),
       m_sunLocator( sunLocator ),
-      m_cloudlayer( false ),
       m_showTileId( false ),
       m_cityLightsTheme( 0 ),
-      m_blueMarbleTheme( 0 ),
-      m_cityLightsTextureLayer( 0 ),
-      m_cloudsTextureLayer( 0 )
+      m_cityLightsTextureLayer( 0 )
 {
 }
 
-void MergedLayerDecorator::initClouds()
-{
-    // look for the texture layers inside the themes
-    // As long as we don't have an Layer Management Class we just lookup 
-    // the name of the layer that has the same name as the theme ID
-
-    // the clouds texture layer is a layer in the bluemarble theme
-    m_blueMarbleTheme = MapThemeManager::loadMapTheme( "earth/bluemarble/bluemarble.dgml" );
-    if ( m_blueMarbleTheme ) {
-        QString blueMarbleId = m_blueMarbleTheme->head()->theme();
-        m_cloudsTextureLayer = static_cast<GeoSceneTexture*>(
-            m_blueMarbleTheme->map()->layer( blueMarbleId )->dataset( "clouds_data" ) );
-    }
-}
-
 void MergedLayerDecorator::initCityLights()
 {
     // look for the texture layers inside the themes
@@ -80,7 +64,6 @@ void MergedLayerDecorator::initCityLights()
 MergedLayerDecorator::~MergedLayerDecorator()
 {
     delete m_cityLightsTheme;
-    delete m_blueMarbleTheme;
 }
 
 void MergedLayerDecorator::paint( const QString& themeId, GeoSceneDocument *mapTheme )
@@ -88,17 +71,6 @@ void MergedLayerDecorator::paint( const QString& themeId, GeoSceneDocument *mapT
 //     QTime time;
 //     time.start();
     
-    if ( m_cloudlayer && m_tile->depth() == 32 && m_id.zoomLevel() < 2 ) {
-        bool show;
-        if ( mapTheme && mapTheme->settings()->propertyAvailable( "clouds", show ) ) {
-
-            // Initialize clouds if it hasn't happened already
-            if ( !m_blueMarbleTheme ) {
-                initClouds();
-            }
-            paintClouds();
-        }
-    }
     if ( m_sunLocator->getShow() && mapTheme ) {
 
         // Initialize citylights layer if it hasn't happened already
@@ -114,16 +86,6 @@ void MergedLayerDecorator::paint( const QString& themeId, GeoSceneDocument *mapT
     }
 }
 
-void MergedLayerDecorator::setShowClouds( bool visible )
-{
-    m_cloudlayer = visible;
-}
-
-bool MergedLayerDecorator::showClouds() const
-{
-    return m_cloudlayer;
-}
-
 void MergedLayerDecorator::setShowTileId( bool visible )
 {
     m_showTileId = visible;
@@ -134,59 +96,12 @@ bool MergedLayerDecorator::showTileId() const
     return m_showTileId;
 }
 
-QImage MergedLayerDecorator::loadDataset( GeoSceneTexture *textureLayer )
-{
-    // TODO use a TileLoader rather than directly accessing TextureTile?
-    TextureTile tile( m_id );
-    
-    connect( &tile, SIGNAL( downloadTile( const QUrl&, const QString&, const QString&, DownloadUsage ) ),
-             this, SIGNAL( downloadTile( const QUrl&, const QString&, const QString&, DownloadUsage ) ) );
-
-    tile.loadDataset( textureLayer );
-    return *( tile.tile() );
-}
-
-void MergedLayerDecorator::paintClouds()
+TextureTile * MergedLayerDecorator::loadDataset( GeoSceneTexture *textureLayer )
 {
-    QImage  cloudtile = loadDataset( m_cloudsTextureLayer );
-    if ( cloudtile.isNull() )
-        return;
-
-    // Do not attempt to paint clouds if cloud tile and map tile have
-    // got different sizes.
-    // FIXME: make cloud a separate texture layer
-    if ( cloudtile.height() != m_tile->height() || cloudtile.width() != m_tile->width() )
-        return;
-
-//     mDebug() << "cloud tile:" << cloudtile.height() << cloudtile.width() << cloudtile.depth()
-//              << "  map tile:" << m_tile->height() << m_tile->width() << m_tile->depth();
-
-    const int  ctileHeight = cloudtile.height();
-    const int  ctileWidth  = cloudtile.width();
-
-    for ( int cur_y = 0; cur_y < ctileHeight; ++cur_y ) {
-        uchar  *cscanline = (uchar*)cloudtile.scanLine( cur_y );
-        QRgb   *scanline  = (QRgb*)m_tile->scanLine( cur_y );
-        for ( int cur_x = 0; cur_x < ctileWidth; ++cur_x ) {
-            qreal  c;
-            if ( cloudtile.depth() == 32)
-                c = qRed( *((QRgb*)cscanline) ) / 255.0;
-            else
-                c = *cscanline / 255.0;
-            QRgb    pix = *scanline;
-            int  r = qRed( pix );
-            int  g = qGreen( pix );
-            int  b = qBlue( pix );
-            *scanline = qRgb( (int)( r + (255-r)*c ),
-                              (int)( g + (255-g)*c ),
-                              (int)( b + (255-b)*c ) );
-            if ( cloudtile.depth() == 32)
-                cscanline += sizeof( QRgb );
-            else
-                cscanline++;
-            scanline++;
-        }
-    }
+    const TileId decorationTileId( textureLayer->sourceDir(), m_id.zoomLevel(), m_id.x(), m_id.y());
+    TextureTile * const tile = m_tileLoader->loadTile( decorationTileId, true );
+    tile->setUsed( true );
+    return tile;
 }
 
 void MergedLayerDecorator::paintSunShading()
@@ -213,16 +128,20 @@ void MergedLayerDecorator::paintSunShading()
 
     //Don't use city lights on non-earth planets!
     if ( m_sunLocator->getCitylights() && m_sunLocator->planet()->id() == "earth" ) {
-        QImage nighttile = loadDataset( m_cityLightsTextureLayer );
-        if ( nighttile.isNull() )
+
+        TextureTile * tile = loadDataset( m_cityLightsTextureLayer );
+        if ( tile->state() == TextureTile::TileEmpty )
             return;
+
+        QImage * nighttile = tile->tile();
+
         for ( int cur_y = 0; cur_y < tileHeight; ++cur_y ) {
             qreal lat = lat_scale * ( m_id.y() * tileHeight + cur_y ) - 0.5*M_PI;
             qreal a = sin( ( lat+DEG2RAD * m_sunLocator->getLat() )/2.0 );
             qreal c = cos(lat)*cos( -DEG2RAD * m_sunLocator->getLat() );
 
             QRgb* scanline  = (QRgb*)m_tile->scanLine( cur_y );
-            QRgb* nscanline = (QRgb*)nighttile.scanLine( cur_y );
+            QRgb* nscanline = (QRgb*)nighttile->scanLine( cur_y );
 
             qreal shade = 0;
             qreal lastShade = -10.0;
diff --git a/marble/src/lib/MergedLayerDecorator.h b/marble/src/lib/MergedLayerDecorator.h
index 701407e..ee7564d 100644
--- a/marble/src/lib/MergedLayerDecorator.h
+++ b/marble/src/lib/MergedLayerDecorator.h
@@ -30,13 +30,15 @@ namespace Marble
 class GeoSceneDocument;
 class GeoSceneTexture;
 class SunLocator;
+class TextureTile;
+class TileLoader;
 
 class MergedLayerDecorator : public QObject
 {
     Q_OBJECT
 	
  public:
-    explicit MergedLayerDecorator(SunLocator* sunLocator);
+    MergedLayerDecorator( TileLoader * const tileLoader, SunLocator* sunLocator );
     virtual ~MergedLayerDecorator();
 
     // The Parameter themeId is only used for displaying the TileId,
@@ -44,9 +46,6 @@ class MergedLayerDecorator : public QObject
     void paint( const QString& themeId, GeoSceneDocument *mapTheme = 0 );
     void paintTileId(const QString& themeId);
     
-    void setShowClouds(bool show);
-    bool showClouds() const;
-
     void setShowTileId(bool show);
     bool showTileId() const;
 	
@@ -54,15 +53,12 @@ class MergedLayerDecorator : public QObject
     void setInfo( TileId const &id );
 	
  Q_SIGNALS:
-    void downloadTile(const QUrl& sourceUrl, const QString& destinationFileName,
-                      const QString& id, DownloadUsage );
     void repaintMap();
 	
  private:
-    QImage loadDataset( GeoSceneTexture *textureLayer );
+    TextureTile * loadDataset( GeoSceneTexture *textureLayer );
     int maxDivisor( int maximum, int fullLength );
 
-    void initClouds();
     void initCityLights();
 
     void paintSunShading();
@@ -70,15 +66,13 @@ class MergedLayerDecorator : public QObject
 	
  protected:
     Q_DISABLE_COPY( MergedLayerDecorator )
+    TileLoader * const m_tileLoader;
     QImage* m_tile;
     TileId m_id;
     SunLocator* m_sunLocator;
-    bool m_cloudlayer;
     bool m_showTileId;
     GeoSceneDocument *m_cityLightsTheme;
-    GeoSceneDocument *m_blueMarbleTheme;
     GeoSceneTexture *m_cityLightsTextureLayer;
-    GeoSceneTexture *m_cloudsTextureLayer;
 };
 
 }
diff --git a/marble/src/lib/SimpleTextureTile.cpp b/marble/src/lib/SimpleTextureTile.cpp
new file mode 100644
index 0000000..f60f190
--- /dev/null
+++ b/marble/src/lib/SimpleTextureTile.cpp
@@ -0,0 +1,57 @@
+// Copyright 2010 Jens-Michael Hoffmann <jmho at c-xx.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+#include "SimpleTextureTile.h"
+
+#include <limits>
+
+#include <QtCore/QString>
+#include <QtGui/QImage>
+
+namespace Marble
+{
+
+SimpleTextureTile::SimpleTextureTile( TileId const & tileId )
+    : m_id( tileId ),
+      m_state( StateEmpty ),
+      m_mergeRule( MergeCopy ),
+      m_expireSecs( std::numeric_limits<int>::max() ),
+      m_image( 0 )
+{
+}
+
+SimpleTextureTile::SimpleTextureTile( TileId const & tileId, QString const & fileName )
+    : m_id( tileId ),
+      m_state( StateExpired ),
+      m_mergeRule( MergeCopy ),
+      m_expireSecs( std::numeric_limits<int>::max() ),
+      m_image( new QImage( fileName ))
+{
+}
+
+SimpleTextureTile::~SimpleTextureTile()
+{
+    delete m_image;
+}
+
+void SimpleTextureTile::setImage( QByteArray const & data )
+{
+    if ( !m_image )
+        m_image = new QImage( QImage::fromData( data ));
+    else
+        m_image->loadFromData( data );
+}
+
+}
diff --git a/marble/src/lib/SimpleTextureTile.h b/marble/src/lib/SimpleTextureTile.h
new file mode 100644
index 0000000..5f824be
--- /dev/null
+++ b/marble/src/lib/SimpleTextureTile.h
@@ -0,0 +1,157 @@
+// Copyright 2010 Jens-Michael Hoffmann <jmho at c-xx.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef MARBLE_SIMPLE_TEXTURE_TILE_H
+#define MARBLE_SIMPLE_TEXTURE_TILE_H
+
+#include <QtCore/QDateTime>
+
+#include "TileId.h"
+
+class QByteArray;
+class QImage;
+
+namespace Marble
+{
+class SimpleTileLoader;
+class TileLoader;
+
+class SimpleTextureTile
+{
+    friend class SimpleTileLoader;
+    friend class TileLoader;
+
+ public:
+    enum State {
+        StateEmpty,
+        StateScaled,
+        StateExpired,
+        StateUptodate
+    };
+
+    enum MergeRule {
+        MergeCopy,
+        MergeMultiply
+    };
+
+    explicit SimpleTextureTile( TileId const & );
+    SimpleTextureTile( TileId const & tileId, QString const & fileName );
+    ~SimpleTextureTile();
+
+    TileId const & id() const;
+    TileId const & composedTileId() const;
+    QDateTime const & lastModified() const;
+    bool expired() const;
+    QImage const * image() const;
+    QImage * image();
+    State state() const;
+    MergeRule mergeRule() const;
+
+ private:
+    Q_DISABLE_COPY( SimpleTextureTile )
+
+    void setState( State const );
+    void setImage( QByteArray const & data );
+    void setImage( QImage * const );
+    void setMergeRule( MergeRule const );
+    void setComposedTileId( TileId const & );
+    void setLastModified( QDateTime const & );
+    void setExpireSecs( int const );
+
+    TileId const m_id;
+    TileId m_composedTileId;
+    State m_state;
+    MergeRule m_mergeRule;
+    QDateTime m_lastModified;
+    int m_expireSecs;
+    QImage * m_image;
+};
+
+
+// inline definitions
+
+inline TileId const & SimpleTextureTile::id() const
+{
+    return m_id;
+}
+
+inline TileId const & SimpleTextureTile::composedTileId() const
+{
+    return m_composedTileId;
+}
+
+inline QDateTime const & SimpleTextureTile::lastModified() const
+{
+    return m_lastModified;
+}
+
+inline bool SimpleTextureTile::expired() const
+{
+    return m_lastModified.secsTo( QDateTime::currentDateTime() ) >= m_expireSecs;
+}
+
+inline QImage const * SimpleTextureTile::image() const
+{
+    return m_image;
+}
+
+inline QImage * SimpleTextureTile::image()
+{
+    return m_image;
+}
+
+inline SimpleTextureTile::State SimpleTextureTile::state() const
+{
+    return m_state;
+}
+
+inline SimpleTextureTile::MergeRule SimpleTextureTile::mergeRule() const
+{
+    return m_mergeRule;
+}
+
+inline void SimpleTextureTile::setState( State const state )
+{
+    m_state = state;
+}
+
+inline void SimpleTextureTile::setImage( QImage * const image )
+{
+    m_image = image;
+}
+
+inline void SimpleTextureTile::setMergeRule( MergeRule const mergeRule )
+{
+    m_mergeRule = mergeRule;
+}
+
+inline void SimpleTextureTile::setComposedTileId( TileId const & id )
+{
+    m_composedTileId = id;
+}
+
+inline void SimpleTextureTile::setLastModified( QDateTime const & lastModified )
+{
+    m_lastModified = lastModified;
+}
+
+inline void SimpleTextureTile::setExpireSecs( int const expireSecs )
+{
+    m_expireSecs = expireSecs;
+}
+
+}
+
+#endif
diff --git a/marble/src/lib/SimpleTileLoader.cpp b/marble/src/lib/SimpleTileLoader.cpp
new file mode 100644
index 0000000..16a4c3e
--- /dev/null
+++ b/marble/src/lib/SimpleTileLoader.cpp
@@ -0,0 +1,165 @@
+// Copyright 2010 Jens-Michael Hoffmann <jmho at c-xx.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+#include "SimpleTileLoader.h"
+
+#include <QtCore/QDateTime>
+#include <QtCore/QFileInfo>
+#include <QtGui/QImage>
+
+#include "GeoSceneTexture.h"
+#include "HttpDownloadManager.h"
+#include "MarbleDebug.h"
+#include "MarbleDirs.h"
+#include "SimpleTextureTile.h"
+#include "TileLoaderHelper.h"
+
+namespace Marble
+{
+
+SimpleTileLoader::SimpleTileLoader( MapThemeManager const * const mapThemeManager,
+                                    HttpDownloadManager * const downloadManager )
+{
+    connect( this, SIGNAL( downloadTile( QUrl, QString, QString, DownloadUsage )),
+             downloadManager, SLOT( addJob( QUrl, QString, QString, DownloadUsage )));
+    connect( downloadManager, SIGNAL( downloadComplete( QByteArray, QString )),
+             SLOT( updateTile( QByteArray, QString )));
+}
+
+// If the tile is locally available:
+//     - if not expired: create SimpleTextureTile, set state to "uptodate", return it => done
+//     - if expired: create SimpleTextureTile, state is set to Expired by default, trigger dl,
+
+SimpleTextureTile * SimpleTileLoader::loadTile( TileId const & composedTileId,
+                                                TileId const & baseTileId )
+{
+    QString const fileName = tileFileName( baseTileId );
+    QFileInfo const fileInfo( fileName );
+    if ( fileInfo.exists() ) {
+        // file is there, so create and return a tile object in any case,
+        // but check if an update should be triggered
+        GeoSceneTexture const * const textureLayer = findTextureLayer( baseTileId );
+        SimpleTextureTile * const tile = new SimpleTextureTile( baseTileId, fileName );
+        tile->setComposedTileId( composedTileId );
+        tile->setLastModified( fileInfo.lastModified() );
+        tile->setExpireSecs( textureLayer->expire() );
+
+        if ( !tile->expired() ) {
+            mDebug() << "SimpleTileLoader::loadTile" << baseTileId.toString() << "StateUptodate";
+            tile->setState( SimpleTextureTile::StateUptodate );
+        } else {
+            mDebug() << "SimpleTileLoader::loadTile" << baseTileId.toString() << "StateExpired";
+            m_waitingForUpdate.insert( baseTileId, tile );
+            triggerDownload( baseTileId );
+        }
+        return tile;
+    }
+
+    // tile was not locally available => trigger download and look for tiles in other levels
+    // for scaling
+    SimpleTextureTile * const tile = new SimpleTextureTile( baseTileId );
+    tile->setComposedTileId( composedTileId );
+    m_waitingForUpdate.insert( baseTileId, tile );
+    triggerDownload( baseTileId );
+    QImage * const replacementTile = scaledLowerLevelTile( baseTileId );
+    if ( replacementTile ) {
+        mDebug() << "SimpleTileLoader::loadTile" << baseTileId.toString() << "StateScaled";
+        tile->setImage( replacementTile );
+        tile->setState( SimpleTextureTile::StateScaled );
+    } else {
+        mDebug() << "SimpleTileLoader::loadTile" << baseTileId.toString() << "No tiles found";
+    }
+    return tile;
+}
+
+void SimpleTileLoader::updateTile( QByteArray const & data, QString const & tileId )
+{
+    TileId const id = TileId::fromString( tileId );
+    SimpleTextureTile * const tile = m_waitingForUpdate.value( id, 0 );
+    Q_ASSERT( tile );
+    m_waitingForUpdate.remove( id );
+    tile->setImage( data );
+    tile->setState( SimpleTextureTile::StateUptodate );
+    tile->setLastModified( QDateTime::currentDateTime() );
+    emit tileCompleted( tile->composedTileId(), id );
+}
+
+inline GeoSceneTexture const * SimpleTileLoader::findTextureLayer( TileId const & id ) const
+{
+    GeoSceneTexture const * const textureLayer = m_textureLayers.value( id.mapThemeIdHash(), 0 );
+    Q_ASSERT( textureLayer );
+    return textureLayer;
+}
+
+inline GeoSceneTexture * SimpleTileLoader::findTextureLayer( TileId const & id )
+{
+    GeoSceneTexture * const textureLayer = m_textureLayers.value( id.mapThemeIdHash(), 0 );
+    Q_ASSERT( textureLayer );
+    return textureLayer;
+}
+
+QString SimpleTileLoader::tileFileName( TileId const & tileId ) const
+{
+    GeoSceneTexture const * const textureLayer = findTextureLayer( tileId );
+    return MarbleDirs::path( TileLoaderHelper::relativeTileFileName
+                             ( textureLayer, tileId.zoomLevel(), tileId.x(), tileId.y() ));
+}
+
+void SimpleTileLoader::triggerDownload( TileId const & id )
+{
+    GeoSceneTexture * const textureLayer = findTextureLayer( id );
+    QUrl const sourceUrl = TileLoaderHelper::downloadUrl( textureLayer, id.zoomLevel(), id.x(),
+                                                          id.y() );
+    QString const destFileName = TileLoaderHelper::relativeTileFileName( textureLayer, id.zoomLevel(),
+                                                                         id.x(), id.y() );
+    emit downloadTile( sourceUrl, destFileName, id.toString(), DownloadBrowse );
+}
+
+    // TODO: get lastModified time stamp into the SimpleTextureTile
+QImage * SimpleTileLoader::scaledLowerLevelTile( TileId const & id )
+{
+    mDebug() << "SimpleTileLoader::scaledLowerLevelTile" << id.toString();
+    QImage * result = 0;
+    int level = id.zoomLevel() - 1;
+    while ( !result && level >= 0 ) {
+        int const deltaLevel = id.zoomLevel() - level;
+        TileId const replacementTileId( id.mapThemeIdHash(), level,
+                                        id.x() >> deltaLevel, id.y() >> deltaLevel );
+        mDebug() << "SimpleTileLoader::scaledLowerLevelTile" << "trying" << replacementTileId.toString();
+        QString const fileName = tileFileName( replacementTileId );
+        QFileInfo const fileInfo( fileName );
+        if ( fileInfo.exists() ) {
+            QImage const toScale( fileName );
+            // which rect to scale?
+            QSize const size = toScale.size();
+            int const restTileX = id.x() % ( 1 << deltaLevel );
+            int const restTileY = id.y() % ( 1 << deltaLevel );
+            int const partWidth = toScale.width() >> deltaLevel;
+            int const partHeight = toScale.height() >> deltaLevel;
+            int const startX = restTileX * partWidth;
+            int const startY = restTileY * partHeight;
+            mDebug() << "QImage::copy:" << startX << startY << partWidth << partHeight;
+            QImage const part = toScale.copy( startX, startY, partWidth, partHeight );
+            mDebug() << "QImage::scaled:" << toScale.size();
+            result = new QImage ( part.scaled( toScale.size() ));
+        }
+        --level;
+    }
+    return result;
+}
+
+}
+
+#include "SimpleTileLoader.moc"
diff --git a/marble/src/lib/SimpleTileLoader.h b/marble/src/lib/SimpleTileLoader.h
new file mode 100644
index 0000000..9ab3ad1
--- /dev/null
+++ b/marble/src/lib/SimpleTileLoader.h
@@ -0,0 +1,80 @@
+// Copyright 2010 Jens-Michael Hoffmann <jmho at c-xx.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef MARBLE_SIMPLE_TILE_LOADER_H
+#define MARBLE_SIMPLE_TILE_LOADER_H
+
+#include <QtCore/QHash>
+#include <QtCore/QObject>
+#include <QtCore/QString>
+
+#include "TileId.h"
+#include "global.h"
+
+class QByteArray;
+class QImage;
+class QUrl;
+
+namespace Marble
+{
+class HttpDownloadManager; // remove?
+class GeoSceneTexture;
+class MapThemeManager;
+class SimpleTextureTile;
+
+class SimpleTileLoader: public QObject
+{
+    Q_OBJECT
+
+ public:
+    SimpleTileLoader( MapThemeManager const * const, HttpDownloadManager * const );
+
+    SimpleTextureTile * loadTile( TileId const & composedTileId, TileId const & baseTileId );
+    void setTextureLayers( QHash<uint, GeoSceneTexture*> const & );
+
+ public Q_SLOTS:
+    void updateTile( QByteArray const & imageData, QString const & tileId );
+
+ Q_SIGNALS:
+    void downloadTile( QUrl const & sourceUrl, QString const & destinationFileName,
+                       QString const & id, DownloadUsage );
+
+    // when this signal is emitted, the SimpleTileLoader gives up ownership of
+    // the corrsponding tile. Might be better to explicitely transfer...
+    void tileCompleted( TileId const & composedTileId, TileId const & baseTileId );
+
+ private:
+    GeoSceneTexture const * findTextureLayer( TileId const & ) const;
+    GeoSceneTexture * findTextureLayer( TileId const & );
+    QString tileFileName( TileId const & ) const;
+    void triggerDownload( TileId const & );
+    QImage * scaledLowerLevelTile( TileId const & );
+
+    // TODO: comment about uint hash key
+    QHash<uint, GeoSceneTexture*> m_textureLayers;
+
+    // contains tiles, for which a download has been triggered
+    // because the tile was not there at all or is expired.
+    QHash<TileId, SimpleTextureTile*> m_waitingForUpdate;
+};
+
+inline void SimpleTileLoader::setTextureLayers( QHash<uint, GeoSceneTexture*> const & layers )
+{
+    m_textureLayers = layers;
+}
+
+}
+
+#endif
diff --git a/marble/src/lib/SphericalScanlineTextureMapper.cpp b/marble/src/lib/SphericalScanlineTextureMapper.cpp
index 9ce169f..70390ab 100644
--- a/marble/src/lib/SphericalScanlineTextureMapper.cpp
+++ b/marble/src/lib/SphericalScanlineTextureMapper.cpp
@@ -28,9 +28,10 @@
 
 using namespace Marble;
 
-SphericalScanlineTextureMapper::SphericalScanlineTextureMapper( TileLoader *tileLoader,
-                                                                QObject * parent )
-    : AbstractScanlineTextureMapper( tileLoader, parent )
+SphericalScanlineTextureMapper::SphericalScanlineTextureMapper( GeoSceneTexture *textureLayer,
+                                                                TileLoader *tileLoader,
+                                                                QObject *parent )
+    : AbstractScanlineTextureMapper( textureLayer, tileLoader, parent )
 {
     m_interlaced = false;
 }
diff --git a/marble/src/lib/SphericalScanlineTextureMapper.h b/marble/src/lib/SphericalScanlineTextureMapper.h
index 6bfe7a5..5c00b41 100644
--- a/marble/src/lib/SphericalScanlineTextureMapper.h
+++ b/marble/src/lib/SphericalScanlineTextureMapper.h
@@ -36,7 +36,8 @@ class TileLoader;
 class SphericalScanlineTextureMapper : public AbstractScanlineTextureMapper
 {
  public:
-    explicit SphericalScanlineTextureMapper( TileLoader *tileLoader, QObject * parent =0 );
+    SphericalScanlineTextureMapper( GeoSceneTexture *textureLayer, TileLoader *tileLoader,
+                                    QObject *parent = 0 );
 
     void mapTexture( ViewParams *viewParams );
 
diff --git a/marble/src/lib/TextureTile.cpp b/marble/src/lib/TextureTile.cpp
index 572f58c..515828e 100644
--- a/marble/src/lib/TextureTile.cpp
+++ b/marble/src/lib/TextureTile.cpp
@@ -18,6 +18,7 @@
 
 #include <cmath>
 
+#include "SimpleTextureTile.h"
 #include "global.h"
 #include "MarbleDebug.h"
 #include "GeoSceneTexture.h"
@@ -63,9 +64,10 @@ TextureTilePrivate::TextureTilePrivate( const TileId& id ) :
       AbstractTilePrivate( id ), 
       jumpTable8(0),
       jumpTable32(0),
-      m_rawtile(),
+      m_resultTile(),
       m_depth(0),
-      m_isGrayscale(false)
+      m_isGrayscale( false ),
+      m_forMergedLayerDecorator( false )
 {
 }
 
@@ -73,6 +75,14 @@ TextureTilePrivate::~TextureTilePrivate()
 {
       delete [] jumpTable32;
       delete [] jumpTable8;
+
+      QVector<SimpleTextureTile*>::const_iterator pos = m_baseTiles.constBegin();
+      QVector<SimpleTextureTile*>::const_iterator const end = m_baseTiles.constEnd();
+      for (; pos != end; ++pos )
+          // only delete tiles with stateUptodate as the other ones are managed
+          // by the SimpleTileLoader until they are fully up to date.
+          if ( (*pos)->state() == SimpleTextureTile::StateUptodate )
+              delete *pos;
 }
 
 uint TextureTilePrivate::pixel( int x, int y ) const
@@ -81,15 +91,15 @@ uint TextureTilePrivate::pixel( int x, int y ) const
         if ( m_isGrayscale )
             return (jumpTable8)[y][x];
         else
-            return m_rawtile.color( (jumpTable8)[y][x] );
+            return m_resultTile.color( (jumpTable8)[y][x] );
     }
     if ( m_depth == 32 )
         return (jumpTable32)[y][x];
     
     if ( m_depth == 1 && !m_isGrayscale )
-        return m_rawtile.color((jumpTable8)[y][x/8] >> 7);
+        return m_resultTile.color((jumpTable8)[y][x/8] >> 7);
 
-    return m_rawtile.pixel( x, y );
+    return m_resultTile.pixel( x, y );
 }
 
 uint TextureTilePrivate::pixelF( qreal x, qreal y, const QRgb& topLeftValue ) const
@@ -102,7 +112,7 @@ uint TextureTilePrivate::pixelF( qreal x, qreal y, const QRgb& topLeftValue ) co
     qreal fY = y - iY;
 
     // Interpolation in y-direction
-    if ( ( iY + 1 ) < m_rawtile.height() ) {
+    if ( ( iY + 1 ) < m_resultTile.height() ) {
 
         QRgb bottomLeftValue  =  pixel( iX, iY + 1 );
 // #define CHEAPHIGH
@@ -122,7 +132,7 @@ uint TextureTilePrivate::pixelF( qreal x, qreal y, const QRgb& topLeftValue ) co
         qreal ml_blue  = ( 1.0 - fY ) * qBlue ( topLeftValue  ) + fY * qBlue ( bottomLeftValue  );
 #endif
         // Interpolation in x-direction
-        if ( iX + 1 < m_rawtile.width() ) {
+        if ( iX + 1 < m_resultTile.width() ) {
 
             qreal fX = x - iX;
 
@@ -174,7 +184,7 @@ uint TextureTilePrivate::pixelF( qreal x, qreal y, const QRgb& topLeftValue ) co
     }
     else {
         // Interpolation in x-direction
-        if ( iX + 1 < m_rawtile.width() ) {
+        if ( iX + 1 < m_resultTile.width() ) {
 
             qreal fX = x - iX;
 
@@ -207,49 +217,34 @@ uint TextureTilePrivate::pixelF( qreal x, qreal y, const QRgb& topLeftValue ) co
     return topLeftValue;
 }
 
-void TextureTilePrivate::scaleTileFrom( GeoSceneTexture *textureLayer, QImage &tile,
-                    qreal sourceX, qreal sourceY, int sourceLevel,
-                    int targetX, int targetY, int targetLevel )
+inline void TextureTilePrivate::mergeCopyToResult( SimpleTextureTile const * const other )
 {
-    const int levelZeroColumns = textureLayer->levelZeroColumns();
-    const int levelZeroRows = textureLayer->levelZeroRows();
-    const int rowsRequestedLevel = TileLoaderHelper::levelToRow( levelZeroRows, targetLevel );
-    const int columnsRequestedLevel = TileLoaderHelper::levelToColumn( levelZeroColumns,
-                                                                       targetLevel );
-    const int rowsCurrentLevel = TileLoaderHelper::levelToRow( levelZeroRows, sourceLevel );
-    const int columnsCurrentLevel = TileLoaderHelper::levelToColumn( levelZeroColumns,
-                                                                     sourceLevel );
-
-    // mDebug() << "About to start cropping an existing image.";
-
-    QSize tilesize = tile.size();
-    qreal normalizedX2 = (qreal)(targetX + 1) / (qreal)( rowsRequestedLevel );
-    qreal normalizedY2 = (qreal)(targetY + 1) / (qreal)( columnsRequestedLevel );
-    qreal currentX2    = normalizedX2 * (qreal)( rowsCurrentLevel );
-    qreal currentY2    = normalizedY2 * (qreal)( columnsCurrentLevel );
-
-    // Determine the rectangular section of the previous tile data 
-    // which we intend to copy from:
-    int left   = (int)( ( sourceX    - (int)( sourceX ) ) * tile.width() );
-    int top    = (int)( ( sourceY    - (int)( sourceY ) ) * tile.height() );
-    int right  = (int)( ( currentX2  - (int)( sourceX ) ) * tile.width() ) - 1;
-    int bottom = (int)( ( currentY2  - (int)( sourceY ) ) * tile.height() ) - 1;
-
-    // Important: Scaling a null image during the next step would be fatal
-    // So we make sure the width and height is at least 1.
-    int rectWidth  = ( right - left > 1 ) ? right  - left : 1;
-    int rectHeight = ( bottom - top > 1 ) ? bottom - top  : 1;
-
-    // This should not create any memory leaks as
-    // 'copy' and 'scaled' return a value (on the
-    // stack) which gets deep copied always into the
-    // same place for m_rawtile on the heap:
-    tile = tile.copy( left, top, rectWidth, rectHeight );
-    tile = tile.scaled( tilesize ); // TODO: use correct size
-    m_state = AbstractTile::TilePartial;
-    // mDebug() << "Finished scaling up the Temporary Tile.";
+    m_resultTile = other->image()->copy();
 }
 
+void TextureTilePrivate::mergeMultiplyToResult( SimpleTextureTile const * const other )
+{
+    // for this operation we assume that the tiles have the same size
+    QImage const * const otherImage = other->image();
+    Q_ASSERT( m_resultTile.size() == otherImage->size() );
+    if ( m_resultTile.size() != otherImage->size() )
+        return;
+
+    int const width = m_resultTile.width();
+    int const height = m_resultTile.height();
+    for ( int y = 0; y < height; ++y ) {
+        for ( int x = 0; x < width; ++x ) {
+            qreal const c = qRed( otherImage->pixel( x, y )) / 255.0;
+            QRgb const oldPixel = m_resultTile.pixel( x, y );
+            int const oldRed = qRed( oldPixel );
+            int const oldGreen = qGreen( oldPixel );
+            int const oldBlue = qBlue( oldPixel );
+            m_resultTile.setPixel( x, y, qRgb(( int )( oldRed + ( 255 - oldRed ) * c ),
+                                              ( int )( oldGreen + ( 255 - oldGreen ) * c ),
+                                              ( int )( oldBlue + ( 255 - oldBlue ) * c )));
+        }
+    }
+}
 
 TextureTile::TextureTile( TileId const& id, QObject * parent )
     : AbstractTile( *new TextureTilePrivate( id ), parent ), d(0)
@@ -271,173 +266,73 @@ TextureTile::~TextureTile()
 {
 }
 
-void TextureTile::setImage( const QByteArray & data )
+bool TextureTile::expired() const
 {
-    d->m_rawtile = QImage::fromData( data );
-    d->m_depth = d->m_rawtile.depth();
-    d->m_isGrayscale = d->m_rawtile.isGrayscale();
-    d->m_created = QDateTime::currentDateTime();
-    initJumpTables();
-    d->m_state = TileComplete;
+    bool result = false;
+    QVector<SimpleTextureTile*>::const_iterator pos = d->m_baseTiles.constBegin();
+    QVector<SimpleTextureTile*>::const_iterator const end = d->m_baseTiles.constEnd();
+    for (; pos != end; ++pos )
+        result |= (*pos)->expired();
+    return result;
 }
 
-void TextureTile::loadDataset( GeoSceneTexture *textureLayer,
-                               QCache<TileId, TextureTile> *tileCache )
+bool TextureTile::forMergedLayerDecorator() const
 {
-    // mDebug() << "TextureTile::loadDataset" << level << x << y;
-    QImage temptile;
-
-    d->m_used = true; // Needed to avoid frequent deletion of tiles
-
-    QString  absfilename;
-
-    // If the tile level offers the requested tile then load it.
-    // Otherwise cycle from the requested tilelevel down to one where
-    // the requested area is covered.  Then scale the area to create a
-    // replacement for the tile that has been requested.
-
-    const int levelZeroColumns = textureLayer->levelZeroColumns();
-    const int levelZeroRows = textureLayer->levelZeroRows();
-    const int rowsRequestedLevel = TileLoaderHelper::levelToRow( levelZeroRows,
-                                                                 d->m_id.zoomLevel() );
-    const int columnsRequestedLevel = TileLoaderHelper::levelToColumn( levelZeroColumns,
-                                                                       d->m_id.zoomLevel() );
-    bool tileFound = false;
-    for ( int currentLevel = d->m_id.zoomLevel(); !tileFound && currentLevel > -1; --currentLevel ) {
-
-        const int rowsCurrentLevel = 
-            TileLoaderHelper::levelToRow( levelZeroRows, currentLevel );
-        const int columnsCurrentLevel =
-            TileLoaderHelper::levelToColumn( levelZeroColumns, currentLevel );
-
-        qreal normalizedX = (qreal)( d->m_id.x() ) / (qreal)( rowsRequestedLevel );
-        qreal normalizedY = (qreal)( d->m_id.y() ) / (qreal)( columnsRequestedLevel );
-        qreal currentX    = normalizedX * (qreal)( rowsCurrentLevel );
-        qreal currentY    = normalizedY * (qreal)( columnsCurrentLevel );
-
-        const QDateTime now = QDateTime::currentDateTime();
-        QDateTime lastModified;
-
-        bool download = false;
-        bool currentTileAvailable = false;
-
-        // Check whether the current tile id is available in the CACHE:
-        const TileId currentTileId( currentLevel, (int)(currentX), (int)(currentY) );
-        if ( tileCache ) {
-            TextureTile *currentTile = tileCache->take( currentTileId );
-            if ( currentTile ) {
-                // the tile was in the cache, but is it up to date?
-                lastModified = currentTile->created();
-                if ( lastModified.secsTo( now ) < textureLayer->expire()) {
-                    temptile = currentTile->rawtile();
-                    currentTileAvailable = true;
-                }
-                delete currentTile;
-            }
-        }
-        // If the current tile id is not in the cache or if it was 
-        // in the cache but has expired load from DISK:
-
-        if ( temptile.isNull() ) {
-            QString relfilename =
-                TileLoaderHelper::relativeTileFileName( textureLayer, currentLevel,
-                                                        (int)(currentX), (int)(currentY) );
-            absfilename = MarbleDirs::path( relfilename );
-            const QFileInfo fileInfo( absfilename );
-            lastModified = fileInfo.lastModified();
-
-            // - if the file does not exist, we want to download it and search an
-            //   existing tile of a lower zoom level for immediate display
-            // - if the file exists and is expired according to the value of the
-            //   expire element we want to download it again and display the old
-            //   tile until the new one is there. Once the updated tile is
-            //   available, it should get displayed.
- 
-            if ( !fileInfo.exists() ) {
-//                mDebug() << "File does not exist:" << fileInfo.filePath();
-                download = true;
-            }
-            else if ( lastModified.secsTo( now ) > textureLayer->expire() ) {
-//                mDebug() << "File does exist, but is expired:" << fileInfo.filePath()
-//                        << "age (seconds):" << lastModified.secsTo( now )
-//                        << "allowed age:" << textureLayer->expire();
-                download = true;
-            }
-
-            if ( fileInfo.exists() ) {
-
-                temptile.load( absfilename );
-                // mDebug() << "TextureTile::loadDataset "
-                //          << "depth:" << temptile.depth()
-                //          << "format:" << temptile.format()
-                //          << "bytesPerLine:" << temptile.bytesPerLine()
-                //          << "numBytes:" << temptile.numBytes() ;
-                download |= temptile.isNull();
-                currentTileAvailable = true;
-            }
-        }
+    return d->m_forMergedLayerDecorator;
+}
 
-        if ( currentTileAvailable ) {
-            if ( !temptile.isNull() ) {
-
-                // Don't scale if the current tile isn't a fallback
-                if ( d->m_id.zoomLevel() != currentLevel ) { 
-                    d->scaleTileFrom( textureLayer, temptile, currentX, currentY, currentLevel,
-                                      d->m_id.x(), d->m_id.y(), d->m_id.zoomLevel() );
-                }
-                else {
-                    d->m_state = TileComplete;
-                }
-
-                d->m_rawtile = temptile;
-                d->m_depth = d->m_rawtile.depth();
-                d->m_created = lastModified;
-                tileFound = true;
-                initJumpTables();
-            }
-        }
+void TextureTile::setForMergedLayerDecorator()
+{
+    d->m_forMergedLayerDecorator = true;
+}
 
-        if ( download ) {
-            QUrl sourceUrl =
-                TileLoaderHelper::downloadUrl( textureLayer, currentLevel,
-                                               currentX, currentY );
-            QString destFileName =
-                TileLoaderHelper::relativeTileFileName( textureLayer, currentLevel,
-                                                        currentX, currentY );
-//            mDebug() << "emit downloadTile(" << sourceUrl << destFileName << ");";
-            emit downloadTile( sourceUrl, destFileName, currentTileId.toString(), DownloadBrowse );
-        }
-    }
+void TextureTile::addBaseTile( SimpleTextureTile * const simpleTile )
+{
+    d->m_baseTiles.append( simpleTile );
+    deriveCompletionState();
+}
 
-//    mDebug() << "TextureTile::loadDataset end";
+void TextureTile::deriveCompletionState()
+{
+    QMap<SimpleTextureTile::State, int> count;
+    QVector<SimpleTextureTile*>::const_iterator pos = d->m_baseTiles.constBegin();
+    QVector<SimpleTextureTile*>::const_iterator const end = d->m_baseTiles.constEnd();
+    for (; pos != end; ++pos )
+        ++count[ (*pos)->state() ];
+
+    if ( count[ SimpleTextureTile::StateUptodate ] == d->m_baseTiles.size() )
+        d->m_state = TileComplete;
+    else if ( count[ SimpleTextureTile::StateEmpty ] == d->m_baseTiles.size() )
+        d->m_state = TileEmpty;
+    else
+        d->m_state = TilePartial;
 }
 
 void TextureTile::initJumpTables()
 {
     //    mDebug() << "Entered initJumpTables( bool ) of Tile" << d->m_id;
 
-    if ( d->m_rawtile.isNull() ) {
+    if ( d->m_resultTile.isNull() ) {
         qWarning() << "An essential tile is missing. Please rerun the application.";
         return;
     }
 
-    switch ( d->m_depth ) {
+    switch ( d->m_resultTile.depth() ) {
         case 48:
         case 32:
             delete [] d->jumpTable32;
-            d->jumpTable32 = jumpTableFromQImage32( d->m_rawtile );
+            d->jumpTable32 = jumpTableFromQImage32( d->m_resultTile );
             break;
         case 8:
         case 1:
             delete [] d->jumpTable8;
-            d->jumpTable8 = jumpTableFromQImage8( d->m_rawtile );
+            d->jumpTable8 = jumpTableFromQImage8( d->m_resultTile );
             break;
         default:
-            qWarning() << "Color depth" << d->m_depth << " is not supported.";
+            qWarning() << "Color depth" << d->m_resultTile.depth() << " is not supported.";
             return;
     }
 
-    d->m_isGrayscale = d->m_rawtile.isGrayscale();
 }
 
 uint TextureTile::pixel( int x, int y ) const
@@ -467,17 +362,45 @@ int TextureTile::depth() const
 
 int TextureTile::numBytes() const
 {
-    return d->m_rawtile.numBytes();
+    return d->m_resultTile.numBytes();
 }
 
 QImage TextureTile::rawtile() 
 {
-    return d->m_rawtile;
+    return d->m_resultTile;
 }
 
 QImage * TextureTile::tile()
 {
-    return &(d->m_rawtile);
+    return &(d->m_resultTile);
+}
+
+bool TextureTile::hasBaseTiles() const
+{
+    return !d->m_baseTiles.isEmpty();
+}
+
+void TextureTile::initResultTile()
+{
+    Q_ASSERT( hasBaseTiles() );
+    QVector<SimpleTextureTile*>::const_iterator pos = d->m_baseTiles.constBegin();
+    QVector<SimpleTextureTile*>::const_iterator const end = d->m_baseTiles.constEnd();
+    for (; pos != end; ++pos )
+        if ( (*pos)->state() != SimpleTextureTile::StateEmpty )
+            switch ( (*pos)->mergeRule() ) {
+            case SimpleTextureTile::MergeCopy:
+                d->mergeCopyToResult( *pos );
+                break;
+            case SimpleTextureTile::MergeMultiply:
+                d->mergeMultiplyToResult( *pos );
+                break;
+            }
+
+    initJumpTables();
+
+    // for now, this seems to be the best place for initializing this stuff
+    d->m_depth = d->m_resultTile.depth();
+    d->m_isGrayscale = d->m_resultTile.isGrayscale();
 }
 
 #include "TextureTile.moc"
diff --git a/marble/src/lib/TextureTile.h b/marble/src/lib/TextureTile.h
index 7a0b694..2d98306 100644
--- a/marble/src/lib/TextureTile.h
+++ b/marble/src/lib/TextureTile.h
@@ -35,24 +35,24 @@ namespace Marble
 
 class TextureTilePrivate;
 class GeoSceneTexture;
+class SimpleTextureTile;
+class TileLoader;
 
 class TextureTile : public AbstractTile
 {
     Q_OBJECT
+    friend class TileLoader;
 
  public:
     explicit TextureTile( TileId const& tid, QObject * parent = 0 );
     virtual ~TextureTile();
 
-    void setImage( const QByteArray & data );
-
-    // TODO: Move into DatasetProvider:
-    void loadDataset( GeoSceneTexture *textureLayer,
-                      QCache<TileId, TextureTile> *tileCache = 0 );
-
     int depth() const;
-
     int numBytes() const;
+    bool expired() const;
+
+    bool forMergedLayerDecorator() const;
+    void setForMergedLayerDecorator();
 
     QImage rawtile();
     QImage *tile();
@@ -81,7 +81,13 @@ class TextureTile : public AbstractTile
  private:
     Q_DECLARE_PRIVATE( TextureTile )
     Q_DISABLE_COPY( TextureTile )
+
+    void addBaseTile( SimpleTextureTile * const );
+    void deriveCompletionState();
     void initJumpTables();
+    bool hasBaseTiles() const;
+    void initResultTile();
+
     TextureTilePrivate *d;
 };
 
diff --git a/marble/src/lib/TextureTile_p.h b/marble/src/lib/TextureTile_p.h
index cd7cfd5..638b747 100644
--- a/marble/src/lib/TextureTile_p.h
+++ b/marble/src/lib/TextureTile_p.h
@@ -20,11 +20,12 @@
 
 #include "AbstractTile_p.h"
 
+#include <QtCore/QVector>
 #include <QtGui/QImage>
 
 namespace Marble
 {
-
+class SimpleTextureTile;
 
 class TextureTilePrivate : AbstractTilePrivate
 {
@@ -34,20 +35,20 @@ class TextureTilePrivate : AbstractTilePrivate
     uchar   **jumpTable8;
     uint    **jumpTable32;
 
-    QImage    m_rawtile;
+    QVector<SimpleTextureTile*> m_baseTiles;
+    QImage    m_resultTile;
 
     int       m_depth;
     bool      m_isGrayscale;
+    bool      m_forMergedLayerDecorator;
 
     explicit TextureTilePrivate( const TileId& id );
     virtual ~TextureTilePrivate();
 
     inline uint pixel( int x, int y ) const;
     inline uint pixelF( qreal x, qreal y, const QRgb& pixel ) const;
-
-    void scaleTileFrom( GeoSceneTexture *textureLayer, QImage &tile,
-                        qreal sourceX, qreal sourceY, int sourceLevel,
-                        int targetX, int targetY, int targetLevel );
+    void mergeCopyToResult( SimpleTextureTile const * const baseTile );
+    void mergeMultiplyToResult( SimpleTextureTile const * const baseTile );
 };
 
 }
diff --git a/marble/src/lib/TileId.cpp b/marble/src/lib/TileId.cpp
index efeca2f..2209704 100644
--- a/marble/src/lib/TileId.cpp
+++ b/marble/src/lib/TileId.cpp
@@ -5,34 +5,42 @@
 // find a copy of this license in LICENSE.txt in the top directory of
 // the source code.
 //
-// Copyright 2008      Jens-Michael Hoffmann  <jensmh at gmx.de>
+// Copyright 2008, 2010 Jens-Michael Hoffmann <jensmh at gmx.de>
 //
 
 // Own
 #include "TileId.h"
 
+#include <QtCore/QStringList>
+
 namespace Marble
 {
 
-TileId::TileId( int zoomLevel, int tileX, int tileY )
-    : m_zoomLevel( zoomLevel ), m_tileX( tileX ), m_tileY( tileY )
+TileId::TileId( QString const & mapThemeId, int zoomLevel, int tileX, int tileY )
+    : m_mapThemeIdHash( qHash( mapThemeId )), m_zoomLevel( zoomLevel ), m_tileX( tileX ), m_tileY( tileY )
+{
+}
+
+TileId::TileId( uint mapThemeIdHash, int zoomLevel, int tileX, int tileY )
+    : m_mapThemeIdHash( mapThemeIdHash ), m_zoomLevel( zoomLevel ), m_tileX( tileX ), m_tileY( tileY )
 {
 }
 
 TileId::TileId()
-    : m_zoomLevel( 0 ), m_tileX( 0 ), m_tileY( 0 )
+    : m_mapThemeIdHash( 0 ), m_zoomLevel( 0 ), m_tileX( 0 ), m_tileY( 0 )
 {
 }
 
 TileId TileId::fromString( QString const& idStr )
 {
-    int first = idStr.indexOf( ':' );
-    int last = idStr.lastIndexOf( ':' );
-
-    return TileId( idStr.left( first ).toInt(),
-                   idStr.mid( first + 1, last - first - 1 ).toInt(),
-                   idStr.mid( last + 1 ).toInt() );
+    QStringList const components = idStr.split( ':', QString::SkipEmptyParts );
+    Q_ASSERT( components.size() == 4 );
+
+    uint const mapThemeIdHash = components[ 0 ].toUInt();
+    int const zoomLevel = components[ 1 ].toInt();
+    int const tileX = components[ 2 ].toInt();
+    int const tileY = components[ 3 ].toInt();
+    return TileId( mapThemeIdHash, zoomLevel, tileX, tileY );
 }
 
-
 }
diff --git a/marble/src/lib/TileId.h b/marble/src/lib/TileId.h
index ffab41b..43b97b1 100644
--- a/marble/src/lib/TileId.h
+++ b/marble/src/lib/TileId.h
@@ -5,7 +5,7 @@
 // find a copy of this license in LICENSE.txt in the top directory of
 // the source code.
 //
-// Copyright 2008      Jens-Michael Hoffmann  <jensmh at gmx.de>
+// Copyright 2008, 2010 Jens-Michael Hoffmann <jensmh at gmx.de>
 //
 
 #ifndef MARBLE_TILE_ID_H
@@ -23,17 +23,20 @@ class TileId
     friend uint qHash( TileId const& );
 
  public:
-    TileId( int zoomLevel, int tileX, int tileY );
+    TileId( QString const & mapThemeId, int zoomLevel, int tileX, int tileY );
+    TileId( uint mapThemeIdHash, int zoomLevel, int tileX, int tileY );
     TileId();
 
     int zoomLevel() const;
     int x() const;
     int y() const;
+    uint mapThemeIdHash() const;
 
     QString toString() const;
     static TileId fromString( QString const& );
 
  private:
+    uint m_mapThemeIdHash;
     int m_zoomLevel;
     int m_tileX;
     int m_tileY;
@@ -60,16 +63,22 @@ inline int TileId::y() const
     return m_tileY;
 }
 
+inline uint TileId::mapThemeIdHash() const
+{
+    return m_mapThemeIdHash;
+}
+
 inline QString TileId::toString() const
 {
-    return QString( "%1:%2:%3" ).arg( m_zoomLevel ).arg( m_tileX ).arg( m_tileY );
+    return QString( "%1:%2:%3:%4" ).arg( m_mapThemeIdHash ).arg( m_zoomLevel ).arg( m_tileX ).arg( m_tileY );
 }
 
 inline bool operator==( TileId const& lhs, TileId const& rhs )
 {
     return lhs.m_zoomLevel == rhs.m_zoomLevel
         && lhs.m_tileX == rhs.m_tileX
-        && lhs.m_tileY == rhs.m_tileY;
+        && lhs.m_tileY == rhs.m_tileY
+        && lhs.m_mapThemeIdHash == rhs.m_mapThemeIdHash;
 }
 
 inline uint qHash( TileId const& tid )
@@ -77,7 +86,7 @@ inline uint qHash( TileId const& tid )
     const quint64 tmp = (( quint64 )( tid.m_zoomLevel ) << 36 )
         + (( quint64 )( tid.m_tileX ) << 18 )
         + ( quint64 )( tid.m_tileY );
-    return ::qHash( tmp );
+    return ::qHash( tmp ) ^ tid.m_mapThemeIdHash;
 }
 
 }
diff --git a/marble/src/lib/TileLoader.cpp b/marble/src/lib/TileLoader.cpp
index f488169..7b3126c 100644
--- a/marble/src/lib/TileLoader.cpp
+++ b/marble/src/lib/TileLoader.cpp
@@ -3,7 +3,7 @@
  *
  * Copyright 2005-2007 Torsten Rahn <tackat at kde.org>
  * Copyright 2007      Inge Wallin  <ingwa at kde.org>
- * Copyright 2008,2009 Jens-Michael Hoffmann <jensmh at gmx.de>
+ * Copyright 2008, 2009, 2010 Jens-Michael Hoffmann <jensmh at gmx.de>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -21,17 +21,22 @@
  * Boston, MA 02110-1301, USA.
  */
 
-
 #include "TileLoader.h"
 
+#include "SimpleTextureTile.h"
+#include "SimpleTileLoader.h"
+#include "MarbleModel.h"
 #include "global.h"
+#include "GeoSceneDocument.h"
+#include "GeoSceneHead.h"
 #include "GeoSceneLayer.h"
+#include "GeoSceneMap.h"
 #include "GeoSceneTexture.h"
 #include "HttpDownloadManager.h"
 #include "DatasetProvider.h"
 #include "TextureTile.h"
+#include "MapThemeManager.h"
 #include "MarbleDirs.h"
-#include "MarbleModel.h"
 #include "TileLoaderHelper.h"
 #include "MarbleDebug.h"
 
@@ -55,29 +60,34 @@ class TileLoaderPrivate
 public:
     TileLoaderPrivate()
         : m_datasetProvider( 0 ),
-          m_downloadManager( 0 ),
-          m_layer( 0 ),
-          m_tileWidth( 0 ),
-          m_tileHeight( 0 )
+          m_mapThemeManager( 0 ),
+          m_simpleTileLoader( 0 )
     {
         m_tileCache.setMaxCost( 20000 * 1024 ); // Cache size measured in bytes
     }
 
     DatasetProvider *m_datasetProvider;
-    HttpDownloadManager *m_downloadManager;
-    GeoSceneLayer *m_layer;
+    MapThemeManager const *m_mapThemeManager;
+    // TODO: comment about uint hash key
+    QHash<uint, GeoSceneLayer const *> m_sceneLayers;
+    QHash<uint, GeoSceneTexture*> m_textureLayers;
+    SimpleTileLoader *m_simpleTileLoader;
     QHash <TileId, TextureTile*>  m_tilesOnDisplay;
-    int           m_tileWidth;
-    int           m_tileHeight;
     QCache <TileId, TextureTile>  m_tileCache;
 };
 
 
-
-TileLoader::TileLoader( HttpDownloadManager *downloadManager, MarbleModel* parent )
-    : d( new TileLoaderPrivate() ),
-      m_parent( parent )
+TileLoader::TileLoader( MapThemeManager const * const mapThemeManager,
+                        HttpDownloadManager * const downloadManager, MarbleModel * const model )
+    : d( new TileLoaderPrivate ),
+      m_parent( model )
 {
+    d->m_mapThemeManager = mapThemeManager;
+    initTextureLayers();
+    d->m_simpleTileLoader = new SimpleTileLoader( mapThemeManager, downloadManager );
+    d->m_simpleTileLoader->setTextureLayers( d->m_textureLayers );
+    connect( d->m_simpleTileLoader, SIGNAL( tileCompleted( TileId, TileId )),
+             SLOT( updateTile( TileId, TileId )));
     setDownloadManager( downloadManager );
 }
 
@@ -85,49 +95,12 @@ TileLoader::~TileLoader()
 {
     flush();
     d->m_tileCache.clear();
-    if ( d->m_downloadManager != 0 )
-        d->m_downloadManager->disconnect( this );
-
+    delete d->m_simpleTileLoader;
     delete d;
 }
 
 void TileLoader::setDownloadManager( HttpDownloadManager *downloadManager )
 {
-    if ( d->m_downloadManager != 0 ) {
-        d->m_downloadManager->disconnect( this );
-        d->m_downloadManager = 0;
-    }
-
-    d->m_downloadManager = downloadManager;
-    if ( d->m_downloadManager != 0 ) {
-        connect( d->m_downloadManager, SIGNAL( downloadComplete( QString, QString )),
-                 SLOT( updateTile( QString, QString )));
-    }
-}
-
-void TileLoader::setLayer( GeoSceneLayer * layer )
-{
-    // Initialize map theme.
-    flush();
-    d->m_tileCache.clear();
-
-    if ( !layer ) {
-        mDebug() << "No layer specified! (GeoSceneLayer * layer = 0)";
-        return;
-    }
-
-    d->m_layer = layer;
-
-    TileId id( 0, 0, 0 );
-    TextureTile tile( id );
-
-    GeoSceneTexture * texture = static_cast<GeoSceneTexture *>( d->m_layer->groundDataset() );
-
-    tile.loadDataset( texture );
-
-    // We assume that all tiles have the same size. TODO: check to be safe
-    d->m_tileWidth  = tile.rawtile().width();
-    d->m_tileHeight = tile.rawtile().height();
 }
 
 void TileLoader::resetTilehash()
@@ -173,40 +146,8 @@ void TileLoader::flush()
     d->m_tilesOnDisplay.clear();
 }
 
-int TileLoader::tileWidth() const
-{
-    return d->m_tileWidth;
-}
-
-int TileLoader::tileHeight() const
-{
-    return d->m_tileHeight;
-}
-
-int TileLoader::globalWidth( int level ) const
+TextureTile* TileLoader::loadTile( TileId const &tileId, bool const forMergedLayerDecorator )
 {
-    if ( !d->m_layer ) return 0;
-
-    GeoSceneTexture * texture = static_cast<GeoSceneTexture *>( d->m_layer->groundDataset() );
-
-    return d->m_tileWidth * TileLoaderHelper::levelToColumn(
-                                texture->levelZeroColumns(), level );
-}
-
-int TileLoader::globalHeight( int level ) const
-{
-    if ( !d->m_layer ) return 0;
-
-    GeoSceneTexture * texture = static_cast<GeoSceneTexture *>( d->m_layer->groundDataset() );
-
-    return d->m_tileHeight * TileLoaderHelper::levelToRow(
-                                texture->levelZeroRows(), level );
-}
-
-TextureTile* TileLoader::loadTile( TileId const &tileId )
-{
-    if ( !d->m_layer ) return 0;
-
     // check if the tile is in the hash
     TextureTile * tile = d->m_tilesOnDisplay.value( tileId, 0 );
     if ( tile ) {
@@ -215,18 +156,15 @@ TextureTile* TileLoader::loadTile( TileId const &tileId )
     }
     // here ends the performance critical section of this method
 
+    mDebug() << "TileLoader::loadTile" << tileId.toString();
+
     // the tile was not in the hash or has been removed because of expiration
     // so check if it is in the cache
     tile = d->m_tileCache.take( tileId );
-
-    GeoSceneTexture * texture = static_cast<GeoSceneTexture *>( d->m_layer->groundDataset() );
-
     if ( tile ) {
         // the tile was in the cache, but is it up to date?
-        const QDateTime now = QDateTime::currentDateTime();
-
-        if ( tile->created().secsTo( now ) < texture->expire()) {
-            d->m_tilesOnDisplay[tileId] = tile;
+        if ( !tile->expired() ) {
+            d->m_tilesOnDisplay[ tileId ] = tile;
             tile->setUsed( true );
             return tile;
         } else {
@@ -235,34 +173,53 @@ TextureTile* TileLoader::loadTile( TileId const &tileId )
         }
     }
 
+    GeoSceneTexture * const texture = d->m_textureLayers[ tileId.mapThemeIdHash() ];
+
     // tile (valid) has not been found in hash or cache, so load it from disk
     // and place it in the hash from where it will get transferred to the cache
 
     // mDebug() << "load Tile from Disk: " << tileId.toString();
     tile = new TextureTile( tileId );
-    d->m_tilesOnDisplay[tileId] = tile;
+    if ( forMergedLayerDecorator )
+        tile->setForMergedLayerDecorator();
+    d->m_tilesOnDisplay[ tileId ] = tile;
 
-    // FIXME: Implement asynchronous tile loading
-    // d->m_datasetProvider->loadDatasets( tile );
+    GeoSceneLayer const * const sceneLayer = d->m_sceneLayers.value( tileId.mapThemeIdHash(), 0 );
+    Q_ASSERT( sceneLayer );
+    mDebug() << "scene layer:" << sceneLayer->name();
 
-    if ( d->m_downloadManager != 0 ) {
-        connect( tile, SIGNAL( downloadTile( QUrl, QString, QString, DownloadUsage ) ),
-                 d->m_downloadManager, SLOT( addJob( QUrl, QString, QString, DownloadUsage ) ) );
-    }
-    tile->loadDataset( texture, &d->m_tileCache );
+    QVector<GeoSceneAbstractDataset*> textureLayers = sceneLayer->datasets();
+    mDebug() << "TileLoader::loadTile: loading base tiles:"
+             << textureLayers.size() << "texture layers found";
 
-    // TODO should emit signal rather than directly calling paintTile
-    // emit paintTile( tile, tilx, tily, tileLevel, d->m_theme, false );
-    m_parent->paintTile( tile, texture );
+    QVector<GeoSceneAbstractDataset*>::const_iterator pos = textureLayers.constBegin();
+    QVector<GeoSceneAbstractDataset*>::const_iterator const end = textureLayers.constEnd();
+    for (; pos != end; ++pos ) {
+        GeoSceneTexture const * const textureLayer = dynamic_cast<GeoSceneTexture const *>( *pos );
+        if ( !textureLayer )
+            mDebug() << "not a texture layer" << (*pos)->name();
+        if ( textureLayer && ( !textureLayer->hasMaximumTileLevel()
+                               || tileId.zoomLevel() <= textureLayer->maximumTileLevel() )) {
+            TileId const simpleTileId( textureLayer->sourceDir(), tileId.zoomLevel(),
+                                       tileId.x(), tileId.y() );
+            mDebug() << "TileLoader::loadTile: base tile" << textureLayer->sourceDir() << simpleTileId.toString();
+            SimpleTextureTile * const simpleTile = d->m_simpleTileLoader->loadTile( tileId, simpleTileId );
+            // hack to try clouds
+            if ( simpleTile && tile->hasBaseTiles() )
+                simpleTile->setMergeRule( SimpleTextureTile::MergeMultiply );
+            if ( simpleTile )
+                tile->addBaseTile( simpleTile );
+        }
+    }
+    Q_ASSERT( tile->hasBaseTiles() );
 
+    if ( tile->state() != TextureTile::TileEmpty ) {
+        tile->initResultTile();
+        mergeDecorations( tile, texture );
+    }
     return tile;
 }
 
-GeoSceneLayer * TileLoader::layer() const
-{
-    return d->m_layer;
-}
-
 quint64 TileLoader::volatileCacheLimit() const
 {
     return d->m_tileCache.maxCost() / 1024;
@@ -281,12 +238,8 @@ QList<TileId> TileLoader::tilesOnDisplay() const
     return result;
 }
 
-int TileLoader::maximumTileLevel() const
+int TileLoader::maximumTileLevel( GeoSceneTexture const * const texture )
 {
-    if ( !d->m_layer )
-        return -1;
-
-    GeoSceneTexture * texture = static_cast<GeoSceneTexture *>( d->m_layer->groundDataset() );
     if ( !texture )
         return -1;
 
@@ -347,39 +300,17 @@ void TileLoader::setVolatileCacheLimit( quint64 kiloBytes )
     d->m_tileCache.setMaxCost( kiloBytes * 1024 );
 }
 
-void TileLoader::updateTile( const QByteArray &data, const QString &idStr )
-{
-    if ( !d->m_layer )
-        return;
-
-    const TileId id = TileId::fromString( idStr );
-    QHash<TileId, TextureTile*>::iterator pos = d->m_tilesOnDisplay.find( id );
-    if ( pos != d->m_tilesOnDisplay.end()) {
-        pos.value()->setImage( data );
-        GeoSceneTexture * const texture = static_cast<GeoSceneTexture *>( d->m_layer->groundDataset() );
-        m_parent->paintTile( pos.value(), texture );
-        emit tileUpdateAvailable();
-    } else {
-        // Remove "false" tile from cache so it doesn't get loaded anymore
-        d->m_tileCache.remove( id );
-    }
-}
-
-void TileLoader::updateTile( const QString &fileName, const QString &idStr )
+void TileLoader::updateTile( TileId const & composedTileId, TileId const & baseTileId )
 {
-    if ( !d->m_layer )
-        return;
-
-    const TileId id = TileId::fromString( idStr );
-    if ( d->m_tilesOnDisplay.contains( id ) ) {
-        GeoSceneTexture * texture = static_cast<GeoSceneTexture *>( d->m_layer->groundDataset() );
-        d->m_tilesOnDisplay[id]->loadDataset( texture, &d->m_tileCache );
-        m_parent->paintTile( d->m_tilesOnDisplay[id], texture );
+    TextureTile * const tile = d->m_tilesOnDisplay.value( composedTileId, 0 );
+    if ( tile ) {
+        tile->deriveCompletionState();
+        tile->initResultTile();
+        mergeDecorations( tile, findTextureLayer( composedTileId ));
         emit tileUpdateAvailable();
-    } else {
-        // Remove "false" tile from cache so it doesn't get loaded anymore
-        d->m_tileCache.remove( id );
-    }
+    } else
+        // TODO: also update tiles in the cache, not doing it is really a waste of i/o
+        d->m_tileCache.remove( composedTileId );
 }
 
 void TileLoader::update()
@@ -390,6 +321,66 @@ void TileLoader::update()
     emit tileUpdateAvailable();
 }
 
+inline GeoSceneTexture const * TileLoader::findTextureLayer( TileId const & id ) const
+{
+    GeoSceneTexture const * const textureLayer = d->m_textureLayers.value( id.mapThemeIdHash(), 0 );
+    Q_ASSERT( textureLayer );
+    return textureLayer;
+}
+
+inline GeoSceneTexture * TileLoader::findTextureLayer( TileId const & id )
+{
+    GeoSceneTexture * const textureLayer = d->m_textureLayers.value( id.mapThemeIdHash(), 0 );
+    Q_ASSERT( textureLayer );
+    return textureLayer;
+}
+
+void TileLoader::initTextureLayers()
+{
+    QList<GeoSceneDocument const *> const & mapThemes = d->m_mapThemeManager->mapThemes();
+    QList<GeoSceneDocument const *>::const_iterator pos = mapThemes.constBegin();
+    QList<GeoSceneDocument const *>::const_iterator const end = mapThemes.constEnd();
+    for (; pos != end; ++pos ) {
+        GeoSceneHead const * head = (*pos)->head();
+        Q_ASSERT( head );
+        const QString mapThemeId = head->target() + '/' + head->theme();
+        mDebug() << "TileLoader::initTextureLayers" << mapThemeId;
+
+        GeoSceneMap const * map = (*pos)->map();
+        Q_ASSERT( map );
+        GeoSceneLayer const * sceneLayer = map->layer( head->theme() );
+        if ( !sceneLayer ) {
+            mDebug() << "ignoring, has no GeoSceneLayer for" << head->theme();
+            continue;
+        }
+
+        d->m_sceneLayers.insert( qHash( mapThemeId ), sceneLayer );
+
+        // find all texture layers
+        QVector<GeoSceneAbstractDataset *> layers = sceneLayer->datasets();
+        QVector<GeoSceneAbstractDataset *>::const_iterator pos = layers.constBegin();
+        QVector<GeoSceneAbstractDataset *>::const_iterator const end = layers.constEnd();
+        for (; pos != end; ++pos ) {
+            GeoSceneTexture * const textureLayer = dynamic_cast<GeoSceneTexture *>( *pos );
+            if ( !textureLayer ) {
+                mDebug() << "ignoring dataset, is not a texture layer";
+                continue;
+            }
+            d->m_textureLayers.insert( qHash( textureLayer->sourceDir() ), textureLayer );
+            mDebug() << "TileLoader::initTextureLayers" << "added texture layer:"
+                     << qHash( textureLayer->sourceDir() ) << textureLayer->sourceDir();
+        }
+    }
+}
+
+void TileLoader::mergeDecorations( TextureTile * const tile,
+                                   GeoSceneTexture * const textureLayer ) const
+{
+    Q_ASSERT( tile->state() != TextureTile::TileEmpty );
+    if ( !tile->forMergedLayerDecorator() )
+        m_parent->paintTile( tile, textureLayer );
+}
+
 }
 
 #include "TileLoader.moc"
diff --git a/marble/src/lib/TileLoader.h b/marble/src/lib/TileLoader.h
index bda81de..ae0c7fc 100644
--- a/marble/src/lib/TileLoader.h
+++ b/marble/src/lib/TileLoader.h
@@ -35,8 +35,10 @@ namespace Marble
 
 class TextureTile;
 class HttpDownloadManager;
+class MapThemeManager;
 class MarbleModel;
 class GeoSceneLayer;
+class GeoSceneTexture;
 
 class TileLoaderPrivate;
 
@@ -64,11 +66,8 @@ class TileLoader : public QObject
          * @param downloadManager The download manager that shall be used to fetch
          *                        the tiles from a remote resource.
          */
-        TileLoader( HttpDownloadManager *downloadManager, MarbleModel* parent);
-
-        /**
-         * Destroys the tile loader.
-         */
+        TileLoader( MapThemeManager const * const mapThemeManager,
+                    HttpDownloadManager * const downloadManager, MarbleModel * const model );
         virtual ~TileLoader();
 
         /**
@@ -82,17 +81,7 @@ class TileLoader : public QObject
          *
          * @param tileId The Id of the requested tile, containing the x and y coordinate and the zoom level.
          */
-        TextureTile* loadTile( TileId const &tileId );
-
-        /**
-         * Sets the texture layer @p the tiles shall be loaded for.
-         */
-        void setLayer( GeoSceneLayer * layer );
-
-        /**
-         * Returns the texture layer the tiles shall be loaded for.
-         */
-        GeoSceneLayer * layer() const;
+        TextureTile* loadTile( TileId const &tileId, bool const forMergedLayerDecorator = false );
 
         /**
          * Resets the internal tile hash.
@@ -114,26 +103,6 @@ class TileLoader : public QObject
         void flush();
 
         /**
-         * Returns the width of a tile loaded by this tile loader.
-         */
-        int tileWidth() const;
-
-        /**
-         * Returns the height of a tile loaded by this tile loader.
-         */
-        int tileHeight() const;
-
-        /**
-         * Returns the global width for the given @p level.
-         */
-        int globalWidth( int level ) const;
-
-        /**
-         * Returns the global height for the given @p level.
-         */
-        int globalHeight( int level ) const;
-
-        /**
          * @brief  Returns the limit of the volatile (in RAM) cache.
          * @return the cache limit in kilobytes
          */
@@ -150,7 +119,7 @@ class TileLoader : public QObject
          * Returns the highest level in which some tiles are theoretically
          * available for the given @p texture layer.
          */
-        int maximumTileLevel() const;
+        static int maximumTileLevel( GeoSceneTexture const * const textureLayer );
 
         /**
          * Returns whether the mandatory most basic tile level is fully available for
@@ -165,9 +134,9 @@ class TileLoader : public QObject
          */
         void setVolatileCacheLimit( quint64 kiloBytes );
 
-        void updateTile( const QByteArray &data, const QString &id );
-
-        void updateTile( const QString &fileName, const QString &id );
+        /**
+         */
+        void updateTile( TileId const & composedTileId, TileId const & baseTileId );
 
         /**
          * Effectively triggers a reload of all tiles that are currently in use
@@ -181,13 +150,13 @@ class TileLoader : public QObject
          * downloaded and is available now.
          */
         void tileUpdateAvailable();
-        
-        void paintTile(TextureTile* tile, int x, int y, int level,
-                       GeoSceneLayer * layer,
-                       bool requestTileUpdate);
 
     private:
         Q_DISABLE_COPY( TileLoader )
+        GeoSceneTexture const * findTextureLayer( TileId const & ) const;
+        GeoSceneTexture * findTextureLayer( TileId const & );
+        void initTextureLayers();
+        void mergeDecorations( TextureTile * const, GeoSceneTexture * const ) const;
 
         TileLoaderPrivate* const d;
         MarbleModel* m_parent;
diff --git a/marble/src/lib/TileLoaderHelper.cpp b/marble/src/lib/TileLoaderHelper.cpp
index 7818404..6c09934 100644
--- a/marble/src/lib/TileLoaderHelper.cpp
+++ b/marble/src/lib/TileLoaderHelper.cpp
@@ -105,8 +105,8 @@ QUrl TileLoaderHelper::downloadUrl( GeoSceneTexture *textureLayer, int zoomLevel
     return tileUrl;
 }
 
-QString TileLoaderHelper::relativeTileFileName( GeoSceneTexture *textureLayer, int level, int x,
-                                                int y )
+QString TileLoaderHelper::relativeTileFileName( GeoSceneTexture const * const textureLayer,
+                                                int level, int x, int y )
 {
     QString relFileName;
     if ( textureLayer ) {
@@ -142,7 +142,7 @@ QString TileLoaderHelper::relativeTileFileName( GeoSceneTexture *textureLayer, i
     return relFileName;
 }
 
-QString TileLoaderHelper::themeStr( GeoSceneTexture *textureLayer )
+QString TileLoaderHelper::themeStr( GeoSceneTexture const * const textureLayer )
 {
     QString oldThemeStr;
 
diff --git a/marble/src/lib/TileLoaderHelper.h b/marble/src/lib/TileLoaderHelper.h
index 9ba4d16..a48740b 100644
--- a/marble/src/lib/TileLoaderHelper.h
+++ b/marble/src/lib/TileLoaderHelper.h
@@ -87,7 +87,7 @@ namespace TileLoaderHelper
     /**
      * @brief Get the relative file name of a tile.
      */
-    QString relativeTileFileName( GeoSceneTexture *textureLayer, int zoomLevel, int x, int y );
+    QString relativeTileFileName( GeoSceneTexture const * const textureLayer, int zoomLevel, int x, int y );
 
     /**
      * @brief Get the theme string
@@ -95,7 +95,7 @@ namespace TileLoaderHelper
      * @return the old style theme string which used to be the argument for many methods,
      *         for example "maps/earth/srtm".
      */
-    QString themeStr( GeoSceneTexture *textureLayer );
+    QString themeStr( GeoSceneTexture const * const textureLayer );
 }
 
 }
diff --git a/marble/src/lib/geodata/scene/GeoSceneMap.cpp b/marble/src/lib/geodata/scene/GeoSceneMap.cpp
index c580f4e..f9b83e9 100644
--- a/marble/src/lib/geodata/scene/GeoSceneMap.cpp
+++ b/marble/src/lib/geodata/scene/GeoSceneMap.cpp
@@ -109,6 +109,21 @@ GeoSceneLayer* GeoSceneMap::layer( const QString& name )
     return layer;
 }
 
+const GeoSceneLayer* GeoSceneMap::layer( const QString& name ) const
+{
+    const GeoSceneLayer* layer = 0;
+
+    QVector<GeoSceneLayer*>::const_iterator it = d->m_layers.constBegin();
+    QVector<GeoSceneLayer*>::const_iterator end = d->m_layers.constEnd();
+    for (; it != end; ++it) {
+        if ( (*it)->name() == name ) {
+            layer = *it;
+            break;
+        }
+    }
+    return layer;
+}
+
 QVector<GeoSceneLayer*> GeoSceneMap::layers() const
 {
     return d->m_layers;
diff --git a/marble/src/lib/geodata/scene/GeoSceneMap.h b/marble/src/lib/geodata/scene/GeoSceneMap.h
index 88fb045..8668483 100644
--- a/marble/src/lib/geodata/scene/GeoSceneMap.h
+++ b/marble/src/lib/geodata/scene/GeoSceneMap.h
@@ -65,6 +65,7 @@ class GEODATA_EXPORT GeoSceneMap : public GeoNode
      * @return A pointer to the layer request by its name
      */
     GeoSceneLayer* layer( const QString& name );
+    const GeoSceneLayer* layer( const QString& name ) const;
 
     /**
      * @brief  Return all layers
diff --git a/marble/src/lib/geodata/scene/GeoSceneTexture.h b/marble/src/lib/geodata/scene/GeoSceneTexture.h
index fea700d..af28c44 100644
--- a/marble/src/lib/geodata/scene/GeoSceneTexture.h
+++ b/marble/src/lib/geodata/scene/GeoSceneTexture.h
@@ -66,6 +66,7 @@ class GeoSceneTexture : public GeoSceneAbstractDataset
     int levelZeroRows() const;
     void setLevelZeroRows( const int );
 
+    bool hasMaximumTileLevel() const;
     int maximumTileLevel() const;
     void setMaximumTileLevel( const int );
 
@@ -104,6 +105,11 @@ class GeoSceneTexture : public GeoSceneAbstractDataset
     QList<DownloadPolicy *> m_downloadPolicies;
 };
 
+inline bool GeoSceneTexture::hasMaximumTileLevel() const
+{
+    return m_maximumTileLevel != -1;
+}
+
 }
 
 #endif // GEOSCENETEXTURE_H


More information about the Marble-devel mailing list