#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"
m_zoomedIn(false),
m_zoomLevel(MAP_DEFAULT_ZOOM_LEVEL),
m_centerTile(QPoint(UNDEFINED, UNDEFINED)),
- /// @todo remove, automatic init to zero // 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)),
settings.setValue(MAP_LAST_ZOOMLEVEL, m_zoomLevel);
}
-QRect MapEngine::calculateTileGrid(SceneCoordinate 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));
return QRect(topLeft, m_tilesGridSize);
}
-GeoCoordinate MapEngine::centerGeoCoordinate()
-{
- qDebug() << __PRETTY_FUNCTION__;
-
- return GeoCoordinate(m_sceneCoordinate);
-}
-
-void MapEngine::centerToCoordinates(GeoCoordinate latLonCoordinate)
+void MapEngine::centerAndZoomTo(QRect rect, bool useMargins)
{
qDebug() << __PRETTY_FUNCTION__;
- scrollToPosition(SceneCoordinate(latLonCoordinate));
-}
+ int marginHorizontal = 0;
+ int marginVertical = 0;
-/// @todo remove
-//QPoint MapEngine::convertLatLonToSceneCoordinate(QPointF latLonCoordinate)
-//{
-// qDebug() << __PRETTY_FUNCTION__;
+ if (useMargins) {
+ marginHorizontal = 50;
+ marginVertical = 5;
+ }
-// qreal longitude = latLonCoordinate.x();
-// qreal latitude = latLonCoordinate.y();
+ // calculate the usable size of the view
+ int viewUsableHeight = m_viewSize.height() - 2 * marginHorizontal;
+ int viewUsableWidth = m_viewSize.width() - 2 * marginVertical;
-// if ((longitude > MAX_LONGITUDE) || (longitude < MIN_LONGITUDE))
-// return QPoint(UNDEFINED, UNDEFINED);
-// if ((latitude > OSM_MAX_LATITUDE) || (latitude < OSM_MIN_LATITUDE))
-// return QPoint(UNDEFINED, UNDEFINED);
+ // 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 z = static_cast<qreal>(MapEngine::tilesPerSide(OSM_MAX_ZOOM_LEVEL));
+ shift++;
+ }
-// 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 * OSM_TILE_SIZE_X, y * z * OSM_TILE_SIZE_Y).toPoint();
-//}
+ scrollToPosition(SceneCoordinate(double(rect.center().x()), double(rect.center().y())));
-//QPointF MapEngine::convertSceneCoordinateToLatLon(int zoomLevel, QPoint sceneCoordinate)
-//{
-// qDebug() << __PRETTY_FUNCTION__;
+ int zoomLevel = qBound(OSM_MIN_ZOOM_LEVEL, OSM_MAX_ZOOM_LEVEL - shift, OSM_MAX_ZOOM_LEVEL);
+ setZoomLevel(zoomLevel);
+}
-// double tileFactor = 1 << (OSM_MAX_ZOOM_LEVEL - zoomLevel);
-// double xFactor = (sceneCoordinate.x() / (OSM_TILE_SIZE_X*tileFactor));
-// double yFactor = (sceneCoordinate.y() / (OSM_TILE_SIZE_Y*tileFactor));
+GeoCoordinate MapEngine::centerGeoCoordinate()
+{
+ qDebug() << __PRETTY_FUNCTION__;
-// tileFactor = 1 << zoomLevel;
-// double longitude = xFactor / tileFactor * 360.0 - 180;
+ return GeoCoordinate(m_sceneCoordinate);
+}
-// double n = M_PI - 2.0 * M_PI * yFactor / tileFactor;
-// double latitude = 180.0 / M_PI * atan(0.5 * (exp(n) - exp(-n)));
+void MapEngine::centerToCoordinates(GeoCoordinate coordinate)
+{
+ qDebug() << __PRETTY_FUNCTION__;
-// return QPointF(longitude, latitude);
-//}
+ scrollToPosition(SceneCoordinate(coordinate));
+}
-QPoint MapEngine::convertSceneCoordinateToTileNumber(int zoomLevel, SceneCoordinate sceneCoordinate)
+QPoint MapEngine::convertSceneCoordinateToTileNumber(int zoomLevel, SceneCoordinate coordinate)
{
qDebug() << __PRETTY_FUNCTION__;
int pow = 1 << (OSM_MAX_ZOOM_LEVEL - zoomLevel);
- int x = static_cast<int>(sceneCoordinate.x() / (OSM_TILE_SIZE_X * pow));
- int y = static_cast<int>(sceneCoordinate.y() / (OSM_TILE_SIZE_Y * pow));
+ 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);
}
-SceneCoordinate MapEngine::convertTileNumberToSceneCoordinate(int zoomLevel, QPoint tileNumber)
+QRectF MapEngine::currentViewSceneRect() const
{
qDebug() << __PRETTY_FUNCTION__;
- int pow = 1 << (OSM_MAX_ZOOM_LEVEL - zoomLevel);
- int x = tileNumber.x() * OSM_TILE_SIZE_X * pow;
- int y = tileNumber.y() * OSM_TILE_SIZE_Y * pow;
+ const QPoint ONE_PIXEL = QPoint(1, 1);
- return SceneCoordinate(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(SceneCoordinate sceneCoordinate)
+void MapEngine::disableAutoCenteringIfRequired(SceneCoordinate coordinate)
{
if (isAutoCenteringEnabled()) {
int zoomFactor = (1 << (OSM_MAX_ZOOM_LEVEL - m_zoomLevel));
SceneCoordinate oldPixelValue(m_lastAutomaticPosition.x() / zoomFactor,
m_lastAutomaticPosition.y() / zoomFactor);
- SceneCoordinate newPixelValue(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)) {
{
qDebug() << __PRETTY_FUNCTION__;
- m_mapScene->spanItems(m_zoomLevel, m_sceneCoordinate, m_viewSize);
+ m_mapScene->spanItems(currentViewSceneRect());
}
-void MapEngine::getTiles(SceneCoordinate sceneCoordinate)
+void MapEngine::getTiles(SceneCoordinate coordinate)
{
qDebug() << __PRETTY_FUNCTION__;
- m_viewTilesGrid = calculateTileGrid(sceneCoordinate);
+ m_viewTilesGrid = calculateTileGrid(coordinate);
updateViewTilesSceneRect();
m_mapScene->setTilesGrid(m_viewTilesGrid);
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);
}
}
{
qDebug() << __PRETTY_FUNCTION__;
- m_gpsLocationItem->updatePosition(SceneCoordinate(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 = SceneCoordinate(position);
m_scrollStartedByGps = true;
scrollToPosition(m_lastAutomaticPosition);
}
-}
-
-qreal MapEngine::greatCircleDistance(GeoCoordinate firstLocation, GeoCoordinate secondLocation)
-{
- qDebug() << __PRETTY_FUNCTION__;
- const qreal TO_RAD = (M_PI / 180);
-
- qreal dLat = (secondLocation.latitude() - firstLocation.latitude()) * TO_RAD;
- qreal dLon = (secondLocation.longitude() - firstLocation.longitude()) * TO_RAD;
- qreal a = pow(sin(dLat / 2), 2)
- + cos(firstLocation.latitude() * TO_RAD)
- * cos(secondLocation.latitude() * TO_RAD)
- * pow(sin(dLon / 2), 2);
- qreal c = 2 * atan2(sqrt(a), sqrt(1 - a));
-
- return (EARTH_RADIUS * c);
+ updateDirectionIndicator();
}
void MapEngine::init()
QSettings settings(DIRECTORY_NAME, FILE_NAME);
- /// @todo remove
-// 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) {
-
-// startLocation = GeoCoordinate(MAP_DEFAULT_LATITUDE, MAP_DEFAULT_LONGITUDE);
-// m_zoomLevel = qBound(MAP_VIEW_MIN_ZOOM_LEVEL, MAP_DEFAULT_ZOOM_LEVEL, OSM_MAX_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();
-// }
-
// 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);
return m_autoCenteringEnabled;
}
-bool MapEngine::isCenterTileChanged(SceneCoordinate 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 << (OSM_MAX_ZOOM_LEVEL - m_zoomLevel));
- GeoCoordinate centerCoordinate = centerGeoCoordinate();
- SceneCoordinate shiftedSceneCoordinate(m_sceneCoordinate.x() + SHIFT * scale,
- m_sceneCoordinate.y());
- GeoCoordinate shiftedCoordinate(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__;
// duplicate to east side? (don't need to duplicate over padding)
if (tileNumber.x() < (tilesGridWidthHalf - MAP_GRID_PADDING)) {
- QPoint adjustedTileNumber(tileNumber.x() + tileMaxIndex(zoomLevel) + 1, tileNumber.y());
+ 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 + MAP_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);
}
}
m_ownLocation->hide();
}
- m_mapScene->spanItems(m_zoomLevel, m_sceneCoordinate, m_viewSize);
+ m_mapScene->spanItems(currentViewSceneRect());
}
QGraphicsScene* MapEngine::scene()
m_scrollStartedByGps = false;
}
-void MapEngine::scrollToPosition(SceneCoordinate scenePosition)
+void MapEngine::scrollToPosition(SceneCoordinate coordinate)
{
qDebug() << __PRETTY_FUNCTION__;
m_scroller->stop();
m_scroller->setEasingCurve(QEasingCurve::InOutQuart);
m_scroller->setDuration(SMOOTH_CENTERING_TIME_MS);
- m_scroller->setStartValue(m_sceneCoordinate.toPointF());
- m_scroller->setEndValue(scenePosition.toPointF());
+ m_scroller->setStartValue(m_sceneCoordinate);
+ m_scroller->setEndValue(coordinate);
m_smoothScrollRunning = true;
m_scroller->start();
}
qDebug() << __PRETTY_FUNCTION__;
m_autoCenteringEnabled = enabled;
+
+ if (!m_autoCenteringEnabled && m_gpsLocationItem->isVisible())
+ updateDirectionIndicator();
}
-void MapEngine::setCenterPosition(SceneCoordinate 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(), OSM_MAP_MIN_PIXEL_X, OSM_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(double(OSM_MAP_MIN_PIXEL_Y),
- scenePosition.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)
m_gpsLocationItem->setEnabled(enabled);
}
+void MapEngine::setRoute(Route &route)
+{
+ qDebug() << __PRETTY_FUNCTION__;
+
+ m_route = route;
+
+ // delete old route track (if exists)
+ if (m_mapRouteItem) {
+ m_mapScene->removeItem(m_mapRouteItem);
+ delete m_mapRouteItem;
+ m_mapRouteItem = 0;
+ }
+
+ // create new route track
+ m_mapRouteItem = new MapRouteItem(&m_route);
+ m_mapScene->addItem(m_mapRouteItem);
+
+ centerAndZoomTo(m_mapRouteItem->boundingRect().toRect());
+}
+
void MapEngine::setZoomLevel(int newZoomLevel)
{
qDebug() << __PRETTY_FUNCTION__;
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()
const QPoint ONE_TILE = QPoint(1, 1);
const double ONE_PIXEL = 1;
- SceneCoordinate topLeft = convertTileNumberToSceneCoordinate(m_zoomLevel,
- m_viewTilesGrid.topLeft());
+ SceneCoordinate topLeft = MapTile::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.
- SceneCoordinate bottomRight = convertTileNumberToSceneCoordinate(m_zoomLevel,
- m_viewTilesGrid.bottomRight()
- + ONE_TILE);
+ 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->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);
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()