Modified MapEngine::removeStackedTiles.
[situare] / src / map / mapengine.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        Jussi Laitinen - jussi.laitinen@ixonos.com
7
8    Situare is free software; you can redistribute it and/or
9    modify it under the terms of the GNU General Public License
10    version 2 as published by the Free Software Foundation.
11
12    Situare is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with Situare; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
20    USA.
21 */
22
23 #include <QtCore>
24 #include <QtGlobal>
25 #include <QDebug>
26 #include <QString>
27 #include <QStringList>
28 #include <QUrl>
29 #include <QHash>
30 #include <QHashIterator>
31 #include <QRect>
32
33 #include "mapengine.h"
34 #include "maptile.h"
35
36 MapEngine::MapEngine(QObject *parent)
37     : QObject(parent)
38     , m_zoomLevel(DEFAULT_ZOOM_LEVEL)
39     , m_viewSize(QSize(DEFAULT_SCREEN_WIDTH, DEFAULT_SCREEN_HEIGHT))
40     , m_centerTile(QPoint(UNDEFINED, UNDEFINED))
41 {
42     m_mapScene = new MapScene(this);
43
44     m_mapFetcher = new MapFetcher(new QNetworkAccessManager(this), this);
45     connect(this, SIGNAL(fetchImage(QUrl)), m_mapFetcher, SLOT(fetchMapImage(QUrl)));
46     connect(m_mapFetcher, SIGNAL(mapImageReceived(QUrl,QPixmap)), this,
47             SLOT(mapImageReceived(QUrl, QPixmap)));
48 }
49
50 void MapEngine::init()
51 {
52     emit zoomLevelChanged(m_zoomLevel);
53     setViewLocation(QPointF(DEFAULT_LONGITUDE, DEFAULT_LATITUDE));
54 }
55
56 void MapEngine::setViewLocation(QPointF latLonCoordinate)
57 {
58     qDebug() << __PRETTY_FUNCTION__;
59     setLocation(convertLatLonToSceneCoordinate(latLonCoordinate));
60 }
61
62 QUrl MapEngine::buildURL(int zoomLevel, QPoint tileNumbers)
63 {
64     QString url = QString("http://tile.openstreetmap.org/mapnik/%1/%2/%3.png")
65                   .arg(zoomLevel).arg(tileNumbers.x()).arg(tileNumbers.y());
66
67     return QUrl(url);
68 }
69
70 void MapEngine::parseURL(const QUrl &url, int &zoom, int &x, int &y)
71 {
72     QString path = url.path();
73     QStringList pathParts = path.split("/", QString::SkipEmptyParts);
74
75     int size = pathParts.size();
76
77     if (size < 3)
78         return;
79
80     zoom = (pathParts.at(size-3)).toInt();
81     x = (pathParts.at(size-2)).toInt();
82     QString yString = pathParts.at(size-1);
83     yString.chop(4);
84     y = yString.toInt();
85 }
86
87 void MapEngine::mapImageReceived(const QUrl &url, const QPixmap &pixmap)
88 {
89     //qDebug() << __PRETTY_FUNCTION__;
90
91     if (!mapTilesInScene.contains(url.toString())) {
92         int zoom = UNDEFINED;
93         int x = UNDEFINED;
94         int y = UNDEFINED;
95
96         parseURL(url, zoom, x, y);
97
98         MapTile *mapTile = new MapTile();
99         mapTile->setZoomLevel(zoom);
100         mapTile->setTileNumber(QPoint(x, y));
101         mapTile->setPixmap(pixmap);
102
103         mapTilesInScene.insert(tilePath(zoom, x, y), mapTile);
104         m_mapScene->addMapTile(mapTile);
105
106         removeStackedTiles(mapTile);
107    }
108 }
109
110 QGraphicsScene* MapEngine::scene()
111 {
112     return dynamic_cast<QGraphicsScene *>(m_mapScene);
113 }
114
115 int MapEngine::tileMaxValue(int zoomLevel)
116 {
117     return (1 << zoomLevel) - 1;
118 }
119
120 QRect MapEngine::calculateGrid(QPoint sceneCoordinate)
121 {
122     QPoint tileCoordinate = convertSceneCoordinateToTileNumber(m_zoomLevel, sceneCoordinate);
123     int gridWidth = (m_viewSize.width()/TILE_SIZE_X + 1) + (GRID_PADDING*2);
124     int gridHeight = (m_viewSize.height()/TILE_SIZE_Y + 1) + (GRID_PADDING*2);
125     int topLeftX = tileCoordinate.x() - (gridWidth/2);
126     int topLeftY = tileCoordinate.y() - (gridHeight/2);
127
128     return QRect(topLeftX, topLeftY, gridWidth, gridHeight);
129 }
130
131 void MapEngine::setLocation(QPoint sceneCoordinate)
132 {
133     //qDebug() << __PRETTY_FUNCTION__;
134
135     m_sceneCoordinate = sceneCoordinate;
136     emit locationChanged(m_sceneCoordinate);
137
138     if (centerTileChanged(sceneCoordinate)) {
139         calculateNewTiles(sceneCoordinate);
140         removeOldTiles();
141     }
142 }
143
144 bool MapEngine::centerTileChanged(QPoint sceneCoordinate)
145 {
146     QPoint centerTile = convertSceneCoordinateToTileNumber(m_zoomLevel, sceneCoordinate);
147     QPoint temp = m_centerTile;
148     m_centerTile = centerTile;
149
150     return (centerTile != temp);
151 }
152
153 void MapEngine::calculateNewTiles(QPoint sceneCoordinate)
154 {
155     //qDebug() << __PRETTY_FUNCTION__;
156
157     viewGrid = calculateGrid(sceneCoordinate);
158
159     int topLeftX = viewGrid.topLeft().x();
160     int topLeftY = viewGrid.topLeft().y();
161     int bottomRightX = viewGrid.bottomRight().x();
162     int bottomRightY = viewGrid.bottomRight().y();
163
164     int tileMaxVal = tileMaxValue(m_zoomLevel);
165
166     for (int x = topLeftX; x <= bottomRightX; ++x) {
167         for (int y = topLeftY; y <= bottomRightY; ++y) {
168
169             int tileX = x;
170             int tileY = y;
171
172             if (tileX < 0)
173                 tileX += tileMaxVal;
174             else if (tileX > tileMaxVal)
175                 tileX -= tileMaxVal;
176
177             if (tileY < 0)
178                 tileY += tileMaxVal;
179             else if (tileY > tileMaxVal)
180                 tileY -= tileMaxVal;
181
182             QUrl url = buildURL(m_zoomLevel, QPoint(tileX, tileY));
183
184             if (!mapTilesInScene.contains(tilePath(m_zoomLevel, tileX, tileY)))
185                 emit fetchImage(url);
186         }
187     }
188 }
189
190 void MapEngine::removeOldTiles()
191 {
192     //qDebug() << __PRETTY_FUNCTION__;
193
194     QPointF topLeft = convertTileNumberToSceneCoordinate(m_zoomLevel, viewGrid.topLeft());
195     QPointF bottomRight = convertTileNumberToSceneCoordinate(m_zoomLevel, viewGrid.bottomRight() + QPoint(1, 1));
196     qreal width = bottomRight.x() - topLeft.x();
197     qreal height = bottomRight.y() - topLeft.y();
198
199     QList<QGraphicsItem *> viewTiles = m_mapScene->items(topLeft.x(), topLeft.y(), width, height, Qt::IntersectsItemShape);
200     QList<QGraphicsItem *> allTiles = m_mapScene->items();
201
202     foreach (QGraphicsItem *tile, viewTiles)
203         allTiles.removeOne(tile);
204
205     QHashIterator<QString, MapTile *> i(mapTilesInScene);
206
207      while (i.hasNext()) {
208          i.next();
209          if (allTiles.contains(i.value()) && mapTilesInScene.contains(i.key())) {
210              MapTile *tile = i.value();
211              if (tile) {
212                 mapTilesInScene.remove(i.key());
213                 m_mapScene->removeItem(tile);
214                 delete tile;
215              }
216          }
217      }
218 }
219
220 void MapEngine::removeStackedTiles(MapTile *newTile)
221 {
222     QRectF newTileSceneRect = newTile->mapRectToScene(newTile->boundingRect());
223
224     QList<QGraphicsItem *> collidingItems = newTile->collidingItems(Qt::IntersectsItemBoundingRect);
225
226     foreach (QGraphicsItem *collidingItem, collidingItems) {
227
228         QRectF collidingItemSceneRect = collidingItem->mapRectToScene(collidingItem->boundingRect());
229
230         if (collidingItem->isObscured(collidingItemSceneRect)) {
231             qDebug() << "Hep";
232         }
233
234     }
235 //    qDebug() << "mapTilesInScene: " << mapTilesInScene.count();
236 //    qDebug() << "m_mapScene: " << m_mapScene->items().count();
237 }
238
239 void MapEngine::viewResized(const QSize &size)
240 {
241     m_viewSize = size;
242     calculateNewTiles(m_sceneCoordinate);
243     removeOldTiles();
244 }
245
246 void MapEngine::zoomIn()
247 {
248     qDebug() << __PRETTY_FUNCTION__;
249
250     if (m_zoomLevel >= MAX_MAP_ZOOM_LEVEL)
251         return;
252
253     m_zoomLevel++;
254     emit zoomLevelChanged(m_zoomLevel);
255
256     setZValues();
257
258     calculateNewTiles(m_sceneCoordinate);
259     removeOldTiles();
260 }
261
262 void MapEngine::zoomOut()
263 {
264     qDebug() << __PRETTY_FUNCTION__;
265
266     if (m_zoomLevel <= MIN_MAP_ZOOM_LEVEL)
267         return;
268
269     m_zoomLevel--;
270     emit zoomLevelChanged(m_zoomLevel);
271
272     setZValues();
273
274     calculateNewTiles(m_sceneCoordinate);
275 }
276
277 void MapEngine::setZValues()
278 {
279     //qDebug() << __PRETTY_FUNCTION__ << "m_zoomLevel:" << m_zoomLevel;
280
281     QList<QGraphicsItem *> items = m_mapScene->items();
282
283     for (int i = 0; i < items.size(); ++i) {
284         MapTile *item = dynamic_cast<MapTile *>(items.at(i));
285         if (item)
286             item->setSceneLevel(m_zoomLevel);
287     }
288
289 }
290
291 QString MapEngine::tilePath(int zoomLevel, int x, int y)
292 {
293     QString tilePathString(QString::number(zoomLevel) + "/");
294     tilePathString.append(QString::number(x) + "/");
295     tilePathString.append(QString::number(y));
296
297     return tilePathString;
298 }