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,
27 #include <QStringList>
30 #include <QHashIterator>
33 #include "mapengine.h"
36 MapEngine::MapEngine(QObject *parent)
38 , m_zoomLevel(DEFAULT_ZOOM_LEVEL)
39 , m_viewSize(QSize(DEFAULT_SCREEN_WIDTH, DEFAULT_SCREEN_HEIGHT))
40 , m_centerTile(QPoint(UNDEFINED, UNDEFINED))
42 m_mapScene = new MapScene(this);
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)));
50 void MapEngine::init()
52 emit zoomLevelChanged(m_zoomLevel);
53 setViewLocation(QPointF(DEFAULT_LONGITUDE, DEFAULT_LATITUDE));
56 void MapEngine::setViewLocation(QPointF latLonCoordinate)
58 qDebug() << __PRETTY_FUNCTION__;
59 setLocation(convertLatLonToSceneCoordinate(latLonCoordinate));
62 QUrl MapEngine::buildURL(int zoomLevel, QPoint tileNumbers)
64 QString url = QString("http://tile.openstreetmap.org/mapnik/%1/%2/%3.png")
65 .arg(zoomLevel).arg(tileNumbers.x()).arg(tileNumbers.y());
70 void MapEngine::parseURL(const QUrl &url, int &zoom, int &x, int &y)
72 QString path = url.path();
73 QStringList pathParts = path.split("/", QString::SkipEmptyParts);
75 int size = pathParts.size();
80 zoom = (pathParts.at(size-3)).toInt();
81 x = (pathParts.at(size-2)).toInt();
82 QString yString = pathParts.at(size-1);
87 void MapEngine::mapImageReceived(const QUrl &url, const QPixmap &pixmap)
89 //qDebug() << __PRETTY_FUNCTION__;
94 parseURL(url, zoom, x, y);
96 if (!m_mapTilesInScene.contains(tilePath(zoom, x, y))) {
98 MapTile *mapTile = new MapTile();
99 mapTile->setZoomLevel(zoom);
100 mapTile->setTileNumber(QPoint(x, y));
101 mapTile->setPixmap(pixmap);
103 m_mapTilesInScene.insert(tilePath(zoom, x, y), mapTile);
104 m_mapScene->addMapTile(mapTile);
106 removeStackedTiles(mapTile);
110 QGraphicsScene* MapEngine::scene()
112 return dynamic_cast<QGraphicsScene *>(m_mapScene);
115 int MapEngine::tileMaxValue(int zoomLevel)
117 return (1 << zoomLevel) - 1;
120 QRect MapEngine::calculateGrid(QPoint sceneCoordinate)
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);
128 return QRect(topLeftX, topLeftY, gridWidth, gridHeight);
131 void MapEngine::setLocation(QPoint sceneCoordinate)
133 //qDebug() << __PRETTY_FUNCTION__;
135 m_sceneCoordinate = sceneCoordinate;
136 emit locationChanged(m_sceneCoordinate);
138 if (centerTileChanged(sceneCoordinate)) {
139 calculateNewTiles(sceneCoordinate);
144 bool MapEngine::centerTileChanged(QPoint sceneCoordinate)
146 QPoint centerTile = convertSceneCoordinateToTileNumber(m_zoomLevel, sceneCoordinate);
147 QPoint temp = m_centerTile;
148 m_centerTile = centerTile;
150 return (centerTile != temp);
153 void MapEngine::calculateNewTiles(QPoint sceneCoordinate)
155 //qDebug() << __PRETTY_FUNCTION__;
157 m_viewGrid = calculateGrid(sceneCoordinate);
159 int topLeftX = m_viewGrid.topLeft().x();
160 int topLeftY = m_viewGrid.topLeft().y();
161 int bottomRightX = m_viewGrid.bottomRight().x();
162 int bottomRightY = m_viewGrid.bottomRight().y();
164 int tileMaxVal = tileMaxValue(m_zoomLevel);
166 for (int x = topLeftX; x <= bottomRightX; ++x) {
167 for (int y = topLeftY; y <= bottomRightY; ++y) {
174 else if (tileX > tileMaxVal)
179 else if (tileY > tileMaxVal)
182 QUrl url = buildURL(m_zoomLevel, QPoint(tileX, tileY));
184 if (!m_mapTilesInScene.contains(tilePath(m_zoomLevel, tileX, tileY)))
185 emit fetchImage(url);
190 void MapEngine::removeTile(MapTile *tile)
192 //qDebug() << __PRETTY_FUNCTION__;
195 m_mapTilesInScene.remove(tilePath(tile->zoomLevel(), tile->tileNumber().x(),
196 tile->tileNumber().y()));
197 m_mapScene->removeItem(tile);
202 void MapEngine::removeOldTiles()
204 //qDebug() << __PRETTY_FUNCTION__;
206 QPointF topLeft = convertTileNumberToSceneCoordinate(m_zoomLevel, m_viewGrid.topLeft());
207 QPointF bottomRight = convertTileNumberToSceneCoordinate(m_zoomLevel, m_viewGrid.bottomRight()
209 qreal width = bottomRight.x() - topLeft.x();
210 qreal height = bottomRight.y() - topLeft.y();
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 while (i.hasNext()) {
223 if (allTiles.contains(i.value()) && m_mapTilesInScene.contains(i.key())) {
224 MapTile *tile = i.value();
229 // qDebug() << m_mapScene->items().count();
232 void MapEngine::removeStackedTiles(MapTile *newTile)
234 //qDebug() << __PRETTY_FUNCTION__;
236 QRectF newTileSceneRect = newTile->mapRectToScene(newTile->boundingRect());
237 QList<QGraphicsItem *> collidingItems = newTile->collidingItems(Qt::IntersectsItemBoundingRect);
239 //Loop all items under new tile
240 foreach (QGraphicsItem *collidingItem, collidingItems) {
242 QRectF collidingItemSceneRect = collidingItem->sceneBoundingRect();
244 //If new tile covers the tile under, remove the tile (zoom out)
245 if (newTileSceneRect.contains(collidingItemSceneRect)) {
246 MapTile *tile = dynamic_cast<MapTile *>(collidingItem);
251 //Get tiles below removal candidate
252 QList<QGraphicsItem *> stackedItems = m_mapScene->items(collidingItemSceneRect,
253 Qt::ContainsItemBoundingRect);
257 //Loop all tiles below removal candidate and combine tiles
258 foreach (QGraphicsItem *stackedItem, stackedItems) {
259 if (stackedItem != collidingItem) {
261 QRectF stackedItemSceneRect = stackedItem->sceneBoundingRect();
262 combined = combined.united(stackedItemSceneRect);
266 //If combined tiles below removal candidate covers removal candidate, remove it (zoom in)
267 if ((combined.contains(collidingItemSceneRect)) && (count >= 4)) {
268 MapTile *tile = dynamic_cast<MapTile *>(collidingItem);
274 // qDebug() << m_mapScene->items().count();
277 void MapEngine::viewResized(const QSize &size)
280 calculateNewTiles(m_sceneCoordinate);
284 void MapEngine::zoomIn()
286 qDebug() << __PRETTY_FUNCTION__;
288 if (m_zoomLevel >= MAX_MAP_ZOOM_LEVEL)
292 emit zoomLevelChanged(m_zoomLevel);
294 setTilesDrawingLevels();
296 calculateNewTiles(m_sceneCoordinate);
299 * @todo Remove old tiles after zoom
301 QTimer::singleShot(500, this, SLOT(removeOldTiles()));
304 void MapEngine::zoomOut()
306 qDebug() << __PRETTY_FUNCTION__;
308 if (m_zoomLevel <= MIN_MAP_ZOOM_LEVEL)
312 emit zoomLevelChanged(m_zoomLevel);
314 setTilesDrawingLevels();
316 calculateNewTiles(m_sceneCoordinate);
319 void MapEngine::setTilesDrawingLevels()
321 //qDebug() << __PRETTY_FUNCTION__ << "m_zoomLevel:" << m_zoomLevel;
323 QList<QGraphicsItem *> items = m_mapScene->items();
325 for (int i = 0; i < items.size(); ++i) {
326 MapTile *item = dynamic_cast<MapTile *>(items.at(i));
328 item->setSceneLevel(m_zoomLevel);
333 QString MapEngine::tilePath(int zoomLevel, int x, int y)
335 QString tilePathString(QString::number(zoomLevel) + "/");
336 tilePathString.append(QString::number(x) + "/");
337 tilePathString.append(QString::number(y));
339 return tilePathString;
342 void MapEngine::scalingFactorChanged(qreal scaleFactor)
344 qDebug() << __PRETTY_FUNCTION__;
347 QPoint MapEngine::convertSceneCoordinateToTileNumber(int zoomLevel, QPoint sceneCoordinate)
349 int pow = 1 << (MAX_MAP_ZOOM_LEVEL - zoomLevel);
350 int x = static_cast<int>(sceneCoordinate.x() / (TILE_SIZE_X*pow));
351 int y = static_cast<int>(sceneCoordinate.y() / (TILE_SIZE_Y*pow));
356 QPoint MapEngine::convertTileNumberToSceneCoordinate(int zoomLevel, QPoint tileNumber)
358 int pow = 1 << (MAX_MAP_ZOOM_LEVEL - zoomLevel);
359 int x = tileNumber.x() * TILE_SIZE_X * pow;
360 int y = tileNumber.y() * TILE_SIZE_Y * pow;
365 QPoint MapEngine::convertLatLonToSceneCoordinate(QPointF latLonCoordinate)
367 qDebug() << __PRETTY_FUNCTION__;
369 qreal longitude = latLonCoordinate.x();
370 qreal latitude = latLonCoordinate.y();
372 if ((longitude > MAX_LONGITUDE) || (longitude < MIN_LONGITUDE))
373 return QPoint(UNDEFINED, UNDEFINED);
374 if ((latitude > MAX_LATITUDE) || (latitude < MIN_LATITUDE))
375 return QPoint(UNDEFINED, UNDEFINED);
377 qreal z = static_cast<qreal>(1 << MAX_MAP_ZOOM_LEVEL);
379 qreal x = static_cast<qreal>((longitude + 180.0) / 360.0);
380 qreal y = static_cast<qreal>((1.0 - log(tan(latitude * M_PI / 180.0) + 1.0
381 / cos(latitude * M_PI / 180.0)) / M_PI) / 2.0);
383 return QPointF(x*z*TILE_SIZE_X, y*z*TILE_SIZE_Y).toPoint();