Modified correct name to ZOOM const.
[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 <QDebug>
24 #include <QString>
25 #include <QStringList>
26 #include <QUrl>
27 #include <QHash>
28 #include <QHashIterator>
29 #include <QQueue>
30 #include <QRect>
31
32 #include "mapengine.h"
33 #include "maptile.h"
34
35 #include <QtCore>
36 #include <QtGlobal>
37
38 MapEngine::MapEngine(QObject *parent)
39     : QObject(parent)
40     , m_zoomLevel(14)
41     , m_viewSize(QSize(973, 614))
42     , m_centerTile(QPoint(-1, -1))
43 {
44     m_mapScene = new MapScene(this);
45
46     m_mapFetcher = new MapFetcher(new QNetworkAccessManager(this), this);
47     connect(this, SIGNAL(fetchImage(QUrl)), m_mapFetcher, SLOT(fetchMapImage(QUrl)));
48     connect(m_mapFetcher, SIGNAL(mapImageReceived(QUrl,QPixmap)), this,
49             SLOT(mapImageReceived(QUrl, QPixmap)));
50 }
51
52 void MapEngine::init()
53 {
54     emit zoomLevelChanged(m_zoomLevel);
55     setViewLocation(QPointF(25.5000, 65.0000));
56
57 //    // Fetch some map tiles for demo purposes
58 //    for (int x=9351; x<=9354; x++) {
59 //        for (int y=4261; y<=4264; y++) {
60 //            QUrl url = buildURL(m_zoomLevel, QPoint(x, y));
61 //            m_mapFetcher->fetchMapImage(url);
62 //        }
63 //    }
64 }
65
66 void MapEngine::setViewLocation(QPointF latLonCoordinate)
67 {
68     qDebug() << __PRETTY_FUNCTION__;
69     setLocation(convertLatLonToSceneCoordinate(latLonCoordinate));
70 }
71
72 QUrl MapEngine::buildURL(int zoomLevel, QPoint tileNumbers)
73 {
74     QString url = QString("http://tile.openstreetmap.org/mapnik/%1/%2/%3.png")
75                   .arg(zoomLevel).arg(tileNumbers.x()).arg(tileNumbers.y());
76
77     return QUrl(url);
78 }
79
80 void MapEngine::parseURL(const QUrl &url, int &zoom, int &x, int &y)
81 {
82     QString path = url.path();
83     QStringList pathParts = path.split("/", QString::SkipEmptyParts);
84
85     int size = pathParts.size();
86
87     if (size < 3)
88         return;
89
90     zoom = (pathParts.at(size-3)).toInt();
91     x = (pathParts.at(size-2)).toInt();
92     QString yString = pathParts.at(size-1);
93     yString.chop(4);
94     y = yString.toInt();
95 }
96
97 void MapEngine::mapImageReceived(const QUrl &url, const QPixmap &pixmap)
98 {
99     if (mapTilesInScene.contains(url.toString())) {
100         int zoom = -1;
101         int x = -1;
102         int y = -1;
103
104         parseURL(url, zoom, x, y);
105
106         MapTile *mapTile = new MapTile();
107         mapTile->setZoomLevel(zoom);
108         mapTile->setTileNumber(QPoint(x, y));
109         mapTile->setPixmap(pixmap);
110
111         mapTilesInScene.insert(url.toString(), mapTile);
112         m_mapScene->addMapTile(mapTile);
113     }
114 }
115
116 QGraphicsScene* MapEngine::scene()
117 {
118     return dynamic_cast<QGraphicsScene *>(m_mapScene);
119 }
120
121 int MapEngine::tileMaxValue(int zoomLevel)
122 {
123     return (1 << zoomLevel) - 1;
124 }
125
126 QRect MapEngine::calculateGrid(QPointF sceneCoordinate)
127 {
128     QPoint tileCoordinate = convertSceneCoordinateToTileNumber(m_zoomLevel, sceneCoordinate);
129
130     int gridWidth = (m_viewSize.width()/TILE_SIZE_X + 1) + (GRID_PADDING*2);
131     int gridHeight = (m_viewSize.height()/TILE_SIZE_Y + 1) + (GRID_PADDING*2);
132
133     int topLeftX = tileCoordinate.x() - (gridWidth/2);
134     int topLeftY = tileCoordinate.y() - (gridHeight/2);
135
136     return QRect(topLeftX, topLeftY, gridWidth, gridHeight);
137 }
138
139 void MapEngine::setLocation(QPointF sceneCoordinate)
140 {
141     m_sceneCoordinate = sceneCoordinate;
142     emit locationChanged(m_sceneCoordinate);
143
144     if (centerTileChanged(sceneCoordinate)) {
145         calculateTileGrid(sceneCoordinate);
146         //removeTilesOutOfBounds();
147     }
148 }
149
150 bool MapEngine::centerTileChanged(QPointF sceneCoordinate)
151 {
152     QPoint centerTile = convertSceneCoordinateToTileNumber(m_zoomLevel, sceneCoordinate);
153     QPoint temp = m_centerTile;
154     m_centerTile = centerTile;
155
156     return (centerTile != temp);
157 }
158
159 void MapEngine::calculateTileGrid(QPointF sceneCoordinate)
160 {
161     //qDebug() << __PRETTY_FUNCTION__;
162
163     viewGrid = calculateGrid(sceneCoordinate);
164
165     int topLeftX = viewGrid.topLeft().x();
166     int topLeftY = viewGrid.topLeft().y();
167     int bottomRightX = viewGrid.bottomRight().x();
168     int bottomRightY = viewGrid.bottomRight().y();
169
170     int tileMaxVal = tileMaxValue(m_zoomLevel);
171
172     for (int x = topLeftX; x <= bottomRightX; ++x) {
173         for (int y = topLeftY; y <= bottomRightY; ++y) {
174
175             int tileX = x;
176             int tileY = y;
177
178             if (tileX < 0)
179                 tileX += tileMaxVal;
180             else if (tileX > tileMaxVal)
181                 tileX -= tileMaxVal;
182
183             if (tileY < 0)
184                 tileY += tileMaxVal;
185             else if (tileY > tileMaxVal)
186                 tileY -= tileMaxVal;
187
188             QUrl url = buildURL(m_zoomLevel, QPoint(tileX, tileY));
189
190             if (!mapTilesInScene.contains(url.toString())) {
191                 mapTilesInScene.insert(url.toString(), NULL);
192                 emit fetchImage(url);
193             }
194         }
195     }
196 }
197
198 void MapEngine::removeTilesOutOfBounds()
199 {
200     //qDebug() << __PRETTY_FUNCTION__;
201
202     int zoomFactor = 1 << (MAX_MAP_ZOOM_LEVEL - m_zoomLevel);
203
204     QPointF topLeft = convertTileNumberToSceneCoordinate(m_zoomLevel, viewGrid.topLeft());
205     QPointF bottomRight = convertTileNumberToSceneCoordinate(m_zoomLevel, viewGrid.bottomRight() + QPoint(1, 1));
206     qreal width = bottomRight.x() - topLeft.x();
207     qreal height = bottomRight.y() - topLeft.y();
208
209     QList<QGraphicsItem *> viewTiles = m_mapScene->items(topLeft.x(), topLeft.y(), width, height, Qt::IntersectsItemShape);
210     QList<QGraphicsItem *> allTiles = m_mapScene->items();
211
212     foreach (QGraphicsItem *tile, viewTiles)
213         allTiles.removeOne(tile);
214
215     QHashIterator<QString, MapTile *> i(mapTilesInScene);
216
217      while (i.hasNext()) {
218          i.next();
219          if (allTiles.contains(i.value())) {
220              MapTile *tile = i.value();
221              if (tile) {
222                  mapTilesInScene.remove(i.key());
223                  m_mapScene->removeItem(tile);
224                  delete tile;
225              }
226          }
227      }
228
229 }
230
231 void MapEngine::viewResized(const QSize &size)
232 {
233     m_viewSize = size;
234     setLocation(m_sceneCoordinate);
235 }
236
237 void MapEngine::zoomIn()
238 {
239     if (m_zoomLevel >= MAX_MAP_ZOOM_LEVEL)
240         return;
241
242     m_zoomLevel++;
243     emit zoomLevelChanged(m_zoomLevel);
244
245     setZValues();
246
247     /// @todo START FETCHING TILES
248     calculateTileGrid(m_sceneCoordinate);
249 }
250
251 void MapEngine::zoomOut()
252 {
253     if (m_zoomLevel <= MIN_MAP_ZOOM_LEVEL)
254         return;
255
256     m_zoomLevel--;
257     emit zoomLevelChanged(m_zoomLevel);
258
259     setZValues();
260
261     /// @todo START FETCHING TILES
262     calculateTileGrid(m_sceneCoordinate);
263 }
264
265 void MapEngine::setZoomLevel(int zoomLevel)
266 {
267     m_zoomLevel = zoomLevel;
268 }
269
270 int MapEngine::getZoomLevel()
271 {
272     return m_zoomLevel;
273 }
274
275 void MapEngine::setZValues()
276 {
277     //qDebug() << __PRETTY_FUNCTION__ << "m_zoomLevel:" << m_zoomLevel;
278
279     QList<QGraphicsItem *> items = m_mapScene->items();
280
281     for (int i = 0; i < items.size(); ++i) {
282         MapTile *item = dynamic_cast<MapTile *>(items.at(i));
283         if (item)
284             item->setSceneLevel(m_zoomLevel);
285     }
286
287 }