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 MapScene::MapScene(QObject *parent)
31 : QGraphicsScene(parent)
32 , m_isRemoveStackedTilesRunning(false)
33 , m_tilesSceneRect(QRect(0, 0, 0, 0))
35 qDebug() << __PRETTY_FUNCTION__;
37 setBackgroundBrush(Qt::lightGray);
38 setSceneRect(QRect(QPoint(MAP_SCENE_MIN_PIXEL_X, OSM_MAP_MIN_PIXEL_Y),
39 QPoint(MAP_SCENE_MAX_PIXEL_X, OSM_MAP_MAX_PIXEL_Y)));
42 void MapScene::addTile(int tileZoomLevel, QPoint tileNumber, const QPixmap &image, int viewZoomLevel)
44 qDebug() << __PRETTY_FUNCTION__;
46 // tile might already be in the scene if expired tile was returned from the cache to be
47 // temporarily shown while downloading the fresh one.
48 QString hashKey = MapEngine::tilePath(tileZoomLevel, tileNumber.x(), tileNumber.y());
49 MapTile *oldTile = tileInScene(hashKey);
53 MapTile *tile = new MapTile();
54 tile->setZoomLevel(tileZoomLevel, viewZoomLevel);
55 tile->setTileNumber(tileNumber);
56 tile->setPixmap(image);
58 m_mapTilesInScene.insert(hashKey, tile);
61 qDebug() << __PRETTY_FUNCTION__ << "tiles count:" << m_mapTilesInScene.count();
63 enqueueRemoveStackedTiles(tile);
64 removeOtherLevelTiles();
67 void MapScene::enqueueRemoveStackedTiles(MapTile *newTile)
69 qDebug() << __PRETTY_FUNCTION__;
71 m_removeStackedTilesList << newTile;
72 if (!m_isRemoveStackedTilesRunning) {
73 m_isRemoveStackedTilesRunning = true;
74 QTimer::singleShot(0, this, SLOT(runNextStackedTilesRemoval()));
78 void MapScene::moveIntersectingItemsHorizontally(QRect from, int distance)
80 qDebug() << __PRETTY_FUNCTION__;
82 QList<QGraphicsItem *> spanItems = items(from, Qt::IntersectsItemBoundingRect);
83 foreach (QGraphicsItem *item, spanItems) {
84 if (!dynamic_cast<MapTile *>(item))
85 item->moveBy(distance, 0);
89 MapTile* MapScene::tileInScene(QString hashKey)
91 qDebug() << __PRETTY_FUNCTION__;
93 return m_mapTilesInScene.value(hashKey, 0);
96 void MapScene::runNextStackedTilesRemoval()
98 qDebug() << __PRETTY_FUNCTION__;
100 if (!m_removeStackedTilesList.isEmpty()) {
101 MapTile *tile = m_removeStackedTilesList.takeFirst();
102 removeStackedTiles(tile);
105 // schedule removal of the next tile if the list is not empty
106 if (!m_removeStackedTilesList.isEmpty())
107 QTimer::singleShot(0, this, SLOT(runNextStackedTilesRemoval()));
109 m_isRemoveStackedTilesRunning = false;
112 void MapScene::removeOtherLevelTiles()
114 qDebug() << __PRETTY_FUNCTION__;
116 for (int x = m_viewTilesGrid.left(); x <= m_viewTilesGrid.right(); x++) {
117 for (int y = m_viewTilesGrid.top(); y <= m_viewTilesGrid.bottom(); y++) {
118 if (!m_mapTilesInScene.contains(MapEngine::tilePath(m_zoomLevel, x, y)))
123 foreach(MapTile *tile, m_mapTilesInScene) {
124 if (tile->zoomLevel() != m_zoomLevel) {
126 qDebug() << __PRETTY_FUNCTION__ << "removed other level tile";
131 void MapScene::removeOutOfViewTiles(QRect tilesGrid, int zoomLevel)
133 qDebug() << __PRETTY_FUNCTION__;
135 QList<QGraphicsItem *> viewItems = items(m_tilesSceneRect, Qt::IntersectsItemBoundingRect);
136 QList<QGraphicsItem *> allItems = items();
138 //Remove tiles which are in view from allTiles
139 foreach (QGraphicsItem *item, viewItems)
140 allItems.removeOne(item);
142 // note: add 1 so odd values are rounded up
143 int tilesGridWidthHalf = (tilesGrid.width() + 1) / 2;
145 // if view is near east limit of the map, then there is duplicate tiles also on the opposite
146 // side of the world which are removed from allTiles
147 if (tilesGrid.right() > (MapEngine::tileMaxIndex(zoomLevel) - tilesGridWidthHalf + MAP_GRID_PADDING)) {
148 QRect oppositeRect = m_tilesSceneRect;
149 oppositeRect.translate(-OMS_MAP_PIXELS_X, 0);
150 QList<QGraphicsItem *> oppositeItems = items(oppositeRect, Qt::IntersectsItemBoundingRect);
151 foreach (QGraphicsItem *item, oppositeItems)
152 allItems.removeOne(item);
155 // if view is near west limit of the map, then there is duplicate tiles also on the opposite
156 // side of the world which are removed from allTiles
157 if (tilesGrid.left() < (tilesGridWidthHalf - MAP_GRID_PADDING)) {
158 QRect oppositeRect = m_tilesSceneRect;
159 oppositeRect.translate(OMS_MAP_PIXELS_X, 0);
160 QList<QGraphicsItem *> oppositeItems = items(oppositeRect, Qt::IntersectsItemBoundingRect);
161 foreach (QGraphicsItem *item, oppositeItems)
162 allItems.removeOne(item);
165 //Remove tiles out of view
166 foreach (QGraphicsItem *item, allItems) {
167 MapTile *tile = dynamic_cast<MapTile *>(item);
173 void MapScene::removeStackedTiles(MapTile *newTile)
175 qDebug() << __PRETTY_FUNCTION__;
177 QRectF newTileSceneRect = newTile->sceneBoundingRect();
179 //Loop all items under new tile
180 QList<QGraphicsItem *> collidingItems = newTile->collidingItems(Qt::IntersectsItemBoundingRect);
181 foreach (QGraphicsItem *collidingItem, collidingItems) {
182 MapTile *collidingTile = dynamic_cast<MapTile *>(collidingItem);
184 if (newTile->zValue() > collidingTile->zValue()) {
185 // remove tile if it is fully obscured by new tile
186 QRectF collidingTileSceneRect = collidingTile->sceneBoundingRect();
187 if (newTileSceneRect.contains(collidingTileSceneRect))
188 removeTile(collidingTile);
194 void MapScene::removeTile(MapTile *tile)
196 qDebug() << __PRETTY_FUNCTION__;
198 m_mapTilesInScene.remove(MapEngine::tilePath(tile->zoomLevel(),
199 tile->tileNumber().x(),
200 tile->tileNumber().y()));
202 m_removeStackedTilesList.removeAll(tile);
205 qDebug() << __PRETTY_FUNCTION__ << "tiles count:" << m_mapTilesInScene.count();
208 void MapScene::setSceneVerticalOverlap(int viewHeight, int zoomLevel)
210 qDebug() << __PRETTY_FUNCTION__;
212 int overlap = viewHeight / 2 * (1 << (OSM_MAX_ZOOM_LEVEL - zoomLevel));
214 QRect rect = sceneRect().toRect();
215 rect.setTop(OSM_MAP_MIN_PIXEL_Y - overlap);
216 rect.setBottom(OSM_MAP_MAX_PIXEL_Y + overlap);
220 void MapScene::setTilesDrawingLevels(int zoomLevel)
222 qDebug() << __PRETTY_FUNCTION__ << "zoomLevel:" << zoomLevel;
224 QList<QGraphicsItem *> allItems = items();
226 for (int i = 0; i < allItems.size(); ++i) {
227 MapTile *item = dynamic_cast<MapTile *>(allItems.at(i));
229 item->setSceneLevel(zoomLevel);
233 void MapScene::setTilesGrid(QRect grid)
235 qDebug() << __PRETTY_FUNCTION__ ;
237 m_viewTilesGrid = grid;
240 void MapScene::setZoomLevel(int zoomLevel)
242 qDebug() << __PRETTY_FUNCTION__ ;
244 m_zoomLevel = zoomLevel;
247 void MapScene::spanItems(int zoomLevel, SceneCoordinate sceneCoordinate, QSize viewSize)
249 qDebug() << __PRETTY_FUNCTION__;
251 // create rects for left and right side
252 QRect leftRect = sceneRect().toRect(); // this way we get the horizontal limits of the scene
253 leftRect.setTop(OSM_MAP_MIN_PIXEL_Y);
254 leftRect.setBottom(OSM_MAP_MAX_PIXEL_Y);
255 QRect rightRect = leftRect;
257 // calculate current horizontal area shown on the view
258 int viewSceneWidth = (1 << (OSM_MAX_ZOOM_LEVEL - zoomLevel)) * viewSize.width();
259 int viewSceneLeft = sceneCoordinate.x() - viewSceneWidth / 2;
260 int viewSceneRight = sceneCoordinate.x() + viewSceneWidth / 2;
262 // limit rects to include only area which really must be moved
263 leftRect.setRight(-1 - (OMS_MAP_PIXELS_X - 1 - viewSceneRight));
264 rightRect.setLeft(OMS_MAP_PIXELS_X + viewSceneLeft);
266 Q_ASSERT_X(leftRect.right() < viewSceneLeft, "spanning rect right value", "move rect is in the view area");
267 Q_ASSERT_X(rightRect.left() > viewSceneRight, "spanning rect left value", "move rect is in the view area");
269 // move all items which intersects the rects
270 if (leftRect.left() < leftRect.right())
271 moveIntersectingItemsHorizontally(leftRect, OMS_MAP_PIXELS_X);
272 if (rightRect.left() < rightRect.right())
273 moveIntersectingItemsHorizontally(rightRect, -OMS_MAP_PIXELS_X);
276 void MapScene::tilesSceneRectUpdated(QRect tilesSceneRect)
278 qDebug() << __PRETTY_FUNCTION__;
280 m_tilesSceneRect = tilesSceneRect;