Zoom animation & min level change, queued stacked tiles removal
authorSami Rämö <sami.ramo@ixonos.com>
Tue, 27 Apr 2010 09:55:33 +0000 (12:55 +0300)
committerSami Rämö <sami.ramo@ixonos.com>
Tue, 27 Apr 2010 09:55:33 +0000 (12:55 +0300)
  * Minimum viewable zoom level to 2

  * Queued removal of stacked tiles

  * Fix for case where map tiles from wrong zoom level
    were shown when zoom level was changed multiple times
    rapidly

  * current view tiles rect is updated frequently to
    MapScene and cleanup algorithms use that value

  * changed self made zoom animation to QPropertyAnimation

src/map/mapcommon.h
src/map/mapengine.cpp
src/map/mapengine.h
src/map/mapscene.cpp
src/map/mapscene.h
src/map/mapview.cpp
src/map/mapview.h
src/ui/mapviewscreen.cpp

index 6435843..e1a8c25 100644 (file)
@@ -30,6 +30,7 @@ const int TILE_SIZE_Y = 256; ///< Tile image size in y direction
 
 const int MIN_MAP_ZOOM_LEVEL = 0; ///< Minimum zoom level
 const int MAX_MAP_ZOOM_LEVEL = 18; ///< Maximum zoom level
+const int MIN_VIEW_ZOOM_LEVEL = 2; ///< Minimum zoom level for MapView
 const int MIN_MAP_SCENE_NORMAL_LEVEL = MAX_MAP_ZOOM_LEVEL + 1;
 
 const int ZOOM_FPS = 30; ///< FPS for the zoom effect
index 4849b86..23fa120 100644 (file)
@@ -36,6 +36,7 @@ MapEngine::MapEngine(QObject *parent)
     : QObject(parent)
     , m_centerTile(QPoint(UNDEFINED, UNDEFINED))
     , m_viewSize(QSize(DEFAULT_SCREEN_WIDTH, DEFAULT_SCREEN_HEIGHT))
+    , m_zoomDirection(OUT)
     , m_zoomLevel(DEFAULT_ZOOM_LEVEL)
 {
     m_mapScene = new MapScene(this);
@@ -100,7 +101,9 @@ void MapEngine::mapImageReceived(const QUrl &url, const QPixmap &pixmap)
     if (!m_mapScene->isTileInScene(hashKey)) {
 
         MapTile *mapTile = new MapTile();
+        /// @todo SET SCENE LEVEL AUTOMATICALLY WHEN CHANGING ZOOM LEVEL
         mapTile->setZoomLevel(zoom);
+        mapTile->setSceneLevel(m_zoomLevel);
         mapTile->setTileNumber(QPoint(x, y));
         mapTile->setPixmap(pixmap);
 
@@ -108,7 +111,8 @@ void MapEngine::mapImageReceived(const QUrl &url, const QPixmap &pixmap)
 
         m_mapScene->debugItemsCount();
 
-        m_mapScene->removeStackedTiles(mapTile, viewRect());
+        m_mapScene->enqueueRemoveStackedTiles(mapTile);
+        //m_mapScene->removeStackedTiles(mapTile, viewRect());
    }
 }
 
@@ -149,7 +153,7 @@ void MapEngine::setLocation(QPoint sceneCoordinate)
 
     if (isCenterTileChanged(sceneCoordinate)) {
         getTiles(sceneCoordinate);
-        m_mapScene->removeOutOfViewTiles(viewRect());
+        m_mapScene->removeOutOfViewTiles();
     }
 }
 
@@ -166,12 +170,13 @@ void MapEngine::getTiles(QPoint sceneCoordinate)
 {
 //    qDebug() << __PRETTY_FUNCTION__;
 
-    m_viewGrid = calculateTileGrid(sceneCoordinate);
+    m_viewTilesGrid = calculateTileGrid(sceneCoordinate);
+    updateViewTilesSceneRect();
 
-    int topLeftX = m_viewGrid.topLeft().x();
-    int topLeftY = m_viewGrid.topLeft().y();
-    int bottomRightX = m_viewGrid.bottomRight().x();
-    int bottomRightY = m_viewGrid.bottomRight().y();
+    int topLeftX = m_viewTilesGrid.topLeft().x();
+    int topLeftY = m_viewTilesGrid.topLeft().y();
+    int bottomRightX = m_viewTilesGrid.bottomRight().x();
+    int bottomRightY = m_viewTilesGrid.bottomRight().y();
 
     int tileMaxVal = tileMaxValue(m_zoomLevel);
 
@@ -199,25 +204,28 @@ void MapEngine::getTiles(QPoint sceneCoordinate)
     }
 }
 
-QRect MapEngine::viewRect()
+void MapEngine::updateViewTilesSceneRect()
 {
-    QPoint topLeft = convertTileNumberToSceneCoordinate(m_zoomLevel, m_viewGrid.topLeft());
-    QPoint bottomRight = convertTileNumberToSceneCoordinate(m_zoomLevel, m_viewGrid.bottomRight()
+    QPoint topLeft = convertTileNumberToSceneCoordinate(m_zoomLevel, m_viewTilesGrid.topLeft());
+    QPoint bottomRight = convertTileNumberToSceneCoordinate(m_zoomLevel, m_viewTilesGrid.bottomRight()
                                                              + QPoint(1, 1));
-    return QRect(topLeft, bottomRight);
+
+    m_mapScene->viewRectUpdated(QRect(topLeft, bottomRight));
 }
 
 void MapEngine::viewResized(const QSize &size)
 {
     m_viewSize = size;
     getTiles(m_sceneCoordinate);
-    m_mapScene->removeOutOfViewTiles(viewRect());
+    m_mapScene->removeOutOfViewTiles();
 }
 
-void MapEngine::viewZoomInFinished()
+void MapEngine::viewZoomFinished()
 {
-    qDebug() << __PRETTY_FUNCTION__;
-    m_mapScene->removeOutOfViewTiles(viewRect());
+//    qDebug() << __PRETTY_FUNCTION__;
+
+    if (m_zoomDirection == IN)
+        m_mapScene->removeOutOfViewTiles();
 }
 
 void MapEngine::zoomIn()
@@ -226,6 +234,7 @@ void MapEngine::zoomIn()
 
     if (m_zoomLevel < MAX_MAP_ZOOM_LEVEL) {
         m_zoomLevel++;
+        m_zoomDirection = IN;
         emit zoomLevelChanged(m_zoomLevel);
 
         m_mapScene->setTilesDrawingLevels(m_zoomLevel);
@@ -238,8 +247,9 @@ void MapEngine::zoomOut()
 {
 //    qDebug() << __PRETTY_FUNCTION__;
 
-    if (m_zoomLevel > MIN_MAP_ZOOM_LEVEL) {
+    if (m_zoomLevel > MIN_VIEW_ZOOM_LEVEL) {
         m_zoomLevel--;
+        m_zoomDirection = OUT;
         emit zoomLevelChanged(m_zoomLevel);
 
         m_mapScene->setTilesDrawingLevels(m_zoomLevel);
index 4b156ab..455da81 100644 (file)
@@ -196,11 +196,12 @@ private:
     int tileMaxValue(int zoomLevel);
 
     /**
-    * @brief Returns the current view rect including margins
+    * @brief Updates the current view rect including margins
     *
-    * @return Current view rect
+    * Calculates tiles rect in scene based on m_viewTilesGrid and
+    * calls MapScene::viewRectUpdated()
     */
-    QRect viewRect();
+    void updateViewTilesSceneRect();
 
 private slots:
     /**
@@ -217,7 +218,7 @@ private slots:
     *
     * Does run removeOutOfViewTiles
     */
-    void viewZoomInFinished();
+    void viewZoomFinished();
 
     /**
     * @brief Slot for zooming in
@@ -260,13 +261,16 @@ signals:
  * DATA MEMBERS
  ******************************************************************************/
 private:
+    enum ZoomDirection { IN, OUT };
+
     MapFetcher *m_mapFetcher; ///< Fetcher for map tiles
     MapScene *m_mapScene; ///< Scene for map tiles
     MapZoomPanel *m_mapZoomPanel; ///< Toolbar for zoom buttons
     QPoint m_centerTile; ///< Current center tile
     QPoint m_sceneCoordinate; ///< Current center coordinate
-    QRect m_viewGrid; ///< Current grid of tiles in view (includes margin)
+    QRect m_viewTilesGrid; ///< Current grid of tiles in view (includes margin)
     QSize m_viewSize; ///< Current view size
+    ZoomDirection m_zoomDirection;
     int m_zoomLevel; ///< Current zoom level
 };
 
index 53def12..33ecbec 100644 (file)
@@ -26,6 +26,8 @@
 
 MapScene::MapScene(QObject *parent)
     : QGraphicsScene(parent)
+    , m_isRemoveStackedTilesRunning(false)
+    , m_viewRect(QRect(0, 0, 0, 0))
 {
     const int maxTilesPerSide = (1 << MAX_MAP_ZOOM_LEVEL);
     const int maxPixelsX = maxTilesPerSide * TILE_SIZE_X;
@@ -41,7 +43,20 @@ void MapScene::addTile(MapTile *mapTile, QString hashKey)
 
 void MapScene::debugItemsCount()
 {
-//    qDebug() << __PRETTY_FUNCTION__ << "items:" << items().count();
+#ifndef Q_WS_MAEMO_5
+    qDebug() << __PRETTY_FUNCTION__ << "ITEM ADDED:" << items().count();
+#endif // Q_WS_MAEMO_5
+}
+
+void MapScene::enqueueRemoveStackedTiles(MapTile *newTile)
+{
+//    qDebug() << __PRETTY_FUNCTION__;
+
+    m_removeStackedTilesList << newTile;
+    if (!m_isRemoveStackedTilesRunning) {
+        m_isRemoveStackedTilesRunning = true;
+        QTimer::singleShot(0, this, SLOT(removeStackedTile()));
+    }
 }
 
 bool MapScene::isTileInScene(QString hashKey)
@@ -49,13 +64,39 @@ bool MapScene::isTileInScene(QString hashKey)
     return m_mapTilesInScene.contains(hashKey);
 }
 
-void MapScene::removeOutOfViewTiles(QRect viewRect)
+void MapScene::removeStackedTile()
 {
 //    qDebug() << __PRETTY_FUNCTION__;
 
-    QList<QGraphicsItem *> viewTiles = items(viewRect, Qt::ContainsItemBoundingRect);
+    if (!m_removeStackedTilesList.isEmpty()) {
+        MapTile *tile = m_removeStackedTilesList.takeFirst();
+        removeStackedTiles(tile);
+    }
+
+    // schedule removal of the next tile if the list is not empty
+    if (!m_removeStackedTilesList.isEmpty()) {
+        QTimer::singleShot(0, this, SLOT(removeStackedTile()));
+    }
+    else {
+        m_isRemoveStackedTilesRunning = false;
+#ifndef Q_WS_MAEMO_5
+    qDebug() << __PRETTY_FUNCTION__ << "STACKED END:" << items().count();
+#endif // Q_WS_MAEMO_5
+    }
+}
+
+void MapScene::removeOutOfViewTiles()
+{
+//    qDebug() << __PRETTY_FUNCTION__;
+
+    QList<QGraphicsItem *> viewTiles = items(m_viewRect, Qt::ContainsItemBoundingRect);
     QList<QGraphicsItem *> allTiles = items();
 
+#ifndef Q_WS_MAEMO_5
+    qDebug() << __PRETTY_FUNCTION__ << "OUT OF VIEW START:" << items().count();
+#endif // Q_WS_MAEMO_5
+
+
 //    qDebug() << __PRETTY_FUNCTION__ << "All tiles:" << allTiles.count();
 //    qDebug() << __PRETTY_FUNCTION__ << "Tiles in view area:" << viewTiles.count();
 
@@ -71,10 +112,13 @@ void MapScene::removeOutOfViewTiles(QRect viewRect)
             removeTile(tileToRemove);
     }
 
-    debugItemsCount();
+#ifndef Q_WS_MAEMO_5
+    qDebug() << __PRETTY_FUNCTION__ << "OUT OF VIEW END:" << items().count();
+#endif // Q_WS_MAEMO_5
+
 }
 
-void MapScene::removeStackedTiles(MapTile *newTile, QRect viewRect)
+void MapScene::removeStackedTiles(MapTile *newTile)
 {
 //    qDebug() << __PRETTY_FUNCTION__;
 
@@ -97,7 +141,7 @@ void MapScene::removeStackedTiles(MapTile *newTile, QRect viewRect)
         else {
             // remove tile if it is obscured in the view area
             QRect collidingTileViewableArea =
-                    collidingTileSceneRect.intersected(viewRect).toRect();
+                    collidingTileSceneRect.intersected(m_viewRect).toRect();
             if (collidingTile->isObscured(collidingTileViewableArea)) {
                 qDebug() << __PRETTY_FUNCTION__ << "Deleting obscured item";
                 MapTile *tile = dynamic_cast<MapTile *>(collidingTile);
@@ -108,7 +152,6 @@ void MapScene::removeStackedTiles(MapTile *newTile, QRect viewRect)
     }
 //    qDebug() << __PRETTY_FUNCTION__ << "All tiles after:"
 //             << newTile->collidingItems(Qt::IntersectsItemBoundingRect).count();
-    debugItemsCount();
 }
 
 
@@ -121,6 +164,7 @@ void MapScene::removeTile(MapTile *tile)
                                                      tile->tileNumber().x(),
                                                      tile->tileNumber().y()));
         removeItem(tile);
+        m_removeStackedTilesList.removeAll(tile);
         delete tile;
     }
 }
@@ -137,3 +181,8 @@ void MapScene::setTilesDrawingLevels(int zoomLevel)
             item->setSceneLevel(zoomLevel);
     }
 }
+
+void MapScene::viewRectUpdated(QRect viewRect)
+{
+    m_viewRect = viewRect;
+}
index 5a8a42c..87d1bee 100644 (file)
@@ -33,6 +33,7 @@
 */
 class MapScene : public QGraphicsScene
 {
+    Q_OBJECT
 public:
     /**
     * @brief Constructor
@@ -56,6 +57,8 @@ public:
 
     void debugItemsCount();
 
+    void enqueueRemoveStackedTiles(MapTile *newTile);
+
     /**
     * @brief Returns if tile mathcing hash key is already in the scene
     *
@@ -69,7 +72,7 @@ public:
     *
     * @param viewRect Current view rect
     */
-    void removeOutOfViewTiles(QRect viewRect);
+    void removeOutOfViewTiles();
 
     /**
     * @brief Remove tiles which are stacked.
@@ -78,9 +81,8 @@ public:
     * tiles.
     *
     * @param newTile new tile covering old tiles
-    * @param viewRect Current view rect
     */
-    void removeStackedTiles(MapTile *newTile, QRect viewRect);
+    void removeStackedTiles(MapTile *newTile);
 
     /**
     * @brief Remove tile.
@@ -98,11 +100,19 @@ public:
     */
     void setTilesDrawingLevels(int zoomLevel);
 
+    void viewRectUpdated(QRect viewRect);
+
+public slots:
+    void removeStackedTile();
+
 /*******************************************************************************
  * DATA MEMBERS
  ******************************************************************************/
 private:
+    bool m_isRemoveStackedTilesRunning;
     QHash<QString, MapTile *> m_mapTilesInScene;  ///< List of map tiles in map scene
+    QList<MapTile *> m_removeStackedTilesList;
+    QRect m_viewRect;
 };
 
 #endif // MAPSCENE_H
index 15de9cb..62ba764 100644 (file)
@@ -45,17 +45,27 @@ MapView::MapView(QWidget *parent)
 
     setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
     setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+
+    m_zoomAnimation = new QPropertyAnimation(this, "viewScale");
+    connect(m_zoomAnimation, SIGNAL(finished()), this, SIGNAL(viewZoomFinished()));
 }
 
 void MapView::setZoomLevel(int zoomLevel)
 {
-    m_zoomTargetScale = pow(2, zoomLevel - MAX_MAP_ZOOM_LEVEL);
-    m_zoomScaleDelta = (m_zoomTargetScale - currentScale()) / (ZOOM_FPS * (ZOOM_TIME / 1000));
-
-    if (m_timerID)
-        killTimer(m_timerID);
-
-    m_timerID = startTimer(1000/ZOOM_FPS);
+//    m_zoomTargetScale = pow(2, zoomLevel - MAX_MAP_ZOOM_LEVEL);
+//    m_zoomScaleDelta = (m_zoomTargetScale - currentScale()) / (ZOOM_FPS * (ZOOM_TIME / 1000));
+//
+//    if (m_timerID)
+//        killTimer(m_timerID);
+//
+//    m_timerID = startTimer(1000/ZOOM_FPS);
+
+    m_zoomAnimation->stop();
+    m_zoomAnimation->setDuration(ZOOM_TIME);
+    m_zoomAnimation->setStartValue(viewScale());
+    m_zoomAnimation->setEndValue(pow(2, zoomLevel - MAX_MAP_ZOOM_LEVEL));
+
+    m_zoomAnimation->start();
 }
 
 void MapView::timerEvent(QTimerEvent *event)
@@ -87,7 +97,7 @@ void MapView::timerEvent(QTimerEvent *event)
        emit viewContentChanged(mapToScene(viewport()->x(), viewport()->y()).toPoint());
 
         if (finished && m_zoomScaleDelta > 0)
-            emit viewZoomInFinished();
+            emit viewZoomFinished();
     }
 }
 
@@ -97,6 +107,20 @@ qreal MapView::currentScale()
     return currentTransform.m11();
 }
 
+qreal MapView::viewScale()
+{
+    QTransform currentTransform = transform();
+    return currentTransform.m11();
+}
+
+void MapView::setViewScale(qreal viewScale)
+{
+    QTransform transform;
+    transform.scale(viewScale, viewScale);
+    setTransform(transform);
+    emit viewContentChanged(mapToScene(viewport()->x(), viewport()->y()).toPoint());
+}
+
 void MapView::mouseMoveEvent(QMouseEvent *event)
 {
     m_scenePosition += m_mousePosition - mapToScene(event->pos()).toPoint();
index 7f13f52..bf61cba 100644 (file)
@@ -23,6 +23,7 @@
 #define MAPVIEW_H
 
 #include <QGraphicsView>
+#include <QPropertyAnimation>
 
 /**
 * @brief Map view widget
@@ -33,6 +34,8 @@ class MapView : public QGraphicsView
 {
     Q_OBJECT
 
+    Q_PROPERTY(qreal viewScale READ viewScale WRITE setViewScale)
+
 public:
     /**
     * @brief Constructor
@@ -104,6 +107,10 @@ private:
     */
     qreal currentScale();
 
+    void setViewScale(qreal viewScale);
+
+    qreal viewScale();
+
 /*******************************************************************************
  * SIGNALS
  ******************************************************************************/
@@ -125,10 +132,10 @@ signals:
     void viewScrolled(QPoint sceneCoordinate);
 
     /**
-    * @brief Signal for informing that zooming in is finished
+    * @brief Signal for informing that zooming animation is finished
     *
     */
-    void viewZoomInFinished();
+    void viewZoomFinished();
 
     /**
     * @brief Signal for updating view content
@@ -141,10 +148,11 @@ signals:
 /*******************************************************************************
  * DATA MEMBERS
  ******************************************************************************/
-private:
+private:           
     QPoint m_mousePosition; ///< Previous mouse event position
     QPoint m_scenePosition; ///< New center position
     int m_timerID; ///< ID number of the timer used for smooth zoom effect
+    QPropertyAnimation *m_zoomAnimation;
     qreal m_zoomScaleDelta; ///< Scaling factor delta for smooth zoom transition effect
     qreal m_zoomTargetScale; ///< Scaling factor of the target zoom level
 };
index de749a8..6a3e4a8 100644 (file)
@@ -39,7 +39,7 @@ MapViewScreen::MapViewScreen(QWidget *parent)
     connect(mapView, SIGNAL(viewResized(QSize)), mapEngine, SLOT(viewResized(QSize)));
     connect(mapView, SIGNAL(viewContentChanged(QPoint)),
             mapEngine, SLOT(alignImmovableItems(QPoint)));
-    connect(mapView, SIGNAL(viewZoomInFinished()), mapEngine, SLOT(viewZoomInFinished()));
+    connect(mapView, SIGNAL(viewZoomFinished()), mapEngine, SLOT(viewZoomFinished()));
 
     QHBoxLayout *mapViewLayout = new QHBoxLayout;
     //DEBUG