2 Situare - A location system for Facebook
3 Copyright (C) 2010 Ixonos Plc. Authors:
5 Sami Rämö - sami.ramo@ixonos.com
7 Situare is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 version 2 as published by the Free Software Foundation.
11 Situare is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with Situare; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
24 #include "mapcommon.h"
25 #include "mapengine.h"
30 const int MAP_MIN_PIXEL_Y = 0;
31 const int MAP_MAX_PIXEL_Y = MAX_TILES_PER_SIDE * TILE_SIZE_Y - 1;
33 MapScene::MapScene(QObject *parent)
34 : QGraphicsScene(parent)
35 , m_isRemoveStackedTilesRunning(false)
36 , m_tilesSceneRect(QRect(0, 0, 0, 0))
38 qDebug() << __PRETTY_FUNCTION__;
40 setBackgroundBrush(Qt::lightGray);
41 setSceneRect(MAP_SCENE_MIN_PIXEL_X, MAP_MIN_PIXEL_Y, MAP_SCENE_MAX_PIXEL_X, MAP_MAX_PIXEL_Y);
44 void MapScene::addTile(int tileZoomLevel, QPoint tileNumber, const QPixmap &image, int viewZoomLevel)
46 qDebug() << __PRETTY_FUNCTION__;
48 // tile might already be in the scene if:
49 // - expired tile was returned from the cache to be temporarily displayed while downloading
51 // - upper level tile is not cleaned after zooming in and back to out (not scrolled enough)
52 // - upper level tile was earlier returned from cache while downloading the requested tile
53 // and user has zoomed up to that level
54 QString hashKey = MapEngine::tilePath(tileZoomLevel, tileNumber.x(), tileNumber.y());
55 MapTile *oldTile = tileInScene(hashKey);
59 MapTile *tile = new MapTile();
60 tile->setZoomLevel(tileZoomLevel, viewZoomLevel);
61 tile->setTileNumber(tileNumber);
62 tile->setPixmap(image);
64 m_mapTilesInScene.insert(hashKey, tile);
67 enqueueRemoveStackedTiles(tile);
70 void MapScene::enqueueRemoveStackedTiles(MapTile *newTile)
72 qDebug() << __PRETTY_FUNCTION__;
74 m_removeStackedTilesList << newTile;
75 if (!m_isRemoveStackedTilesRunning) {
76 m_isRemoveStackedTilesRunning = true;
77 QTimer::singleShot(0, this, SLOT(runNextStackedTilesRemoval()));
81 void MapScene::moveIntersectingItemsHorizontally(QRect from, int distance)
83 qDebug() << __PRETTY_FUNCTION__;
85 QList<QGraphicsItem *> spanItems = items(from, Qt::IntersectsItemBoundingRect);
86 foreach (QGraphicsItem *item, spanItems) {
87 if (dynamic_cast<MapTile *>(item))
89 item->moveBy(distance, 0);
93 MapTile* MapScene::tileInScene(QString hashKey)
95 qDebug() << __PRETTY_FUNCTION__;
97 return m_mapTilesInScene.value(hashKey, 0);
100 void MapScene::runNextStackedTilesRemoval()
102 qDebug() << __PRETTY_FUNCTION__;
104 if (!m_removeStackedTilesList.isEmpty()) {
105 MapTile *tile = m_removeStackedTilesList.takeFirst();
106 removeStackedTiles(tile);
109 // schedule removal of the next tile if the list is not empty
110 if (!m_removeStackedTilesList.isEmpty())
111 QTimer::singleShot(0, this, SLOT(runNextStackedTilesRemoval()));
113 m_isRemoveStackedTilesRunning = false;
116 void MapScene::removeOutOfViewTiles(QRect tilesGrid, int zoomLevel)
118 qDebug() << __PRETTY_FUNCTION__;
120 QList<QGraphicsItem *> viewTiles = items(m_tilesSceneRect, Qt::IntersectsItemBoundingRect);
121 QList<QGraphicsItem *> allTiles = items();
123 //Remove tiles which are in view from allTiles
124 foreach (QGraphicsItem *tile, viewTiles)
125 allTiles.removeOne(tile);
127 // note: add 1 so odd values are rounded up
128 int tilesGridWidthHalf = (tilesGrid.width() + 1) / 2;
130 if (tilesGrid.right() > (MapEngine::tileMaxValue(zoomLevel) - tilesGridWidthHalf + GRID_PADDING)) {
131 QRect oppositeRect = m_tilesSceneRect;
132 oppositeRect.translate(-MAP_PIXELS_X, 0);
133 QList<QGraphicsItem *> oppositeTiles = items(oppositeRect, Qt::IntersectsItemBoundingRect);
134 foreach (QGraphicsItem *tile, oppositeTiles)
135 allTiles.removeOne(tile);
138 if (tilesGrid.left() < (tilesGridWidthHalf - GRID_PADDING)) {
139 QRect oppositeRect = m_tilesSceneRect;
140 oppositeRect.translate(MAP_PIXELS_X, 0);
141 QList<QGraphicsItem *> oppositeTiles = items(oppositeRect, Qt::IntersectsItemBoundingRect);
142 foreach (QGraphicsItem *tile, oppositeTiles)
143 allTiles.removeOne(tile);
146 //Remove tiles out of view
147 foreach (QGraphicsItem *tile, allTiles) {
148 MapTile *tileToRemove = dynamic_cast<MapTile *>(tile);
150 removeTile(tileToRemove);
153 qWarning() << __PRETTY_FUNCTION__ << "items in scene:" << items().count();
156 void MapScene::removeStackedTiles(MapTile *newTile)
158 qDebug() << __PRETTY_FUNCTION__;
160 QRectF newTileSceneRect = newTile->sceneBoundingRect();
162 //Loop all items under new tile
163 QList<QGraphicsItem *> collidingItems = newTile->collidingItems(Qt::IntersectsItemBoundingRect);
164 foreach (QGraphicsItem *collidingItem, collidingItems) {
165 MapTile *collidingTile = dynamic_cast<MapTile *>(collidingItem);
167 if (newTile->zValue() > collidingTile->zValue()) {
168 // remove tile if it is fully obscured by new tile
169 QRectF collidingTileSceneRect = collidingTile->sceneBoundingRect();
170 if (newTileSceneRect.contains(collidingTileSceneRect))
171 removeTile(collidingTile);
175 qWarning() << __PRETTY_FUNCTION__ << "items in scene:" << items().count();
178 void MapScene::removeTile(MapTile *tile)
180 qDebug() << __PRETTY_FUNCTION__;
182 m_mapTilesInScene.remove(MapEngine::tilePath(tile->zoomLevel(),
183 tile->tileNumber().x(),
184 tile->tileNumber().y()));
186 m_removeStackedTilesList.removeAll(tile);
190 void MapScene::setSceneVerticalOverlap(int viewHeight, int zoomLevel)
192 qDebug() << __PRETTY_FUNCTION__;
194 int overlap = viewHeight / 2 * (1 << (MAX_MAP_ZOOM_LEVEL - zoomLevel));
196 QRect rect = sceneRect().toRect();
197 rect.setTop(MAP_MIN_PIXEL_Y - overlap);
198 rect.setBottom(MAP_MAX_PIXEL_Y + overlap);
202 void MapScene::setTilesDrawingLevels(int zoomLevel)
204 qDebug() << __PRETTY_FUNCTION__ << "zoomLevel:" << zoomLevel;
206 QList<QGraphicsItem *> allItems = items();
208 for (int i = 0; i < allItems.size(); ++i) {
209 MapTile *item = dynamic_cast<MapTile *>(allItems.at(i));
211 item->setSceneLevel(zoomLevel);
215 void MapScene::spanItems(int zoomLevel, QPoint sceneCoordinate, QSize viewSize)
217 qDebug() << __PRETTY_FUNCTION__;
219 // create rects for left and right side
220 QRect leftRect = sceneRect().toRect(); // this way we get the horizontal limits of the scene
221 leftRect.setTop(MAP_MIN_PIXEL_Y);
222 leftRect.setBottom(MAP_MAX_PIXEL_Y);
223 QRect rightRect = leftRect;
225 // calculate current horizontal area shown on the view
226 int viewSceneWidth = (1 << (MAX_MAP_ZOOM_LEVEL - zoomLevel)) * viewSize.width();
227 int viewSceneLeft = sceneCoordinate.x() - viewSceneWidth / 2;
228 int viewSceneRight = sceneCoordinate.x() + viewSceneWidth / 2;
230 // limit rects to include only area which really must be moved
231 leftRect.setRight(-1 - (MAP_PIXELS_X - 1 - viewSceneRight));
232 rightRect.setLeft(MAP_PIXELS_X + viewSceneLeft);
234 Q_ASSERT_X(leftRect.right() < viewSceneLeft, "spanning rect right value", "move rect is in the view area");
235 Q_ASSERT_X(rightRect.left() > viewSceneRight, "spanning rect left value", "move rect is in the view area");
237 // move all items which intersects the rects
238 if (leftRect.left() < leftRect.right())
239 moveIntersectingItemsHorizontally(leftRect, MAP_PIXELS_X);
240 if (rightRect.left() < rightRect.right())
241 moveIntersectingItemsHorizontally(rightRect, -MAP_PIXELS_X);
244 void MapScene::tilesSceneRectUpdated(QRect tilesSceneRect)
246 qDebug() << __PRETTY_FUNCTION__;
248 m_tilesSceneRect = tilesSceneRect;