Merge branch 'master' into situare_interact
[situare] / src / map / mapengine.cpp
index 529a9ba..089f85a 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <QtAlgorithms>
 #include <QDebug>
+#include <QGraphicsView>
 #include <QString>
 #include <QStringList>
 #include <QUrl>
 #include <QRect>
 
 #include "common.h"
+#include "coordinates/geocoordinate.h"
 #include "frienditemshandler.h"
 #include "gpslocationitem.h"
 #include "mapcommon.h"
 #include "mapfetcher.h"
+#include "maprouteitem.h"
 #include "mapscene.h"
 #include "mapscroller.h"
 #include "maptile.h"
@@ -54,17 +57,18 @@ MapEngine::MapEngine(QObject *parent)
       m_scrollStartedByGps(false),
       m_smoothScrollRunning(false),
       m_zoomedIn(false),
-      m_zoomLevel(DEFAULT_ZOOM_LEVEL),
+      m_zoomLevel(MAP_DEFAULT_ZOOM_LEVEL),
       m_centerTile(QPoint(UNDEFINED, UNDEFINED)),
-      m_lastAutomaticPosition(QPoint(0, 0)),
+      m_sceneCoordinate(SceneCoordinate(GeoCoordinate(MAP_DEFAULT_LATITUDE, MAP_DEFAULT_LONGITUDE))),
       m_tilesGridSize(QSize(0, 0)),
-      m_viewSize(QSize(DEFAULT_SCREEN_WIDTH, DEFAULT_SCREEN_HEIGHT))
+      m_viewSize(QSize(DEFAULT_SCREEN_WIDTH, DEFAULT_SCREEN_HEIGHT)),
+      m_mapRouteItem(0)
 {
     qDebug() << __PRETTY_FUNCTION__;
 
     m_mapScene = new MapScene(this);
 
-    m_mapFetcher = new MapFetcher(NetworkAccessManager::instance(), this);
+    m_mapFetcher = new MapFetcher(new NetworkAccessManager(this), this);
     connect(this, SIGNAL(fetchImage(int, int, int)),
             m_mapFetcher, SLOT(enqueueFetchMapImage(int, int, int)));
     connect(m_mapFetcher, SIGNAL(mapImageReceived(int, int, int, QPixmap)),
@@ -86,8 +90,8 @@ MapEngine::MapEngine(QObject *parent)
     connect(this, SIGNAL(friendsLocationsReady(QList<User*>&)),
             m_friendItemsHandler, SLOT(friendListUpdated(QList<User*>&)));
 
-    connect(this, SIGNAL(friendImageReady(User*)),
-            m_friendItemsHandler, SLOT(friendImageReady(User*)));
+    connect(this, SIGNAL(friendImageReady(QString,QPixmap)),
+            m_friendItemsHandler, SLOT(friendImageReady(QString,QPixmap)));
 
     connect(this, SIGNAL(friendsLocationsReady(QList<User*>&)),
             this, SLOT(friendsPositionsUpdated()));
@@ -97,8 +101,8 @@ MapEngine::MapEngine(QObject *parent)
 
     m_scroller = &MapScroller::getInstance();
 
-    connect(m_scroller, SIGNAL(coordinateUpdated(QPoint)),
-            this, SLOT(setCenterPosition(QPoint)));
+    connect(m_scroller, SIGNAL(coordinateUpdated(SceneCoordinate)),
+            this, SLOT(setCenterPosition(SceneCoordinate)));
 
     connect(m_scroller, SIGNAL(stateChanged(QAbstractAnimation::State, QAbstractAnimation::State)),
             this, SLOT(scrollerStateChanged(QAbstractAnimation::State)));
@@ -108,17 +112,17 @@ MapEngine::~MapEngine()
 {
     qDebug() << __PRETTY_FUNCTION__;
 
-    QSettings settings(DIRECTORY_NAME, FILE_NAME);
-    settings.setValue(MAP_LAST_POSITION,
-                      convertSceneCoordinateToLatLon(m_zoomLevel, m_sceneCoordinate));
+    QSettings settings(SETTINGS_ORGANIZATION_NAME, SETTINGS_APPLICATION_NAME);
+
+    settings.setValue(MAP_LAST_POSITION, QVariant::fromValue(centerGeoCoordinate()));
     settings.setValue(MAP_LAST_ZOOMLEVEL, m_zoomLevel);
 }
 
-QRect MapEngine::calculateTileGrid(QPoint sceneCoordinate)
+QRect MapEngine::calculateTileGrid(SceneCoordinate coordinate)
 {
     qDebug() << __PRETTY_FUNCTION__;
 
-    QPoint tileCoordinate = convertSceneCoordinateToTileNumber(m_zoomLevel, sceneCoordinate);
+    QPoint tileCoordinate = convertSceneCoordinateToTileNumber(m_zoomLevel, coordinate);
 
     QPoint topLeft;
     topLeft.setX(tileCoordinate.x() - (m_tilesGridSize.width() / 2));
@@ -127,90 +131,98 @@ QRect MapEngine::calculateTileGrid(QPoint sceneCoordinate)
     return QRect(topLeft, m_tilesGridSize);
 }
 
-QPointF MapEngine::centerGeoCoordinate()
+void MapEngine::centerAndZoomTo(QRect rect, bool useMargins)
 {
     qDebug() << __PRETTY_FUNCTION__;
 
-    return convertSceneCoordinateToLatLon(m_zoomLevel, m_sceneCoordinate);
-}
+    int marginHorizontal = 0;
+    int marginVertical = 0;
 
-void MapEngine::centerToCoordinates(QPointF latLonCoordinate)
-{
-    qDebug() << __PRETTY_FUNCTION__;
+    if (useMargins) {
+        marginHorizontal = 5;
+        marginVertical = 5;
+    }
 
-    scrollToPosition(convertLatLonToSceneCoordinate(latLonCoordinate));
-}
+    // calculate the usable size of the view
+    int viewUsableHeight = m_viewSize.height() - 2 * marginVertical;
+    int viewUsableWidth = m_viewSize.width() - 2 * marginHorizontal;
 
-QPoint MapEngine::convertLatLonToSceneCoordinate(QPointF latLonCoordinate)
-{
-    qDebug() << __PRETTY_FUNCTION__;
+    // calculate how many levels must be zoomed out from the closest zoom level to get the rect
+    // fit inside the usable view area
+    int shift = 0;
+    while ((rect.height() > (viewUsableHeight * (1 << shift)))
+           || (rect.width() > (viewUsableWidth * (1 << shift)))) {
 
-    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);
+        shift++;
+    }
 
-    qreal z = static_cast<qreal>(MapEngine::tilesPerSide(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);
+    scrollToPosition(SceneCoordinate(double(rect.center().x()), double(rect.center().y())));
 
-    return QPointF(x * z * TILE_SIZE_X, y * z * TILE_SIZE_Y).toPoint();
+    int zoomLevel = qBound(OSM_MIN_ZOOM_LEVEL, OSM_MAX_ZOOM_LEVEL - shift, OSM_MAX_ZOOM_LEVEL);
+    setZoomLevel(zoomLevel);
 }
 
-QPointF MapEngine::convertSceneCoordinateToLatLon(int zoomLevel, QPoint sceneCoordinate)
+GeoCoordinate MapEngine::centerGeoCoordinate()
 {
     qDebug() << __PRETTY_FUNCTION__;
 
-    double tileFactor = 1 << (MAX_MAP_ZOOM_LEVEL - zoomLevel);
-    double xFactor = (sceneCoordinate.x() / (TILE_SIZE_X*tileFactor));
-    double yFactor = (sceneCoordinate.y() / (TILE_SIZE_Y*tileFactor));
+    return GeoCoordinate(m_sceneCoordinate);
+}
 
-    tileFactor = 1 << zoomLevel;
-    double longitude = xFactor / tileFactor * 360.0 - 180;
+void MapEngine::centerToCoordinates(GeoCoordinate coordinate)
+{
+    qDebug() << __PRETTY_FUNCTION__;
 
-    double n = M_PI - 2.0 * M_PI * yFactor / tileFactor;
-    double latitude = 180.0 / M_PI * atan(0.5 * (exp(n) - exp(-n)));
+    scrollToPosition(SceneCoordinate(coordinate));
+}
 
-    return QPointF(longitude, latitude);
+void MapEngine::clearRoute()
+{
+    qDebug() << __PRETTY_FUNCTION__;
+
+    if (m_mapRouteItem) {
+        m_mapScene->removeItem(m_mapRouteItem);
+        delete m_mapRouteItem;
+        m_mapRouteItem = 0;
+    }
 }
 
-QPoint MapEngine::convertSceneCoordinateToTileNumber(int zoomLevel, QPoint sceneCoordinate)
+QPoint MapEngine::convertSceneCoordinateToTileNumber(int zoomLevel, SceneCoordinate coordinate)
 {
     qDebug() << __PRETTY_FUNCTION__;
 
-    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));
+    int pow = 1 << (OSM_MAX_ZOOM_LEVEL - zoomLevel);
+    int x = static_cast<int>(coordinate.x() / (OSM_TILE_SIZE_X * pow));
+    int y = static_cast<int>(coordinate.y() / (OSM_TILE_SIZE_Y * pow));
 
     return QPoint(x, y);
 }
 
-QPoint MapEngine::convertTileNumberToSceneCoordinate(int zoomLevel, QPoint tileNumber)
+QRectF MapEngine::currentViewSceneRect() const
 {
     qDebug() << __PRETTY_FUNCTION__;
 
-    int pow = 1 << (MAX_MAP_ZOOM_LEVEL - zoomLevel);
-    int x = tileNumber.x() * TILE_SIZE_X * pow;
-    int y = tileNumber.y() * TILE_SIZE_Y * pow;
+    const QPoint ONE_PIXEL = QPoint(1, 1);
 
-    return QPoint(x, y);
+    QGraphicsView *view = m_mapScene->views().first();
+    QPointF sceneTopLeft = view->mapToScene(0, 0);
+    QPoint viewBottomRight = QPoint(view->size().width(), view->size().height()) - ONE_PIXEL;
+    QPointF sceneBottomRight = view->mapToScene(viewBottomRight);
+
+    return QRectF(sceneTopLeft, sceneBottomRight);
 }
 
-void MapEngine::disableAutoCenteringIfRequired(QPoint sceneCoordinate)
+void MapEngine::disableAutoCenteringIfRequired(SceneCoordinate coordinate)
 {
     if (isAutoCenteringEnabled()) {
-        int zoomFactor = (1 << (MAX_MAP_ZOOM_LEVEL - m_zoomLevel));
+        int zoomFactor = (1 << (OSM_MAX_ZOOM_LEVEL - m_zoomLevel));
 
-        QPoint oldPixelValue = QPoint(m_lastAutomaticPosition.x() / zoomFactor,
+        SceneCoordinate oldPixelValue(m_lastAutomaticPosition.x() / zoomFactor,
                                       m_lastAutomaticPosition.y() / zoomFactor);
 
-        QPoint newPixelValue = QPoint(sceneCoordinate.x() / zoomFactor,
-                                      sceneCoordinate.y() / zoomFactor);
+        SceneCoordinate newPixelValue(coordinate.x() / zoomFactor,
+                                      coordinate.y() / zoomFactor);
 
         if ((abs(oldPixelValue.x() - newPixelValue.x()) > AUTO_CENTERING_DISABLE_DISTANCE)
             || (abs(oldPixelValue.y() - newPixelValue.y()) > AUTO_CENTERING_DISABLE_DISTANCE)) {
@@ -224,14 +236,14 @@ void MapEngine::friendsPositionsUpdated()
 {
     qDebug() << __PRETTY_FUNCTION__;
 
-    m_mapScene->spanItems(m_zoomLevel, m_sceneCoordinate, m_viewSize);
+    m_mapScene->spanItems(currentViewSceneRect());
 }
 
-void MapEngine::getTiles(QPoint sceneCoordinate)
+void MapEngine::getTiles(SceneCoordinate coordinate)
 {
     qDebug() << __PRETTY_FUNCTION__;
 
-    m_viewTilesGrid = calculateTileGrid(sceneCoordinate);
+    m_viewTilesGrid = calculateTileGrid(coordinate);
     updateViewTilesSceneRect();
     m_mapScene->setTilesGrid(m_viewTilesGrid);
 
@@ -240,70 +252,67 @@ void MapEngine::getTiles(QPoint sceneCoordinate)
     int bottomRightX = m_viewTilesGrid.bottomRight().x();
     int bottomRightY = m_viewTilesGrid.bottomRight().y();
 
-    int tileMaxVal = tileMaxIndex(m_zoomLevel);
+    int tileMaxVal = MapTile::lastTileIndex(m_zoomLevel);
 
     for (int x = topLeftX; x <= bottomRightX; ++x) {
         for (int y = topLeftY; y <= bottomRightY; ++y) {
 
             // map doesn't span in vertical direction, so y index must be inside the limits
             if (y >= MAP_TILE_MIN_INDEX && y <= tileMaxVal) {
-                if (!m_mapScene->tileInScene(tilePath(m_zoomLevel, x, y)))
+                if (!m_mapScene->tileInScene(MapTile::tilePath(m_zoomLevel, x, y)))
                     emit fetchImage(m_zoomLevel, normalize(x, MAP_TILE_MIN_INDEX, tileMaxVal), y);
             }
         }
     }
 }
 
-void MapEngine::gpsPositionUpdate(QPointF position, qreal accuracy)
+void MapEngine::gpsPositionUpdate(GeoCoordinate position, qreal accuracy)
 {
     qDebug() << __PRETTY_FUNCTION__;
 
-    m_gpsLocationItem->updatePosition(convertLatLonToSceneCoordinate(position), accuracy);
-    m_mapScene->spanItems(m_zoomLevel, m_sceneCoordinate, m_viewSize);
+    m_gpsPosition = position;
+
+    // update GPS location item (but only if accuracy is a valid number)
+    if (!isnan(accuracy)) {
+        qreal resolution = MapScene::horizontalResolutionAtLatitude(position.latitude());
+        m_gpsLocationItem->updateItem(SceneCoordinate(position).toPointF(), accuracy, resolution);
+    }
+
+    m_mapScene->spanItems(currentViewSceneRect());
 
+    // do automatic centering (if enabled)
     if (m_autoCenteringEnabled) {
-        m_lastAutomaticPosition = convertLatLonToSceneCoordinate(position);
+        m_lastAutomaticPosition = SceneCoordinate(position);
         m_scrollStartedByGps = true;
         scrollToPosition(m_lastAutomaticPosition);
     }
-}
-
-qreal MapEngine::greatCircleDistance(QPointF firstLocation, QPointF secondLocation)
-{
-    qDebug() << __PRETTY_FUNCTION__;
-
-    const qreal TORAD = (M_PI/180);
 
-    qreal dLat = (secondLocation.y() - firstLocation.y())*TORAD;
-    qreal dLon = (secondLocation.x() - firstLocation.x())*TORAD;
-    qreal a = pow(sin(dLat/2),2) + cos(firstLocation.y()*TORAD) * cos(secondLocation.y()*TORAD)
-              * pow(sin(dLon/2),2);
-    qreal c = 2 * atan2(sqrt(a), sqrt(1-a));
-
-    return (EARTH_RADIUS * c);
+    updateDirectionIndicator();
 }
 
 void MapEngine::init()
 {
     qDebug() << __PRETTY_FUNCTION__;
 
-    QPointF startLocation;
-    QSettings settings(DIRECTORY_NAME, FILE_NAME);
+    QSettings settings(SETTINGS_ORGANIZATION_NAME, SETTINGS_APPLICATION_NAME);
 
-    if (settings.value(MAP_LAST_POSITION, ERROR_VALUE_NOT_FOUND_ON_SETTINGS).toString()
-        == ERROR_VALUE_NOT_FOUND_ON_SETTINGS || settings.value(MAP_LAST_ZOOMLEVEL,
-        ERROR_VALUE_NOT_FOUND_ON_SETTINGS).toString() == ERROR_VALUE_NOT_FOUND_ON_SETTINGS) {
+    // init can be only done if both values exists in the settings
+    if (settings.contains(MAP_LAST_POSITION) && settings.contains(MAP_LAST_ZOOMLEVEL)) {
+        QVariant zoomLevel = settings.value(MAP_LAST_ZOOMLEVEL);
+        QVariant location = settings.value(MAP_LAST_POSITION);
 
-        startLocation = QPointF(DEFAULT_LONGITUDE, DEFAULT_LATITUDE);
-        m_zoomLevel = qBound(MIN_VIEW_ZOOM_LEVEL, DEFAULT_START_ZOOM_LEVEL, MAX_MAP_ZOOM_LEVEL);
-    } else {
-        m_zoomLevel = settings.value(MAP_LAST_ZOOMLEVEL, ERROR_VALUE_NOT_FOUND_ON_SETTINGS).toInt();
-        startLocation = settings.value(MAP_LAST_POSITION,
-                                       ERROR_VALUE_NOT_FOUND_ON_SETTINGS).toPointF();
+        // also the init can be only done if we are able to convert variants into target data types
+        if (zoomLevel.canConvert<int>() && location.canConvert<GeoCoordinate>()) {
+            m_zoomLevel = zoomLevel.toInt();
+            m_sceneCoordinate = SceneCoordinate(location.value<GeoCoordinate>());
+        }
     }
 
+    // emit zoom level and center coordinate so that all parts of the map system gets initialized
+    // NOTE: emit is also done even if we weren't able to read initial valuef from the settings
+    //       so that the default values set in the constructor are used
     emit zoomLevelChanged(m_zoomLevel);
-    centerToCoordinates(QPointF(startLocation.x(), startLocation.y()));
+    scrollToPosition(m_sceneCoordinate);
 }
 
 bool MapEngine::isAutoCenteringEnabled()
@@ -311,32 +320,17 @@ bool MapEngine::isAutoCenteringEnabled()
     return m_autoCenteringEnabled;
 }
 
-bool MapEngine::isCenterTileChanged(QPoint sceneCoordinate)
+bool MapEngine::isCenterTileChanged(SceneCoordinate coordinate)
 {
     qDebug() << __PRETTY_FUNCTION__;
 
-    QPoint centerTile = convertSceneCoordinateToTileNumber(m_zoomLevel, sceneCoordinate);
+    QPoint centerTile = convertSceneCoordinateToTileNumber(m_zoomLevel, coordinate);
     QPoint temp = m_centerTile;
     m_centerTile = centerTile;
 
     return (centerTile != temp);
 }
 
-qreal MapEngine::sceneResolution()
-{
-    qDebug() << __PRETTY_FUNCTION__;
-
-    const int SHIFT = 200;
-    const int KM_TO_M = 1000;
-    qreal scale = (1 << (MAX_MAP_ZOOM_LEVEL - m_zoomLevel));
-    QPointF centerCoordinate = centerGeoCoordinate();
-    QPoint shiftedSceneCoordinate = QPoint(m_sceneCoordinate.x() + SHIFT*scale
-                                           , m_sceneCoordinate.y());
-    QPointF shiftedCoordinate = convertSceneCoordinateToLatLon(m_zoomLevel, shiftedSceneCoordinate);
-    qreal dist = greatCircleDistance(centerCoordinate, shiftedCoordinate) * KM_TO_M;
-    return (dist / SHIFT);
-}
-
 void MapEngine::mapImageReceived(int zoomLevel, int x, int y, const QPixmap &image)
 {
     qDebug() << __PRETTY_FUNCTION__;
@@ -349,14 +343,18 @@ void MapEngine::mapImageReceived(int zoomLevel, int x, int y, const QPixmap &ima
     int tilesGridWidthHalf = (m_viewTilesGrid.width() + 1) / 2;
 
     // duplicate to east side? (don't need to duplicate over padding)
-    if (tileNumber.x() < (tilesGridWidthHalf - GRID_PADDING)) {
-        QPoint adjustedTileNumber(tileNumber.x() + tileMaxIndex(zoomLevel) + 1, tileNumber.y());
+    if (tileNumber.x() < (tilesGridWidthHalf - MAP_GRID_PADDING)) {
+        QPoint adjustedTileNumber(tileNumber.x() + MapTile::lastTileIndex(zoomLevel) + 1,
+                                  tileNumber.y());
         m_mapScene->addTile(zoomLevel, adjustedTileNumber, image, m_zoomLevel);
     }
 
     // duplicate to west side? (don't need to duplicate over padding)
-    if (tileNumber.x() > (tileMaxIndex(zoomLevel) - tilesGridWidthHalf + GRID_PADDING)) {
-        QPoint adjustedTileNumber(tileNumber.x() - tileMaxIndex(zoomLevel) - 1, tileNumber.y());
+    if (tileNumber.x() > (MapTile::lastTileIndex(zoomLevel)
+                          - tilesGridWidthHalf
+                          + MAP_GRID_PADDING)) {
+        QPoint adjustedTileNumber(tileNumber.x() - MapTile::lastTileIndex(zoomLevel) - 1,
+                                  tileNumber.y());
         m_mapScene->addTile(zoomLevel, adjustedTileNumber, image, m_zoomLevel);
     }
 }
@@ -380,18 +378,14 @@ void MapEngine::receiveOwnLocation(User *user)
     qDebug() << __PRETTY_FUNCTION__;
 
     if(user) {
-        QPoint newPosition = convertLatLonToSceneCoordinate(user->coordinates());
-        if (m_ownLocation->pos().toPoint() != newPosition) {
-            m_ownLocation->setPos(newPosition);
-        }
-
+        m_ownLocation->setPos(SceneCoordinate(user->coordinates()).toPointF());
         if (!m_ownLocation->isVisible())
             m_ownLocation->show();
     } else {
         m_ownLocation->hide();
     }
 
-    m_mapScene->spanItems(m_zoomLevel, m_sceneCoordinate, m_viewSize);
+    m_mapScene->spanItems(currentViewSceneRect());
 }
 
 QGraphicsScene* MapEngine::scene()
@@ -417,7 +411,7 @@ void MapEngine::scrollerStateChanged(QAbstractAnimation::State newState)
     m_scrollStartedByGps = false;
 }
 
-void MapEngine::scrollToPosition(QPoint scenePosition)
+void MapEngine::scrollToPosition(SceneCoordinate coordinate)
 {
     qDebug() << __PRETTY_FUNCTION__;
 
@@ -425,7 +419,7 @@ void MapEngine::scrollToPosition(QPoint scenePosition)
     m_scroller->setEasingCurve(QEasingCurve::InOutQuart);
     m_scroller->setDuration(SMOOTH_CENTERING_TIME_MS);
     m_scroller->setStartValue(m_sceneCoordinate);
-    m_scroller->setEndValue(scenePosition);
+    m_scroller->setEndValue(coordinate);
     m_smoothScrollRunning = true;
     m_scroller->start();
 }
@@ -435,31 +429,38 @@ void MapEngine::setAutoCentering(bool enabled)
     qDebug() << __PRETTY_FUNCTION__;
 
     m_autoCenteringEnabled = enabled;
+
+    if (!m_autoCenteringEnabled && m_gpsLocationItem->isVisible())
+        updateDirectionIndicator();
 }
 
-void MapEngine::setCenterPosition(QPoint scenePosition)
+void MapEngine::setCenterPosition(SceneCoordinate coordinate)
 {
     qDebug() << __PRETTY_FUNCTION__;
 
     // jump to opposite side of the world if world horizontal limit is exceeded
-    scenePosition.setX(normalize(scenePosition.x(), MAP_MIN_PIXEL_X, MAP_MAX_PIXEL_X));
+    coordinate.setX(normalize(coordinate.x(), OSM_MAP_MIN_PIXEL_X, OSM_MAP_MAX_PIXEL_X));
 
     // don't allow vertical scene coordinates go out of the map
-    scenePosition.setY(qBound(MAP_MIN_PIXEL_Y, scenePosition.y(), MAP_MAX_PIXEL_Y));
+    coordinate.setY(qBound(double(OSM_MAP_MIN_PIXEL_Y),
+                              coordinate.y(),
+                              double(OSM_MAP_MAX_PIXEL_Y)));
 
     if (!m_smoothScrollRunning)
-        disableAutoCenteringIfRequired(scenePosition);
+        disableAutoCenteringIfRequired(coordinate);
 
-    m_sceneCoordinate = scenePosition;
+    m_sceneCoordinate = coordinate;
     emit locationChanged(m_sceneCoordinate);
 
-    if (isCenterTileChanged(scenePosition)) {
-        getTiles(scenePosition);
+    if (isCenterTileChanged(coordinate)) {
+        getTiles(coordinate);
         m_mapScene->removeOutOfViewTiles(m_viewTilesGrid, m_zoomLevel);
     }
 
-    m_mapScene->spanItems(m_zoomLevel, m_sceneCoordinate, m_viewSize);
-    emit newMapResolution(sceneResolution());
+    m_mapScene->spanItems(currentViewSceneRect());
+    emit newMapResolution(viewResolution());
+
+    updateDirectionIndicator();
 }
 
 void MapEngine::setGPSEnabled(bool enabled)
@@ -469,26 +470,16 @@ void MapEngine::setGPSEnabled(bool enabled)
     m_gpsLocationItem->setEnabled(enabled);
 }
 
-void MapEngine::setRoute(Route route)
+void MapEngine::setRoute(Route &route)
 {
-    qWarning() << __PRETTY_FUNCTION__;
-
-//    m_route = route;
+    qDebug() << __PRETTY_FUNCTION__;
 
-    qWarning() << __PRETTY_FUNCTION__ << "from:" << route.startPointName();
-    qWarning() << __PRETTY_FUNCTION__ << "to:" << route.endPointName();
-    qWarning() << __PRETTY_FUNCTION__ << "distance:" << route.totalDistance();
-    qWarning() << __PRETTY_FUNCTION__ << "estimated time:" << route.totalTime();
+    clearRoute();
 
-    foreach (QPointF point, route.geometryPoints())
-        qWarning() << __PRETTY_FUNCTION__ << "geometry point:" << point.x() << point.y();
+    m_mapRouteItem = new MapRouteItem(&route);
+    m_mapScene->addItem(m_mapRouteItem);
 
-    foreach (RouteSegment *segment, route.segments()) {
-        qWarning() << __PRETTY_FUNCTION__ << "segment:" << segment->instruction();
-    }
-
-    /// @todo draw track
-    /// @todo calculate bounding box for the track, add margins, ensure visible (zoom, center)
+    centerAndZoomTo(m_mapRouteItem->boundingRect().toRect());
 }
 
 void MapEngine::setZoomLevel(int newZoomLevel)
@@ -510,10 +501,10 @@ void MapEngine::setTilesGridSize(const QSize &viewSize)
     // converting scene tile to tile number does cause grid centering inaccuracy of one tile
     const int CENTER_TILE_INACCURACY = 1;
 
-    int gridWidth = ceil(qreal(viewSize.width()) / TILE_SIZE_X + SCROLLING_RESERVE)
-                    + CENTER_TILE_INACCURACY + (GRID_PADDING * 2);
-    int gridHeight = ceil(qreal(viewSize.height()) / TILE_SIZE_Y + SCROLLING_RESERVE)
-                     + CENTER_TILE_INACCURACY + (GRID_PADDING * 2);
+    int gridWidth = ceil(qreal(viewSize.width()) / OSM_TILE_SIZE_X + SCROLLING_RESERVE)
+                    + CENTER_TILE_INACCURACY + (MAP_GRID_PADDING * 2);
+    int gridHeight = ceil(qreal(viewSize.height()) / OSM_TILE_SIZE_Y + SCROLLING_RESERVE)
+                     + CENTER_TILE_INACCURACY + (MAP_GRID_PADDING * 2);
 
     m_mapFetcher->setDownloadQueueSize(gridWidth * gridHeight);
 
@@ -521,28 +512,32 @@ void MapEngine::setTilesGridSize(const QSize &viewSize)
     m_tilesGridSize.setWidth(gridWidth);
 }
 
-int MapEngine::tileMaxIndex(int zoomLevel)
+void MapEngine::showMapArea(const GeoCoordinate &swBound, const GeoCoordinate &neBound)
 {
     qDebug() << __PRETTY_FUNCTION__;
 
-    // subtract one because first tile index is zero
-    return tilesPerSide(zoomLevel) - 1;
+    QRect area;
+    area.setTopRight(SceneCoordinate(neBound).toPointF().toPoint());
+    area.setBottomLeft(SceneCoordinate(swBound).toPointF().toPoint());
+
+    centerAndZoomTo(area, false);
 }
 
-QString MapEngine::tilePath(int zoomLevel, int x, int y)
+void MapEngine::updateDirectionIndicator()
 {
     qDebug() << __PRETTY_FUNCTION__;
 
-    QString tilePathString(QString::number(zoomLevel) + "/");
-    tilePathString.append(QString::number(x) + "/");
-    tilePathString.append(QString::number(y));
+    qreal distance = m_gpsPosition.distanceTo(m_sceneCoordinate);
 
-    return tilePathString;
-}
+    qreal direction = m_sceneCoordinate.azimuthTo(SceneCoordinate(m_gpsPosition));
 
-int MapEngine::tilesPerSide(int zoomLevel)
-{
-    return (1 << zoomLevel);
+    // direction indicator triangle should be drawn only if the gps location item is not currently
+    // visible on the view
+    bool drawDirectionIndicatorTriangle = true;
+    if (currentViewSceneRect().contains(m_gpsLocationItem->pos()))
+        drawDirectionIndicatorTriangle = false;
+
+    emit directionIndicatorValuesUpdate(direction, distance, drawDirectionIndicatorTriangle);
 }
 
 void MapEngine::updateViewTilesSceneRect()
@@ -550,16 +545,21 @@ void MapEngine::updateViewTilesSceneRect()
     qDebug() << __PRETTY_FUNCTION__;
 
     const QPoint ONE_TILE = QPoint(1, 1);
-    const QPoint ONE_PIXEL = QPoint(1, 1);
+    const double ONE_PIXEL = 1;
+
+    SceneCoordinate topLeft = MapTile::convertTileNumberToSceneCoordinate(m_zoomLevel,
+                                                                        m_viewTilesGrid.topLeft());
 
-    QPoint topLeft = convertTileNumberToSceneCoordinate(m_zoomLevel, m_viewTilesGrid.topLeft());
     // one tile - one pixel is added because returned coordinates are pointing to upper left corner
     // of the last tile.
-    QPoint bottomRight = convertTileNumberToSceneCoordinate(m_zoomLevel,
-                                                            m_viewTilesGrid.bottomRight()
-                                                             + ONE_TILE) - ONE_PIXEL;
+    SceneCoordinate bottomRight
+            = MapTile::convertTileNumberToSceneCoordinate(m_zoomLevel,
+                                                          m_viewTilesGrid.bottomRight() + ONE_TILE);
+    bottomRight.setX(bottomRight.x() - ONE_PIXEL);
+    bottomRight.setY(bottomRight.y() - ONE_PIXEL);
 
-    m_mapScene->tilesSceneRectUpdated(QRect(topLeft, bottomRight));
+    m_mapScene->tilesSceneRectUpdated(QRect(topLeft.toPointF().toPoint(),
+                                            bottomRight.toPointF().toPoint()));
 }
 
 void MapEngine::viewResized(const QSize &size)
@@ -575,18 +575,29 @@ void MapEngine::viewResized(const QSize &size)
     m_mapScene->setSceneVerticalOverlap(m_viewSize.height(), m_zoomLevel);
 }
 
+qreal MapEngine::viewResolution()
+{
+    qDebug() << __PRETTY_FUNCTION__;
+
+    qreal scale = (1 << (OSM_MAX_ZOOM_LEVEL - m_zoomLevel));
+
+    return MapScene::horizontalResolutionAtLatitude(centerGeoCoordinate().latitude()) * scale;
+}
+
 void MapEngine::viewZoomFinished()
 {
     qDebug() << __PRETTY_FUNCTION__;
 
+    updateDirectionIndicator();
+
     if (m_zoomedIn) {
         m_zoomedIn = false;
         m_mapScene->removeOutOfViewTiles(m_viewTilesGrid, m_zoomLevel);
     }
 
-    if (m_zoomLevel == MAX_MAP_ZOOM_LEVEL)
+    if (m_zoomLevel == OSM_MAX_ZOOM_LEVEL)
         emit maxZoomLevelReached();
-    else if (m_zoomLevel == MIN_VIEW_ZOOM_LEVEL)
+    else if (m_zoomLevel == MAP_VIEW_MIN_ZOOM_LEVEL)
         emit minZoomLevelReached();
 }
 
@@ -597,15 +608,15 @@ void MapEngine::zoomed()
     m_mapScene->setZoomLevel(m_zoomLevel);
     getTiles(m_sceneCoordinate);
     m_mapScene->setSceneVerticalOverlap(m_viewSize.height(), m_zoomLevel);
-    m_mapScene->spanItems(m_zoomLevel, m_sceneCoordinate, m_viewSize);
-    emit newMapResolution(sceneResolution());
+    m_mapScene->spanItems(currentViewSceneRect());
+    emit newMapResolution(viewResolution());
 }
 
 void MapEngine::zoomIn()
 {
     qDebug() << __PRETTY_FUNCTION__;
 
-    if (m_zoomLevel < MAX_MAP_ZOOM_LEVEL) {
+    if (m_zoomLevel < OSM_MAX_ZOOM_LEVEL) {
         m_zoomLevel++;
         m_zoomedIn = true;
         zoomed();
@@ -616,7 +627,7 @@ void MapEngine::zoomOut()
 {
     qDebug() << __PRETTY_FUNCTION__;
 
-    if (m_zoomLevel > MIN_VIEW_ZOOM_LEVEL) {
+    if (m_zoomLevel > MAP_VIEW_MIN_ZOOM_LEVEL) {
         m_zoomLevel--;
         zoomed();
     }