Added indicatorbuttonpanel.cpp and indicatorbuttonpanel.h
[situare] / src / map / mapscene.cpp
1 /*
2    Situare - A location system for Facebook
3    Copyright (C) 2010  Ixonos Plc. Authors:
4
5        Sami Rämö - sami.ramo@ixonos.com
6
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.
10
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.
15
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,
19    USA.
20 */
21
22 #include <QDebug>
23
24 #include "mapcommon.h"
25 #include "mapengine.h"
26 #include "maptile.h"
27
28 #include "mapscene.h"
29
30 MapScene::MapScene(QObject *parent)
31     : QGraphicsScene(parent)
32     , m_isRemoveStackedTilesRunning(false)
33     , m_zoomLevel(0)
34     , m_tilesSceneRect(QRect(0, 0, 0, 0))
35     , m_viewTilesGrid(QRect(0, 0, 0, 0))
36 {
37     qDebug() << __PRETTY_FUNCTION__;
38
39     setBackgroundBrush(Qt::lightGray);
40     setSceneRect(QRect(QPoint(MAP_SCENE_MIN_PIXEL_X, OSM_MAP_MIN_PIXEL_Y),
41                        QPoint(MAP_SCENE_MAX_PIXEL_X, OSM_MAP_MAX_PIXEL_Y)));
42 }
43
44 void MapScene::addTile(int tileZoomLevel, QPoint tileNumber, const QPixmap &image, int viewZoomLevel)
45 {
46     qDebug() << __PRETTY_FUNCTION__;
47
48     // tile might already be in the scene if expired tile was returned from the cache to be
49     // temporarily shown while downloading the fresh one.
50     QString hashKey = MapEngine::tilePath(tileZoomLevel, tileNumber.x(), tileNumber.y());
51     MapTile *oldTile = tileInScene(hashKey);
52     if (oldTile)
53         removeTile(oldTile);
54
55     MapTile *tile = new MapTile();
56     tile->setZoomLevel(tileZoomLevel, viewZoomLevel);
57     tile->setTileNumber(tileNumber);
58     tile->setPixmap(image);
59
60     m_mapTilesInScene.insert(hashKey, tile);
61     addItem(tile);
62
63     qDebug() << __PRETTY_FUNCTION__ << "tiles count:" << m_mapTilesInScene.count();
64
65     enqueueRemoveStackedTiles(tile);
66     removeOtherLevelTiles();
67 }
68
69 void MapScene::enqueueRemoveStackedTiles(MapTile *newTile)
70 {
71     qDebug() << __PRETTY_FUNCTION__;
72
73     m_removeStackedTilesList << newTile;
74     if (!m_isRemoveStackedTilesRunning) {
75         m_isRemoveStackedTilesRunning = true;
76         QTimer::singleShot(0, this, SLOT(runNextStackedTilesRemoval()));
77     }
78 }
79
80 void MapScene::moveIntersectingItemsHorizontally(QRect from, int distance)
81 {
82     qDebug() << __PRETTY_FUNCTION__;
83
84     QList<QGraphicsItem *> spanItems = items(from, Qt::IntersectsItemBoundingRect);
85     foreach (QGraphicsItem *item, spanItems) {
86         if (!dynamic_cast<MapTile *>(item))
87             item->moveBy(distance, 0);
88     }
89 }
90
91 MapTile* MapScene::tileInScene(QString hashKey)
92 {
93     qDebug() << __PRETTY_FUNCTION__;
94
95     return m_mapTilesInScene.value(hashKey, 0);
96 }
97
98 void MapScene::runNextStackedTilesRemoval()
99 {
100     qDebug() << __PRETTY_FUNCTION__;
101
102     if (!m_removeStackedTilesList.isEmpty()) {
103         MapTile *tile = m_removeStackedTilesList.takeFirst();
104         removeStackedTiles(tile);
105     }
106
107     // schedule removal of the next tile if the list is not empty
108     if (!m_removeStackedTilesList.isEmpty())
109         QTimer::singleShot(0, this, SLOT(runNextStackedTilesRemoval()));
110     else
111         m_isRemoveStackedTilesRunning = false;
112 }
113
114 void MapScene::removeOtherLevelTiles()
115 {
116     qDebug() << __PRETTY_FUNCTION__;
117
118     for (int x = m_viewTilesGrid.left(); x <= m_viewTilesGrid.right(); x++) {
119         for (int y = m_viewTilesGrid.top(); y <= m_viewTilesGrid.bottom(); y++) {
120             if (!m_mapTilesInScene.contains(MapEngine::tilePath(m_zoomLevel, x, y)))
121                 return;
122         }
123     }
124
125     foreach(MapTile *tile, m_mapTilesInScene) {
126         if (tile->zoomLevel() != m_zoomLevel) {
127             removeTile(tile);
128             qDebug() << __PRETTY_FUNCTION__ << "removed other level tile";
129         }
130     }
131 }
132
133 void MapScene::removeOutOfViewTiles(QRect tilesGrid, int zoomLevel)
134 {
135     qDebug() << __PRETTY_FUNCTION__;
136
137     QList<QGraphicsItem *> viewItems = items(m_tilesSceneRect, Qt::IntersectsItemBoundingRect);
138     QList<QGraphicsItem *> allItems = items();
139
140     //Remove tiles which are in view from allTiles
141     foreach (QGraphicsItem *item, viewItems)
142         allItems.removeOne(item);
143
144     // note: add 1 so odd values are rounded up
145     int tilesGridWidthHalf = (tilesGrid.width() + 1) / 2;
146
147     // if view is near east limit of the map, then there is duplicate tiles also on the opposite
148     // side of the world which are removed from allTiles
149     if (tilesGrid.right() > (MapEngine::tileMaxIndex(zoomLevel) - tilesGridWidthHalf + MAP_GRID_PADDING)) {
150         QRect oppositeRect = m_tilesSceneRect;
151         oppositeRect.translate(-OSM_MAP_PIXELS_X, 0);
152         QList<QGraphicsItem *> oppositeItems = items(oppositeRect, Qt::IntersectsItemBoundingRect);
153         foreach (QGraphicsItem *item, oppositeItems)
154             allItems.removeOne(item);
155     }
156
157     // if view is near west limit of the map, then there is duplicate tiles also on the opposite
158     // side of the world which are removed from allTiles
159     if (tilesGrid.left() < (tilesGridWidthHalf - MAP_GRID_PADDING)) {
160         QRect oppositeRect = m_tilesSceneRect;
161         oppositeRect.translate(OSM_MAP_PIXELS_X, 0);
162         QList<QGraphicsItem *> oppositeItems = items(oppositeRect, Qt::IntersectsItemBoundingRect);
163         foreach (QGraphicsItem *item, oppositeItems)
164             allItems.removeOne(item);
165     }
166
167     //Remove tiles out of view
168     foreach (QGraphicsItem *item, allItems) {
169         MapTile *tile = dynamic_cast<MapTile *>(item);
170         if (tile)
171             removeTile(tile);
172     }
173 }
174
175 void MapScene::removeStackedTiles(MapTile *newTile)
176 {
177     qDebug() << __PRETTY_FUNCTION__;
178
179     QRectF newTileSceneRect = newTile->sceneBoundingRect();
180
181     //Loop all items under new tile
182     QList<QGraphicsItem *> collidingItems = newTile->collidingItems(Qt::IntersectsItemBoundingRect);
183     foreach (QGraphicsItem *collidingItem, collidingItems) {
184         MapTile *collidingTile = dynamic_cast<MapTile *>(collidingItem);
185         if (collidingTile) {
186             if (newTile->zValue() > collidingTile->zValue()) {
187                 // remove tile if it is fully obscured by new tile
188                 QRectF collidingTileSceneRect = collidingTile->sceneBoundingRect();
189                 if (newTileSceneRect.contains(collidingTileSceneRect))
190                     removeTile(collidingTile);
191             }
192         }
193     }
194 }
195
196 void MapScene::removeTile(MapTile *tile)
197 {
198     qDebug() << __PRETTY_FUNCTION__;
199
200     m_mapTilesInScene.remove(MapEngine::tilePath(tile->zoomLevel(),
201                                                  tile->tileNumber().x(),
202                                                  tile->tileNumber().y()));
203     removeItem(tile);
204     m_removeStackedTilesList.removeAll(tile);
205     delete tile;
206
207     qDebug() << __PRETTY_FUNCTION__ << "tiles count:" << m_mapTilesInScene.count();
208 }
209
210 void MapScene::setSceneVerticalOverlap(int viewHeight, int zoomLevel)
211 {
212     qDebug() << __PRETTY_FUNCTION__;
213
214     int overlap = viewHeight / 2 * (1 << (OSM_MAX_ZOOM_LEVEL - zoomLevel));
215
216     QRect rect = sceneRect().toRect();
217     rect.setTop(OSM_MAP_MIN_PIXEL_Y - overlap);
218     rect.setBottom(OSM_MAP_MAX_PIXEL_Y + overlap);
219     setSceneRect(rect);
220 }
221
222 void MapScene::setTilesDrawingLevels(int zoomLevel)
223 {
224     qDebug() << __PRETTY_FUNCTION__ << "zoomLevel:" << zoomLevel;
225
226     QList<QGraphicsItem *> allItems = items();
227
228     for (int i = 0; i < allItems.size(); ++i) {
229         MapTile *item = dynamic_cast<MapTile *>(allItems.at(i));
230         if (item)
231             item->setSceneLevel(zoomLevel);
232     }
233 }
234
235 void MapScene::setTilesGrid(QRect grid)
236 {
237     qDebug() << __PRETTY_FUNCTION__ ;
238
239     m_viewTilesGrid = grid;
240 }
241
242 void MapScene::setZoomLevel(int zoomLevel)
243 {
244     qDebug() << __PRETTY_FUNCTION__ ;
245
246     m_zoomLevel = zoomLevel;
247 }
248
249 void MapScene::spanItems(QRectF viewSceneRect)
250 {
251     qDebug() << __PRETTY_FUNCTION__;
252
253     // create rects for left and right side
254     QRect leftRect = sceneRect().toRect(); // this way we get the horizontal limits of the scene
255     leftRect.setTop(OSM_MAP_MIN_PIXEL_Y);
256     leftRect.setBottom(OSM_MAP_MAX_PIXEL_Y);
257     QRect rightRect = leftRect;
258
259     // limit rects to include only area which really must be moved
260     leftRect.setRight(-1 - (OSM_MAP_PIXELS_X - 1 - viewSceneRect.right()));
261     rightRect.setLeft(OSM_MAP_PIXELS_X + viewSceneRect.left());
262
263     Q_ASSERT_X(leftRect.right() < viewSceneRect.left(), "spanning rect right value", "move rect is in the view area");
264     Q_ASSERT_X(rightRect.left() > viewSceneRect.right(), "spanning rect left value", "move rect is in the view area");
265
266     // move all items which intersects the rects
267     if (leftRect.left() < leftRect.right())
268         moveIntersectingItemsHorizontally(leftRect, OSM_MAP_PIXELS_X);
269     if (rightRect.left() < rightRect.right())
270         moveIntersectingItemsHorizontally(rightRect, -OSM_MAP_PIXELS_X);
271 }
272
273 void MapScene::tilesSceneRectUpdated(QRect tilesSceneRect)
274 {
275     qDebug() << __PRETTY_FUNCTION__;
276
277     m_tilesSceneRect = tilesSceneRect;
278 }