2 Situare - A location system for Facebook
3 Copyright (C) 2010 Ixonos Plc. Authors:
5 Sami Rämö - sami.ramo@ixonos.com
6 Jussi Laitinen - jussi.laitinen@ixonos.com
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.
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.
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,
25 #include <QStringList>
28 #include <QHashIterator>
31 #include "mapengine.h"
34 MapEngine::MapEngine(QObject *parent)
36 , m_centerTile(QPoint(UNDEFINED, UNDEFINED))
37 , m_viewSize(QSize(DEFAULT_SCREEN_WIDTH, DEFAULT_SCREEN_HEIGHT))
38 , m_zoomLevel(DEFAULT_ZOOM_LEVEL)
40 m_mapScene = new MapScene(this);
42 m_mapFetcher = new MapFetcher(new QNetworkAccessManager(this), this);
43 connect(this, SIGNAL(fetchImage(QUrl)), m_mapFetcher, SLOT(fetchMapImage(QUrl)));
44 connect(m_mapFetcher, SIGNAL(mapImageReceived(QUrl,QPixmap)), this,
45 SLOT(mapImageReceived(QUrl, QPixmap)));
48 void MapEngine::init()
50 emit zoomLevelChanged(m_zoomLevel);
51 setViewLocation(QPointF(DEFAULT_LONGITUDE, DEFAULT_LATITUDE));
54 void MapEngine::setViewLocation(QPointF latLonCoordinate)
56 qDebug() << __PRETTY_FUNCTION__;
57 setLocation(convertLatLonToSceneCoordinate(latLonCoordinate));
60 QUrl MapEngine::buildURL(int zoomLevel, QPoint tileNumbers)
62 QString url = QString("http://tile.openstreetmap.org/mapnik/%1/%2/%3.png")
63 .arg(zoomLevel).arg(tileNumbers.x()).arg(tileNumbers.y());
68 void MapEngine::parseURL(const QUrl &url, int &zoom, int &x, int &y)
70 QString path = url.path();
71 QStringList pathParts = path.split("/", QString::SkipEmptyParts);
73 int size = pathParts.size();
76 zoom = (pathParts.at(size-3)).toInt();
77 x = (pathParts.at(size-2)).toInt();
78 QString yString = pathParts.at(size-1);
84 void MapEngine::mapImageReceived(const QUrl &url, const QPixmap &pixmap)
86 //qDebug() << __PRETTY_FUNCTION__;
92 parseURL(url, zoom, x, y);
93 QString hashKey = tilePath(zoom, x, y);
94 if (!m_mapTilesInScene.contains(hashKey)) {
96 MapTile *mapTile = new MapTile();
97 mapTile->setZoomLevel(zoom);
98 mapTile->setTileNumber(QPoint(x, y));
99 mapTile->setPixmap(pixmap);
101 m_mapTilesInScene.insert(hashKey, mapTile);
102 m_mapScene->addMapTile(mapTile);
104 removeStackedTiles(mapTile);
108 QGraphicsScene* MapEngine::scene()
110 return dynamic_cast<QGraphicsScene *>(m_mapScene);
113 int MapEngine::tileMaxValue(int zoomLevel)
115 return (1 << zoomLevel) - 1;
118 QRect MapEngine::calculateTileGrid(QPoint sceneCoordinate)
120 QPoint tileCoordinate = convertSceneCoordinateToTileNumber(m_zoomLevel, sceneCoordinate);
121 int gridWidth = (m_viewSize.width()/TILE_SIZE_X + 1) + (GRID_PADDING*2);
122 int gridHeight = (m_viewSize.height()/TILE_SIZE_Y + 1) + (GRID_PADDING*2);
123 int topLeftX = tileCoordinate.x() - (gridWidth/2);
124 int topLeftY = tileCoordinate.y() - (gridHeight/2);
126 return QRect(topLeftX, topLeftY, gridWidth, gridHeight);
129 void MapEngine::setLocation(QPoint sceneCoordinate)
131 //qDebug() << __PRETTY_FUNCTION__;
133 m_sceneCoordinate = sceneCoordinate;
134 emit locationChanged(m_sceneCoordinate);
136 if (isCenterTileChanged(sceneCoordinate)) {
137 getTiles(sceneCoordinate);
138 removeTilesOutOfView();
142 bool MapEngine::isCenterTileChanged(QPoint sceneCoordinate)
144 QPoint centerTile = convertSceneCoordinateToTileNumber(m_zoomLevel, sceneCoordinate);
145 QPoint temp = m_centerTile;
146 m_centerTile = centerTile;
148 return (centerTile != temp);
151 void MapEngine::getTiles(QPoint sceneCoordinate)
153 // qDebug() << __PRETTY_FUNCTION__;
155 m_viewGrid = calculateTileGrid(sceneCoordinate);
157 int topLeftX = m_viewGrid.topLeft().x();
158 int topLeftY = m_viewGrid.topLeft().y();
159 int bottomRightX = m_viewGrid.bottomRight().x();
160 int bottomRightY = m_viewGrid.bottomRight().y();
162 int tileMaxVal = tileMaxValue(m_zoomLevel);
164 for (int x = topLeftX; x <= bottomRightX; ++x) {
165 for (int y = topLeftY; y <= bottomRightY; ++y) {
172 else if (tileX > tileMaxVal)
177 else if (tileY > tileMaxVal)
180 QUrl url = buildURL(m_zoomLevel, QPoint(tileX, tileY));
182 if (!m_mapTilesInScene.contains(tilePath(m_zoomLevel, tileX, tileY)))
183 emit fetchImage(url);
188 void MapEngine::removeTile(MapTile *tile)
190 //qDebug() << __PRETTY_FUNCTION__;
193 m_mapTilesInScene.remove(tilePath(tile->zoomLevel(), tile->tileNumber().x(),
194 tile->tileNumber().y()));
195 m_mapScene->removeItem(tile);
200 void MapEngine::removeTilesOutOfView()
202 //qDebug() << __PRETTY_FUNCTION__;
204 /// @todo Jussi: convertTi.... returns QPoint
205 QPointF topLeft = convertTileNumberToSceneCoordinate(m_zoomLevel, m_viewGrid.topLeft());
206 QPointF bottomRight = convertTileNumberToSceneCoordinate(m_zoomLevel, m_viewGrid.bottomRight()
208 qreal width = bottomRight.x() - topLeft.x();
209 qreal height = bottomRight.y() - topLeft.y();
211 /// @todo Jussi: insert comments
212 QList<QGraphicsItem *> viewTiles = m_mapScene->items(topLeft.x(), topLeft.y(), width, height,
213 Qt::ContainsItemBoundingRect);
214 QList<QGraphicsItem *> allTiles = m_mapScene->items();
216 foreach (QGraphicsItem *tile, viewTiles)
217 allTiles.removeOne(tile);
219 QHashIterator<QString, MapTile *> i(m_mapTilesInScene);
221 /// @todo Jussi: loop through allTiles and remove from hash also
222 while (i.hasNext()) {
224 if (allTiles.contains(i.value())) {
225 MapTile *tile = i.value();
231 void MapEngine::removeStackedTiles(MapTile *newTile)
233 //qDebug() << __PRETTY_FUNCTION__;
235 /// @todo Jussi: use sceneBoundingRect DONE
236 // QRectF newTileSceneRect = newTile->mapRectToScene(newTile->boundingRect());
237 QRectF newTileSceneRect = newTile->sceneBoundingRect();
238 QList<QGraphicsItem *> collidingTiles = newTile->collidingItems(Qt::IntersectsItemBoundingRect);
240 //Loop all items under new tile
241 foreach (QGraphicsItem *collidingTile, collidingTiles) {
243 QRectF collidingTileSceneRect = collidingTile->sceneBoundingRect();
245 //If new tile covers the tile under, remove the tile
246 if (newTileSceneRect.contains(collidingTileSceneRect)) {
247 MapTile *tile = dynamic_cast<MapTile *>(collidingTile);
252 //Get tiles below removal candidate
253 QList<QGraphicsItem *> stackedTiles = m_mapScene->items(collidingTileSceneRect,
254 Qt::ContainsItemBoundingRect);
255 QRectF combined = combineTiles(collidingTile, stackedTiles);
257 //If combined tiles below removal candidate covers removal candidate, remove it
258 if (combined.contains(collidingTileSceneRect)) {
259 MapTile *tile = dynamic_cast<MapTile *>(collidingTile);
266 QRectF MapEngine::combineTiles(QGraphicsItem *parentTile,
267 const QList<QGraphicsItem*> &stackedTiles)
272 foreach (QGraphicsItem *stackedTile, stackedTiles) {
273 if (stackedTile != parentTile) {
275 QRectF stackedTileSceneRect = stackedTile->sceneBoundingRect();
276 combined = combined.united(stackedTileSceneRect);
286 void MapEngine::viewResized(const QSize &size)
289 getTiles(m_sceneCoordinate);
290 removeTilesOutOfView();
293 void MapEngine::zoomIn()
295 qDebug() << __PRETTY_FUNCTION__;
297 if (m_zoomLevel < MAX_MAP_ZOOM_LEVEL) {
299 emit zoomLevelChanged(m_zoomLevel);
301 setTilesDrawingLevels();
303 getTiles(m_sceneCoordinate);
305 // remove unused tiles after zooming is done
306 QTimer::singleShot(ZOOM_TIME, this, SLOT(removeTilesOutOfView()));
308 /// @todo DONE: Sami: return only to end
311 void MapEngine::zoomOut()
313 qDebug() << __PRETTY_FUNCTION__;
315 if (m_zoomLevel > MIN_MAP_ZOOM_LEVEL) {
317 emit zoomLevelChanged(m_zoomLevel);
319 setTilesDrawingLevels();
321 getTiles(m_sceneCoordinate);
323 /// @todo DONE: Sami: return
326 void MapEngine::setTilesDrawingLevels()
328 //qDebug() << __PRETTY_FUNCTION__ << "m_zoomLevel:" << m_zoomLevel;
330 QList<QGraphicsItem *> items = m_mapScene->items();
332 for (int i = 0; i < items.size(); ++i) {
333 MapTile *item = dynamic_cast<MapTile *>(items.at(i));
335 item->setSceneLevel(m_zoomLevel);
340 QString MapEngine::tilePath(int zoomLevel, int x, int y)
342 QString tilePathString(QString::number(zoomLevel) + "/");
343 tilePathString.append(QString::number(x) + "/");
344 tilePathString.append(QString::number(y));
346 return tilePathString;
349 void MapEngine::scalingFactorChanged(qreal scaleFactor)
351 qDebug() << __PRETTY_FUNCTION__;
354 QPoint MapEngine::convertSceneCoordinateToTileNumber(int zoomLevel, QPoint sceneCoordinate)
356 int pow = 1 << (MAX_MAP_ZOOM_LEVEL - zoomLevel);
357 int x = static_cast<int>(sceneCoordinate.x() / (TILE_SIZE_X*pow));
358 int y = static_cast<int>(sceneCoordinate.y() / (TILE_SIZE_Y*pow));
363 QPoint MapEngine::convertTileNumberToSceneCoordinate(int zoomLevel, QPoint tileNumber)
365 int pow = 1 << (MAX_MAP_ZOOM_LEVEL - zoomLevel);
366 int x = tileNumber.x() * TILE_SIZE_X * pow;
367 int y = tileNumber.y() * TILE_SIZE_Y * pow;
372 QPoint MapEngine::convertLatLonToSceneCoordinate(QPointF latLonCoordinate)
374 // qDebug() << __PRETTY_FUNCTION__;
376 qreal longitude = latLonCoordinate.x();
377 qreal latitude = latLonCoordinate.y();
379 if ((longitude > MAX_LONGITUDE) || (longitude < MIN_LONGITUDE))
380 return QPoint(UNDEFINED, UNDEFINED);
381 if ((latitude > MAX_LATITUDE) || (latitude < MIN_LATITUDE))
382 return QPoint(UNDEFINED, UNDEFINED);
384 qreal z = static_cast<qreal>(1 << MAX_MAP_ZOOM_LEVEL);
386 qreal x = static_cast<qreal>((longitude + 180.0) / 360.0);
387 qreal y = static_cast<qreal>((1.0 - log(tan(latitude * M_PI / 180.0) + 1.0
388 / cos(latitude * M_PI / 180.0)) / M_PI) / 2.0);
390 return QPointF(x*z*TILE_SIZE_X, y*z*TILE_SIZE_Y).toPoint();