Moved URL builder and parser to MapFetcher
[situare] / src / map / mapengine.cpp
index 949279c..1cddef3 100644 (file)
@@ -4,6 +4,7 @@
 
        Sami Rämö - sami.ramo@ixonos.com
        Jussi Laitinen - jussi.laitinen@ixonos.com
+       Pekka Nissinen - pekka.nissinen@ixonos.com
 
    Situare is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
 #include <QString>
 #include <QStringList>
 #include <QUrl>
+#include <QHash>
+#include <QHashIterator>
+#include <QRect>
 
 #include "mapengine.h"
 #include "maptile.h"
-//#include "mapscene.h"
 
-#include <QtCore>
-#include <QtGlobal>
-
-MapEngine::MapEngine(QWidget *parent)
+MapEngine::MapEngine(QObject *parent)
     : QObject(parent)
+    , m_centerTile(QPoint(UNDEFINED, UNDEFINED))
+    , m_viewSize(QSize(DEFAULT_SCREEN_WIDTH, DEFAULT_SCREEN_HEIGHT))
+    , m_zoomedIn(false)
+    , m_zoomLevel(DEFAULT_ZOOM_LEVEL)
 {
     m_mapScene = new MapScene(this);
-    m_zoomLevel = 14;
 
     m_mapFetcher = new MapFetcher(new QNetworkAccessManager(this), this);
-    connect(m_mapFetcher, SIGNAL(mapImageReceived(QUrl,QPixmap)), this,
-            SLOT(mapImageReceived(QUrl, QPixmap)));
+    connect(this, SIGNAL(fetchImage(int,int,int)), m_mapFetcher, SLOT(enqueueFetchMapImage(int,int,int)));
+    connect(m_mapFetcher, SIGNAL(mapImageReceived(int,int,int,QPixmap)), this,
+            SLOT(mapImageReceived(int,int,int,QPixmap)));
+
+    m_mapZoomPanel = new MapZoomPanel(NULL, MAP_ZOOM_PANEL_POSITION_X, MAP_ZOOM_PANEL_POSITION_Y);
+    m_mapScene->addItem(m_mapZoomPanel);
+    connect(m_mapZoomPanel, SIGNAL(zoomInPressed()), this, SLOT(zoomIn()));
+    connect(m_mapZoomPanel, SIGNAL(zoomOutPressed()), this, SLOT(zoomOut()));
+
+    m_ownLocation = new OwnLocationItem();
+    m_mapScene->addItem(m_ownLocation);
 }
 
-/**
-  * @todo DONE Create URL builder method
-  * QUrl buildURL(zoomLevel, QPoint tileNumbers)
-  */
-void MapEngine::setViewLocation(QPointF latLonCoordinate)
+void MapEngine::init()
 {
     emit zoomLevelChanged(m_zoomLevel);
+    setViewLocation(QPointF(DEFAULT_LONGITUDE, DEFAULT_LATITUDE));
+}
+
+void MapEngine::setViewLocation(QPointF latLonCoordinate)
+{
+    qDebug() << __PRETTY_FUNCTION__;
+    setLocation(convertLatLonToSceneCoordinate(latLonCoordinate));
+}
+
+void MapEngine::mapImageReceived(int zoomLevel, int x, int y, const QPixmap &image)
+{
+    qDebug() << __PRETTY_FUNCTION__;
+
+    QString hashKey = tilePath(zoomLevel, x, y);
+    if (!m_mapScene->isTileInScene(hashKey)) {
+
+        MapTile *mapTile = new MapTile();
+        mapTile->setZoomLevel(zoomLevel, m_zoomLevel);
+        mapTile->setTileNumber(QPoint(x, y));
+        mapTile->setPixmap(image);
+
+        m_mapScene->addTile(mapTile, hashKey);
+
+        m_mapScene->enqueueRemoveStackedTiles(mapTile);
+   }
+}
+
+QGraphicsScene* MapEngine::scene()
+{
+    return dynamic_cast<QGraphicsScene *>(m_mapScene);
+}
+
+int MapEngine::tileMaxValue(int zoomLevel)
+{
+    return (1 << zoomLevel) - 1;
+}
+
+QRect MapEngine::calculateTileGrid(QPoint sceneCoordinate)
+{
+    QPoint tileCoordinate = convertSceneCoordinateToTileNumber(m_zoomLevel, sceneCoordinate);
+    int gridWidth = (m_viewSize.width()/TILE_SIZE_X + 1) + (GRID_PADDING*2);
+    int gridHeight = (m_viewSize.height()/TILE_SIZE_Y + 1) + (GRID_PADDING*2);
+    int topLeftX = tileCoordinate.x() - (gridWidth/2);
+    int topLeftY = tileCoordinate.y() - (gridHeight/2);
+
+    m_mapFetcher->setDownloadQueueSize(gridWidth * gridHeight);
+
+    return QRect(topLeftX, topLeftY, gridWidth, gridHeight);
+}
+
+void MapEngine::alignImmovableItems(QPoint viewTopLeft)
+{
+    m_mapZoomPanel->setPos(viewTopLeft);
+
+    qDebug() << __PRETTY_FUNCTION__ << "viewTopLeft:" << viewTopLeft;
+}
+
+void MapEngine::setLocation(QPoint sceneCoordinate)
+{
+    qDebug() << __PRETTY_FUNCTION__;
+
+    m_sceneCoordinate = sceneCoordinate;
+    emit locationChanged(m_sceneCoordinate);
+
+    if (isCenterTileChanged(sceneCoordinate)) {
+        getTiles(sceneCoordinate);
+        m_mapScene->removeOutOfViewTiles();
+    }
+}
+
+bool MapEngine::isCenterTileChanged(QPoint sceneCoordinate)
+{
+    QPoint centerTile = convertSceneCoordinateToTileNumber(m_zoomLevel, sceneCoordinate);
+    QPoint temp = m_centerTile;
+    m_centerTile = centerTile;
+
+    return (centerTile != temp);
+}
 
-    /// Fetch some map tiles for demo purposes
-    for (int x=9351; x<=9354; x++) {
-        for (int y=4261; y<=4264; y++) {
-            QUrl url = buildURL(m_zoomLevel, QPoint(x, y));
-            m_mapFetcher->fetchMapImage(url);
+void MapEngine::getTiles(QPoint sceneCoordinate)
+{
+    qDebug() << __PRETTY_FUNCTION__;
+
+    m_viewTilesGrid = calculateTileGrid(sceneCoordinate);
+    updateViewTilesSceneRect();
+
+    int topLeftX = m_viewTilesGrid.topLeft().x();
+    int topLeftY = m_viewTilesGrid.topLeft().y();
+    int bottomRightX = m_viewTilesGrid.bottomRight().x();
+    int bottomRightY = m_viewTilesGrid.bottomRight().y();
+
+    int tileMaxVal = tileMaxValue(m_zoomLevel);
+
+    for (int x = topLeftX; x <= bottomRightX; ++x) {
+        for (int y = topLeftY; y <= bottomRightY; ++y) {
+
+            int tileX = x;
+            int tileY = y;
+
+            if (tileX < 0)
+                tileX += tileMaxVal;
+            else if (tileX > tileMaxVal)
+                tileX -= tileMaxVal;
+
+            if (tileY < 0)
+                tileY += tileMaxVal;
+            else if (tileY > tileMaxVal)
+                tileY -= tileMaxVal;
+
+            if (!m_mapScene->isTileInScene(tilePath(m_zoomLevel, tileX, tileY)))
+                emit fetchImage(m_zoomLevel, tileX, tileY);
         }
     }
 }
 
-QUrl MapEngine::buildURL(int zoomLevel, QPoint tileNumbers)
+void MapEngine::updateViewTilesSceneRect()
 {
-    QString url = QString("http://tile.openstreetmap.org/mapnik/%1/%2/%3.png")
-                  .arg(zoomLevel).arg(tileNumbers.x()).arg(tileNumbers.y());
+    QPoint topLeft = convertTileNumberToSceneCoordinate(m_zoomLevel, m_viewTilesGrid.topLeft());
+    QPoint bottomRight = convertTileNumberToSceneCoordinate(m_zoomLevel,
+                                                            m_viewTilesGrid.bottomRight()
+                                                             + QPoint(1, 1)) - QPoint(1, 1);
 
-    return QUrl(url);
+    m_mapScene->viewRectUpdated(QRect(topLeft, bottomRight));
 }
 
-/**
-* @todo Create URL parser methor and refactor code (use pointers for returning)
-*/
-void MapEngine::parseURL(const QUrl &url, int &zoom, int &x, int &y)
+void MapEngine::viewResized(const QSize &size)
 {
-    QString path = url.path();
-    QStringList pathParts = path.split("/", QString::SkipEmptyParts);
+    m_viewSize = size;
+    getTiles(m_sceneCoordinate);
+    m_mapScene->removeOutOfViewTiles();
+}
 
-    zoom = (pathParts.at(1)).toInt();
-    x = (pathParts.at(2)).toInt();
-    QString yString = pathParts.at(3);
-    yString.chop(4);
-    y = yString.toInt();
+void MapEngine::viewZoomFinished()
+{
+    qDebug() << __PRETTY_FUNCTION__;
 
+    if (m_zoomedIn) {
+        m_zoomedIn = false;
+        m_mapScene->removeOutOfViewTiles();
+    }
 }
 
-/**
-* @todo Create URL parser methor and refactor code (use pointers for returning)
-* DONE
-*/
-void MapEngine::mapImageReceived(const QUrl &url, const QPixmap &pixmap)
+void MapEngine::zoomIn()
 {
-    int zoom = -1;
-    int x = -1;
-    int y = -1;
+    qDebug() << __PRETTY_FUNCTION__;
 
-    parseURL(url, zoom, x, y);
+    if (m_zoomLevel < MAX_MAP_ZOOM_LEVEL) {
+        m_zoomLevel++;
+        m_zoomedIn = true;
+        emit zoomLevelChanged(m_zoomLevel);
 
-    MapTile *mapTile = new MapTile();
-    mapTile->setZoomLevel(zoom);
-    mapTile->setTileNumber(QPoint(x, y));
-    mapTile->setPixmap(pixmap);
-    m_mapScene->addMapTile(mapTile);
+        m_mapScene->setTilesDrawingLevels(m_zoomLevel);
+
+        getTiles(m_sceneCoordinate);
+    }
 }
 
-QGraphicsScene* MapEngine::scene()
+void MapEngine::zoomOut()
 {
-    return dynamic_cast<QGraphicsScene *>(m_mapScene);
+    qDebug() << __PRETTY_FUNCTION__;
+
+    if (m_zoomLevel > MIN_VIEW_ZOOM_LEVEL) {
+        m_zoomLevel--;
+        emit zoomLevelChanged(m_zoomLevel);
+
+        m_mapScene->setTilesDrawingLevels(m_zoomLevel);
+
+        getTiles(m_sceneCoordinate);
+    }
+}
+
+QString MapEngine::tilePath(int zoomLevel, int x, int y)
+{
+    QString tilePathString(QString::number(zoomLevel) + "/");
+    tilePathString.append(QString::number(x) + "/");
+    tilePathString.append(QString::number(y));
+
+    return tilePathString;
+}
+
+QPoint MapEngine::convertSceneCoordinateToTileNumber(int zoomLevel, QPoint sceneCoordinate)
+{
+    int pow = 1 << (MAX_MAP_ZOOM_LEVEL - zoomLevel);
+    int x = static_cast<int>(sceneCoordinate.x() / (TILE_SIZE_X*pow));
+    int y = static_cast<int>(sceneCoordinate.y() / (TILE_SIZE_Y*pow));
+
+    return QPoint(x, y);
+}
+
+QPoint MapEngine::convertTileNumberToSceneCoordinate(int zoomLevel, QPoint tileNumber)
+{
+    int pow = 1 << (MAX_MAP_ZOOM_LEVEL - zoomLevel);
+    int x = tileNumber.x() * TILE_SIZE_X * pow;
+    int y = tileNumber.y() * TILE_SIZE_Y * pow;
+
+    return QPoint(x, y);
+}
+
+QPoint MapEngine::convertLatLonToSceneCoordinate(QPointF latLonCoordinate)
+{
+    qDebug() << __PRETTY_FUNCTION__;
+
+    qreal longitude = latLonCoordinate.x();
+    qreal latitude = latLonCoordinate.y();
+
+    if ((longitude > MAX_LONGITUDE) || (longitude < MIN_LONGITUDE))
+        return QPoint(UNDEFINED, UNDEFINED);
+    if ((latitude > MAX_LATITUDE) || (latitude < MIN_LATITUDE))
+        return QPoint(UNDEFINED, UNDEFINED);
+
+    qreal z = static_cast<qreal>(1 << MAX_MAP_ZOOM_LEVEL);
+
+    qreal x = static_cast<qreal>((longitude + 180.0) / 360.0);
+    qreal y = static_cast<qreal>((1.0 - log(tan(latitude * M_PI / 180.0) + 1.0
+                                / cos(latitude * M_PI / 180.0)) / M_PI) / 2.0);
+
+    return QPointF(x*z*TILE_SIZE_X, y*z*TILE_SIZE_Y).toPoint();
 }