summaryrefslogtreecommitdiff
path: root/src/location/maps/qgeotiledmapscene.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/location/maps/qgeotiledmapscene.cpp')
-rw-r--r--src/location/maps/qgeotiledmapscene.cpp140
1 files changed, 81 insertions, 59 deletions
diff --git a/src/location/maps/qgeotiledmapscene.cpp b/src/location/maps/qgeotiledmapscene.cpp
index 3e75cece..fbd05645 100644
--- a/src/location/maps/qgeotiledmapscene.cpp
+++ b/src/location/maps/qgeotiledmapscene.cpp
@@ -43,7 +43,16 @@
#include <QtCore/private/qobject_p.h>
#include <QtQuick/QSGImageNode>
#include <QtQuick/QQuickWindow>
+#include <QtGui/QVector3D>
#include <cmath>
+#include <QtPositioning/private/qlocationutils_p.h>
+#include <QtPositioning/private/qdoublematrix4x4_p.h>
+#include <QtPositioning/private/qwebmercator_p.h>
+
+static QVector3D toVector3D(const QDoubleVector3D& in)
+{
+ return QVector3D(in.x(), in.y(), in.z());
+}
QT_BEGIN_NAMESPACE
@@ -75,6 +84,7 @@ public:
// the number of tiles in each direction for the whole map (earth) at the current zoom level.
// it is 1<<zoomLevel
int m_sideLength;
+ double m_mapEdgeSize;
QHash<QGeoTileSpec, QSharedPointer<QGeoTileTexture> > m_textures;
@@ -85,31 +95,16 @@ public:
int m_maxTileY;
int m_tileXWrapsBelow; // the wrap point as a tile index
- // cameraToScreen transform
- double m_screenWidth; // in pixels
- double m_screenHeight; // in pixels
bool m_linearScaling;
bool m_dropTextures;
void addTile(const QGeoTileSpec &spec, QSharedPointer<QGeoTileTexture> texture);
- QDoubleVector2D itemPositionToMercator(const QDoubleVector2D &pos) const;
- QDoubleVector2D mercatorToItemPosition(const QDoubleVector2D &mercator) const;
-
- QDoubleVector2D geoToMapProjection(const QGeoCoordinate &coordinate) const;
- QGeoCoordinate mapProjectionToGeo(const QDoubleVector2D &projection) const;
-
- QDoubleVector2D wrapMapProjection(const QDoubleVector2D &projection) const;
- QDoubleVector2D unwrapMapProjection(const QDoubleVector2D &wrappedProjection) const;
-
- QDoubleVector2D wrappedMapProjectionToItemPosition(const QDoubleVector2D &wrappedProjection) const;
- QDoubleVector2D itemPositionToWrappedMapProjection(const QDoubleVector2D &itemPosition) const;
-
- void setVisibleTiles(const QSet<QGeoTileSpec> &tiles);
+ void setVisibleTiles(const QSet<QGeoTileSpec> &visibleTiles);
void removeTiles(const QSet<QGeoTileSpec> &oldTiles);
bool buildGeometry(const QGeoTileSpec &spec, QSGImageNode *imageNode);
- void setTileBounds(const QSet<QGeoTileSpec> &tiles);
+ void updateTileBounds(const QSet<QGeoTileSpec> &tiles);
void setupCamera();
inline bool isTiltedOrRotated() { return (m_cameraData.tilt() > 0.0) || (m_cameraData.bearing() > 0.0); }
};
@@ -143,6 +138,7 @@ void QGeoTiledMapScene::setCameraData(const QGeoCameraData &cameraData)
float delta = cameraData.zoomLevel() - d->m_intZoomLevel;
d->m_linearScaling = qAbs(delta) > 0.05 || d->isTiltedOrRotated();
d->m_sideLength = 1 << d->m_intZoomLevel;
+ d->m_mapEdgeSize = std::pow(2.0, cameraData.zoomLevel()) * d->m_tileSize;
}
void QGeoTiledMapScene::setVisibleTiles(const QSet<QGeoTileSpec> &tiles)
@@ -191,8 +187,6 @@ QGeoTiledMapScenePrivate::QGeoTiledMapScenePrivate()
m_maxTileX(-1),
m_maxTileY(-1),
m_tileXWrapsBelow(0),
- m_screenWidth(0.0),
- m_screenHeight(0.0),
m_linearScaling(false),
m_dropTextures(false)
{
@@ -245,19 +239,19 @@ void QGeoTiledMapScenePrivate::addTile(const QGeoTileSpec &spec, QSharedPointer<
m_textures.insert(spec, texture);
}
-void QGeoTiledMapScenePrivate::setVisibleTiles(const QSet<QGeoTileSpec> &tiles)
+void QGeoTiledMapScenePrivate::setVisibleTiles(const QSet<QGeoTileSpec> &visibleTiles)
{
// work out the tile bounds for the new scene
- setTileBounds(tiles);
+ updateTileBounds(visibleTiles);
// set up the gl camera for the new scene
setupCamera();
- QSet<QGeoTileSpec> toRemove = m_visibleTiles - tiles;
+ QSet<QGeoTileSpec> toRemove = m_visibleTiles - visibleTiles;
if (!toRemove.isEmpty())
removeTiles(toRemove);
- m_visibleTiles = tiles;
+ m_visibleTiles = visibleTiles;
}
void QGeoTiledMapScenePrivate::removeTiles(const QSet<QGeoTileSpec> &oldTiles)
@@ -272,7 +266,7 @@ void QGeoTiledMapScenePrivate::removeTiles(const QSet<QGeoTileSpec> &oldTiles)
}
}
-void QGeoTiledMapScenePrivate::setTileBounds(const QSet<QGeoTileSpec> &tiles)
+void QGeoTiledMapScenePrivate::updateTileBounds(const QSet<QGeoTileSpec> &tiles)
{
if (tiles.isEmpty()) {
m_minTileX = -1;
@@ -354,26 +348,24 @@ void QGeoTiledMapScenePrivate::setTileBounds(const QSet<QGeoTileSpec> &tiles)
void QGeoTiledMapScenePrivate::setupCamera()
{
+ // NOTE: The following instruction is correct only because WebMercator is a square projection!
double f = 1.0 * qMin(m_screenSize.width(), m_screenSize.height());
- // fraction of zoom level
+ // Using fraction of zoom level, z varies between [ m_tileSize , 2 * m_tileSize [
double z = std::pow(2.0, m_cameraData.zoomLevel() - m_intZoomLevel) * m_tileSize;
- // Maps smaller than screen size are NOT supported. But we can't simply return here, as this call
- // might be invoked also before a valid zoom level is set.
-
// calculate altitude that allows the visible map tiles
- // to fit in the screen correctly (note that a larger f would cause
- // the camera be higher, resulting in empty areas displayed around
- // the tiles or repeated tiles)
- double altitude = f / (2.0 * z) ;
+ // to fit in the screen correctly (note that a larger f will cause
+ // the camera be higher, resulting in gray areas displayed around
+ // the tiles)
+ double altitude = f / (2.0 * z);
// calculate center
double edge = m_scaleFactor * m_tileSize;
// first calculate the camera center in map space in the range of 0 <-> sideLength (2^z)
QDoubleVector2D camCenterMercator = QWebMercator::coordToMercator(m_cameraData.center());
- QDoubleVector3D center = m_sideLength * camCenterMercator;
+ QDoubleVector3D center = (m_sideLength * camCenterMercator);
// wrap the center if necessary (due to dateline crossing)
if (center.x() < m_tileXWrapsBelow)
@@ -383,16 +375,15 @@ void QGeoTiledMapScenePrivate::setupCamera()
center.setX(center.x() - 1.0 * m_minTileX);
center.setY(1.0 * m_minTileY - center.y());
- m_screenHeight = m_screenSize.height();
- m_screenWidth = m_screenSize.width();
-
// apply necessary scaling to the camera center
center *= edge;
// calculate eye
-
+ double apertureSize = 1.0;
+ if (m_cameraData.fieldOfView() != 90.0) //aperture(90 / 2) = 1
+ apertureSize = tan(QLocationUtils::radians(m_cameraData.fieldOfView()) * 0.5);
QDoubleVector3D eye = center;
- eye.setZ(altitude * edge);
+ eye.setZ(altitude * edge / apertureSize);
// calculate up
@@ -400,19 +391,24 @@ void QGeoTiledMapScenePrivate::setupCamera()
QDoubleVector3D side = QDoubleVector3D::normal(view, QDoubleVector3D(0.0, 1.0, 0.0));
QDoubleVector3D up = QDoubleVector3D::normal(side, view);
- // old bearing, tilt and roll code
- // QMatrix4x4 mBearing;
- // mBearing.rotate(-1.0 * camera.bearing(), view);
- // up = mBearing * up;
-
- // QDoubleVector3D side2 = QDoubleVector3D::normal(up, view);
- // QMatrix4x4 mTilt;
- // mTilt.rotate(camera.tilt(), side2);
- // eye = (mTilt * view) + center;
+ // old bearing, tilt and roll code.
+ // Now using double matrices until distilling the transformation to QMatrix4x4
+ QDoubleMatrix4x4 mBearing;
+ // -1.0 * bearing removed, now map north goes in the bearing direction
+ mBearing.rotate(-1.0 * m_cameraData.bearing(), view);
+ up = mBearing * up;
+
+ QDoubleVector3D side2 = QDoubleVector3D::normal(up, view);
+ if (m_cameraData.tilt() > 0.01) {
+ QDoubleMatrix4x4 mTilt;
+ mTilt.rotate(m_cameraData.tilt(), side2);
+ eye = mTilt * view + center;
+ }
- // view = eye - center;
- // side = QDoubleVector3D::normal(view, QDoubleVector3D(0.0, 1.0, 0.0));
- // up = QDoubleVector3D::normal(view, side2);
+ view = eye - center;
+ view.normalize();
+ side = QDoubleVector3D::normal(view, QDoubleVector3D(0.0, 1.0, 0.0));
+ up = QDoubleVector3D::normal(view, side2);
// QMatrix4x4 mRoll;
// mRoll.rotate(camera.roll(), view);
@@ -421,15 +417,19 @@ void QGeoTiledMapScenePrivate::setupCamera()
// near plane and far plane
double nearPlane = 1.0;
- double farPlane = (altitude + 1.0) * edge;
+ // Clip plane. Used to be (altitude + 1.0) * edge. This does not affect the perspective. minimum value would be > 0.0
+ // Since, for some reasons possibly related to how QSG works, this clipping plane is unable to clip part of tiles,
+ // Instead of farPlane = (altitude + m_cameraData.clipDistance()) * edge , we use a fixed large clipDistance, and
+ // leave the clipping only in QGeoCameraTiles::createFrustum
+ double farPlane = (altitude + 10000.0) * edge;
m_cameraUp = up;
m_cameraCenter = center;
m_cameraEye = eye;
double aspectRatio = 1.0 * m_screenSize.width() / m_screenSize.height();
- float halfWidth = 1;
- float halfHeight = 1;
+ float halfWidth = 1 * apertureSize;
+ float halfHeight = 1 * apertureSize;
if (aspectRatio > 1.0) {
halfWidth *= aspectRatio;
} else if (aspectRatio > 0.0f && aspectRatio < 1.0f) {
@@ -503,14 +503,33 @@ public:
QHash<QGeoTileSpec, QSGTexture *> textures;
};
-static bool qgeotiledmapscene_isTileInViewport(const QRectF &tileRect, const QMatrix4x4 &matrix) {
+static bool qgeotiledmapscene_isTileInViewport_Straight(const QRectF &tileRect, const QMatrix4x4 &matrix)
+{
const QRectF boundingRect = QRectF(matrix * tileRect.topLeft(), matrix * tileRect.bottomRight());
return QRectF(-1, -1, 2, 2).intersects(boundingRect);
}
-static QVector3D toVector3D(const QDoubleVector3D& in)
+static bool qgeotiledmapscene_isTileInViewport_rotationTilt(const QRectF &tileRect, const QMatrix4x4 &matrix)
{
- return QVector3D(in.x(), in.y(), in.z());
+ // Transformed corners
+ const QPointF tlt = matrix * tileRect.topLeft();
+ const QPointF trt = matrix * tileRect.topRight();
+ const QPointF blt = matrix * tileRect.bottomLeft();
+ const QPointF brt = matrix * tileRect.bottomRight();
+
+ const QRectF boundingRect = QRectF(QPointF(qMin(qMin(qMin(tlt.x(), trt.x()), blt.x()), brt.x())
+ ,qMax(qMax(qMax(tlt.y(), trt.y()), blt.y()), brt.y()))
+ ,QPointF(qMax(qMax(qMax(tlt.x(), trt.x()), blt.x()), brt.x())
+ ,qMin(qMin(qMin(tlt.y(), trt.y()), blt.y()), brt.y()))
+ );
+ return QRectF(-1, -1, 2, 2).intersects(boundingRect);
+}
+
+static bool qgeotiledmapscene_isTileInViewport(const QRectF &tileRect, const QMatrix4x4 &matrix, const bool straight)
+{
+ if (straight)
+ return qgeotiledmapscene_isTileInViewport_Straight(tileRect, matrix);
+ return qgeotiledmapscene_isTileInViewport_rotationTilt(tileRect, matrix);
}
void QGeoTiledMapRootNode::updateTiles(QGeoTiledMapTileContainerNode *root,
@@ -533,11 +552,13 @@ void QGeoTiledMapRootNode::updateTiles(QGeoTiledMapTileContainerNode *root,
foreach (const QGeoTileSpec &s, toRemove)
delete root->tiles.take(s);
+ bool straight = !d->isTiltedOrRotated();
for (QHash<QGeoTileSpec, QSGImageNode *>::iterator it = root->tiles.begin();
it != root->tiles.end(); ) {
QSGImageNode *node = it.value();
- bool ok = d->buildGeometry(it.key(), node) && qgeotiledmapscene_isTileInViewport(node->rect(), root->matrix());
+ bool ok = d->buildGeometry(it.key(), node)
+ && qgeotiledmapscene_isTileInViewport(node->rect(), root->matrix(), straight);
QSGNode::DirtyState dirtyBits = 0;
@@ -567,7 +588,8 @@ void QGeoTiledMapRootNode::updateTiles(QGeoTiledMapTileContainerNode *root,
QSGImageNode *tileNode = window->createImageNode();
// note: setTexture will update coordinates so do it here, before we buildGeometry
tileNode->setTexture(textures.value(s));
- if (d->buildGeometry(s, tileNode) && qgeotiledmapscene_isTileInViewport(tileNode->rect(), root->matrix())) {
+ if (d->buildGeometry(s, tileNode)
+ && qgeotiledmapscene_isTileInViewport(tileNode->rect(), root->matrix(), straight)) {
if (tileNode->texture()->textureSize().width() > d->m_tileSize) {
tileNode->setFiltering(QSGTexture::Linear); // with mipmapping QSGTexture::Nearest generates artifacts
tileNode->setMipmapFiltering(QSGTexture::Linear);
@@ -596,7 +618,7 @@ QSGNode *QGeoTiledMapScene::updateSceneGraph(QSGNode *oldNode, QQuickWindow *win
mapRoot = new QGeoTiledMapRootNode();
// Setting clip rect to fullscreen, as now the map can never be smaller than the viewport.
- mapRoot->setClipRect(QRect(0, 0, d->m_screenWidth, d->m_screenHeight));
+ mapRoot->setClipRect(QRect(0, 0, w, h));
QMatrix4x4 itemSpaceMatrix;
itemSpaceMatrix.scale(w / 2, h / 2);