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,
26 #include "mapcommon.h"
27 #include "mapengine.h"
32 const int WORLD_PIXELS_Y = MAX_TILES_PER_SIDE * TILE_SIZE_Y;
34 MapScene::MapScene(QObject *parent)
35 : QGraphicsScene(parent)
36 , m_isRemoveStackedTilesRunning(false)
37 , m_tilesSceneRect(QRect(0, 0, 0, 0))
39 qDebug() << __PRETTY_FUNCTION__;
41 setBackgroundBrush(Qt::lightGray);
42 setSceneRect(-WORLD_PIXELS_X * 2, 0, WORLD_PIXELS_X * 5 - 1, WORLD_PIXELS_Y - 1);
45 void MapScene::addTile(int tileZoomLevel, QPoint tileNumber, const QPixmap &image, int viewZoomLevel)
47 qDebug() << __PRETTY_FUNCTION__;
49 // tile might already be in the scene if expired tile was returned from the cache to be
50 // temporarily displayed while downloading the fresh one. Tile can also be in the scene if the
51 // zoom level is low and world is spanning around, in which case the old tile removal is
52 // unnecessary, but this situation can't be recognised with information currently available
53 QString hashKey = MapEngine::tilePath(tileZoomLevel, tileNumber.x(), tileNumber.y());
54 MapTile *oldTile = tileInScene(hashKey);
58 MapTile *tile = new MapTile();
59 tile->setZoomLevel(tileZoomLevel, viewZoomLevel);
60 tile->setTileNumber(tileNumber);
61 tile->setPixmap(image);
63 m_mapTilesInScene.insertMulti(hashKey, tile);
66 enqueueRemoveStackedTiles(tile);
69 void MapScene::enqueueRemoveStackedTiles(MapTile *newTile)
71 qDebug() << __PRETTY_FUNCTION__;
73 m_removeStackedTilesList << newTile;
74 if (!m_isRemoveStackedTilesRunning) {
75 m_isRemoveStackedTilesRunning = true;
76 QTimer::singleShot(0, this, SLOT(runNextStackedTilesRemoval()));
80 void MapScene::moveIntersectingItemsHorizontally(QRect from, int distance)
82 qDebug() << __PRETTY_FUNCTION__;
84 qWarning() << __PRETTY_FUNCTION__ << "left:" << from.left() << "right:" << from.right() << "distance:" << distance;
86 QList<QGraphicsItem *> spanItems = items(from, Qt::IntersectsItemBoundingRect);
87 foreach (QGraphicsItem *item, spanItems) {
88 if (dynamic_cast<MapTile *>(item))
90 qWarning() << __PRETTY_FUNCTION__ << "moving item...";
91 item->moveBy(distance, 0);
95 MapTile* MapScene::tileInScene(QString hashKey)
97 qDebug() << __PRETTY_FUNCTION__;
99 return m_mapTilesInScene.value(hashKey, 0);
102 void MapScene::runNextStackedTilesRemoval()
104 qDebug() << __PRETTY_FUNCTION__;
106 if (!m_removeStackedTilesList.isEmpty()) {
107 MapTile *tile = m_removeStackedTilesList.takeFirst();
108 removeStackedTiles(tile);
111 // schedule removal of the next tile if the list is not empty
112 if (!m_removeStackedTilesList.isEmpty())
113 QTimer::singleShot(0, this, SLOT(runNextStackedTilesRemoval()));
115 m_isRemoveStackedTilesRunning = false;
118 void MapScene::removeOutOfViewTiles(QRect tilesGrid, int zoomLevel)
120 qDebug() << __PRETTY_FUNCTION__;
122 // qWarning() << __PRETTY_FUNCTION__ << "m_tilesSceneRect:" << m_tilesSceneRect.left() << m_tilesSceneRect.top() << "/" << m_tilesSceneRect.right() << m_tilesSceneRect.bottom();
124 QList<QGraphicsItem *> viewTiles = items(m_tilesSceneRect, Qt::IntersectsItemBoundingRect);
125 QList<QGraphicsItem *> allTiles = items();
127 //Remove tiles which are in view from allTiles
128 foreach (QGraphicsItem *tile, viewTiles)
129 allTiles.removeOne(tile);
131 int tilesGridWidthHalf = (tilesGrid.width() + 1) / 2;
132 // qWarning() << __PRETTY_FUNCTION__ << "half:" << tilesGridWidthHalf;
134 if (tilesGrid.right() > (MapEngine::tileMaxValue(zoomLevel) - tilesGridWidthHalf + GRID_PADDING)) { /// @todo must be mirrored also when near the world limit
135 QRect oppositeRect = m_tilesSceneRect;
136 oppositeRect.translate(-WORLD_PIXELS_X, 0);
137 // qWarning() << __PRETTY_FUNCTION__ << "oppositeRect:" << oppositeRect.left() << oppositeRect.top() << "/" << oppositeRect.right() << oppositeRect.bottom();
138 QList<QGraphicsItem *> oppositeTiles = items(oppositeRect, Qt::IntersectsItemBoundingRect);
139 foreach (QGraphicsItem *tile, oppositeTiles)
140 allTiles.removeOne(tile);
143 if (tilesGrid.left() < (tilesGridWidthHalf - GRID_PADDING)) { /// @todo must be mirrored also when near the world limit
144 QRect oppositeRect = m_tilesSceneRect;
145 oppositeRect.translate(WORLD_PIXELS_X, 0);
146 QList<QGraphicsItem *> oppositeTiles = items(oppositeRect, Qt::IntersectsItemBoundingRect);
147 foreach (QGraphicsItem *tile, oppositeTiles)
148 allTiles.removeOne(tile);
151 //Remove tiles out of view
152 foreach (QGraphicsItem *tile, allTiles) {
153 MapTile *tileToRemove = dynamic_cast<MapTile *>(tile);
155 // qWarning() << __PRETTY_FUNCTION__ << "removing tile, x:" << tileToRemove->tileNumber().x() << "y:" << tileToRemove->tileNumber().y() << "pos:" << tileToRemove->pos().x() << tileToRemove->pos().y();
156 removeTile(tileToRemove);
161 void MapScene::removeStackedTiles(MapTile *newTile)
163 qDebug() << __PRETTY_FUNCTION__;
165 QRectF newTileSceneRect = newTile->sceneBoundingRect();
167 //Loop all items under new tile
168 QList<QGraphicsItem *> collidingItems = newTile->collidingItems(Qt::IntersectsItemBoundingRect);
169 foreach (QGraphicsItem *collidingItem, collidingItems) {
170 MapTile *collidingTile = dynamic_cast<MapTile *>(collidingItem);
172 if (newTile->zValue() > collidingTile->zValue()) {
173 // remove tile if it is fully obscured by new tile
174 QRectF collidingTileSceneRect = collidingTile->sceneBoundingRect();
175 if (newTileSceneRect.contains(collidingTileSceneRect))
176 removeTile(collidingTile);
182 void MapScene::removeTile(MapTile *tile)
184 qDebug() << __PRETTY_FUNCTION__;
186 m_mapTilesInScene.remove(MapEngine::tilePath(tile->zoomLevel(),
187 tile->tileNumber().x(),
188 tile->tileNumber().y()));
190 m_removeStackedTilesList.removeAll(tile);
194 void MapScene::setSceneVerticalOverlap(int viewHeight, int zoomLevel)
196 qDebug() << __PRETTY_FUNCTION__;
198 int overlap = viewHeight / 2 * (1 << (MAX_MAP_ZOOM_LEVEL - zoomLevel));
200 QRect rect = sceneRect().toRect();
201 rect.setTop(-overlap);
202 rect.setBottom(WORLD_PIXELS_Y + overlap - 1);
205 // qWarning() << __PRETTY_FUNCTION__ << "scene rect:" << rect.left() << rect.top() << rect.right() << rect.bottom();
208 void MapScene::setTilesDrawingLevels(int zoomLevel)
210 qDebug() << __PRETTY_FUNCTION__ << "zoomLevel:" << zoomLevel;
212 QList<QGraphicsItem *> allItems = items();
214 for (int i = 0; i < allItems.size(); ++i) {
215 MapTile *item = dynamic_cast<MapTile *>(allItems.at(i));
217 item->setSceneLevel(zoomLevel);
221 void MapScene::spanItems(ScrollDirection direction, int zoomLevel, QPoint sceneCoordinate, QSize viewSize)
223 qWarning() << __PRETTY_FUNCTION__;
225 // create rects for left and right side
226 QRect leftRect = sceneRect().toRect(); // this way we get the horizontal limits of the scene
228 leftRect.setBottom(WORLD_PIXELS_Y);
229 QRect rightRect = leftRect;
231 // calculate current horizontal area shown on the view
232 int viewSceneWidth = (1 << (MAX_MAP_ZOOM_LEVEL - zoomLevel)) * viewSize.width();
233 int viewSceneLeft = sceneCoordinate.x() - viewSceneWidth / 2;
234 int viewSceneRight = sceneCoordinate.x() + viewSceneWidth / 2;
236 // limit rect widths to be out of the current view
237 leftRect.setRight(viewSceneLeft);
238 rightRect.setLeft(viewSceneRight);
240 // move all items which intersects the rects
241 if (leftRect.width() > 0)
242 moveIntersectingItemsHorizontally(leftRect, WORLD_PIXELS_X);
243 if (rightRect.width() > 0)
244 moveIntersectingItemsHorizontally(rightRect, -WORLD_PIXELS_X);
246 /// @todo get small rects from boths sides of the view
248 /// @todo get all intersecting items, check which end of the view is nearer and move there
251 // QRect spanRect; // = m_viewRect;
252 // spanRect.setTop(0);
253 // spanRect.setBottom(WORLD_PIXELS_Y);
256 // int viewSceneWidth = (1 << (MAX_MAP_ZOOM_LEVEL - zoomLevel)) * viewSize.width();
257 // int gap = WORLD_PIXELS_X - viewSceneWidth;
259 // qCritical() << __PRETTY_FUNCTION__ << "viewSceneWidth > WORLD_PIXELS_X";
261 // int limiterUnNormalized = sceneCoordinate.x() + ((viewSceneWidth + gap) / 2);
262 // int limiter = MapEngine::normalize(limiterUnNormalized, 0, WORLD_PIXELS_X -1);
264 // if (sceneCoordinate.x() > limiter) {
265 // spanRect.setRight(limiter);
266 // spanRect.setLeft(sceneRect().left());
267 // delta = WORLD_PIXELS_X;
268 // qWarning() << __PRETTY_FUNCTION__ << "-->";
271 // spanRect.setLeft(limiter);
272 // spanRect.setRight(sceneRect().right());
273 // delta = -WORLD_PIXELS_X;
274 // qWarning() << __PRETTY_FUNCTION__ << "<--";
277 // QTransform transform;
278 // qreal scale = pow(2, zoomLevel - MAX_MAP_ZOOM_LEVEL);
279 // transform.scale(scale, scale);
280 // QList<QGraphicsItem *> spanItems = items(spanRect, Qt::IntersectsItemBoundingRect,
281 // Qt::DescendingOrder, transform);
283 // foreach (QGraphicsItem *item, spanItems) {
284 // if (dynamic_cast<MapTile *>(item))
286 // item->moveBy(delta, 0);
292 void MapScene::tilesSceneRectUpdated(QRect tilesSceneRect)
294 qDebug() << __PRETTY_FUNCTION__;
296 m_tilesSceneRect = tilesSceneRect;