summaryrefslogtreecommitdiffstats
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.cpp749
1 files changed, 749 insertions, 0 deletions
diff --git a/src/location/maps/qgeotiledmapscene.cpp b/src/location/maps/qgeotiledmapscene.cpp
new file mode 100644
index 00000000..57750f4d
--- /dev/null
+++ b/src/location/maps/qgeotiledmapscene.cpp
@@ -0,0 +1,749 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2014 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtLocation module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qgeotiledmapscene_p.h"
+#include "qgeocameradata_p.h"
+#include "qabstractgeotilecache_p.h"
+#include "qgeotilespec_p.h"
+#include <QtPositioning/private/qdoublevector3d_p.h>
+#include <QtCore/private/qobject_p.h>
+#include <QtQuick/QSGSimpleTextureNode>
+#include <QtQuick/QQuickWindow>
+#include <cmath>
+
+QT_BEGIN_NAMESPACE
+
+class QGeoTiledMapScenePrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QGeoTiledMapScene)
+public:
+ QGeoTiledMapScenePrivate();
+ ~QGeoTiledMapScenePrivate();
+
+ QSize m_screenSize; // in pixels
+ int m_tileSize; // the pixel resolution for each tile
+ QGeoCameraData m_cameraData;
+ QSet<QGeoTileSpec> m_visibleTiles;
+
+ QDoubleVector3D m_cameraUp;
+ QDoubleVector3D m_cameraEye;
+ QDoubleVector3D m_cameraCenter;
+ QMatrix4x4 m_projectionMatrix;
+
+ // scales up the tile geometry and the camera altitude, resulting in no visible effect
+ // other than to control the accuracy of the render by keeping the values in a sensible range
+ double m_scaleFactor;
+
+ // rounded down, positive zoom is zooming in, corresponding to reduced altitude
+ int m_intZoomLevel;
+
+ // mercatorToGrid transform
+ // the number of tiles in each direction for the whole map (earth) at the current zoom level.
+ // it is 1<<zoomLevel
+ int m_sideLength;
+
+ QHash<QGeoTileSpec, QSharedPointer<QGeoTileTexture> > m_textures;
+
+ // tilesToGrid transform
+ int m_minTileX; // the minimum tile index, i.e. 0 to sideLength which is 1<< zoomLevel
+ int m_minTileY;
+ int m_maxTileX;
+ int m_maxTileY;
+ int m_tileXWrapsBelow; // the wrap point as a tile index
+
+ // cameraToGrid transform
+ double m_mercatorCenterX; // center of camera in grid space (0 to sideLength)
+ double m_mercatorCenterY;
+ double m_mercatorWidth; // width of camera in grid space (0 to sideLength)
+ double m_mercatorHeight;
+
+ // screenToWindow transform
+ double m_screenOffsetX; // in pixels
+ double m_screenOffsetY; // in pixels
+ // cameraToScreen transform
+ double m_screenWidth; // in pixels
+ double m_screenHeight; // in pixels
+
+ bool m_useVerticalLock;
+ bool m_verticalLock;
+ bool m_linearScaling;
+
+ void addTile(const QGeoTileSpec &spec, QSharedPointer<QGeoTileTexture> texture);
+
+ QDoubleVector2D itemPositionToMercator(const QDoubleVector2D &pos) const;
+ QDoubleVector2D mercatorToItemPosition(const QDoubleVector2D &mercator) const;
+
+ void setVisibleTiles(const QSet<QGeoTileSpec> &tiles);
+ void removeTiles(const QSet<QGeoTileSpec> &oldTiles);
+ bool buildGeometry(const QGeoTileSpec &spec, QSGGeometry::TexturedPoint2D *vertices);
+ void setTileBounds(const QSet<QGeoTileSpec> &tiles);
+ void setupCamera();
+};
+
+QGeoTiledMapScene::QGeoTiledMapScene(QObject *parent)
+ : QObject(*new QGeoTiledMapScenePrivate(),parent)
+{
+}
+
+QGeoTiledMapScene::~QGeoTiledMapScene()
+{
+}
+
+void QGeoTiledMapScene::setUseVerticalLock(bool lock)
+{
+ Q_D(QGeoTiledMapScene);
+ d->m_useVerticalLock = lock;
+}
+
+void QGeoTiledMapScene::setScreenSize(const QSize &size)
+{
+ Q_D(QGeoTiledMapScene);
+ d->m_screenSize = size;
+}
+
+void QGeoTiledMapScene::setTileSize(int tileSize)
+{
+ Q_D(QGeoTiledMapScene);
+ d->m_tileSize = tileSize;
+}
+
+void QGeoTiledMapScene::setCameraData(const QGeoCameraData &cameraData)
+{
+ Q_D(QGeoTiledMapScene);
+ d->m_cameraData = cameraData;
+ d->m_intZoomLevel = static_cast<int>(std::floor(d->m_cameraData.zoomLevel()));
+ float delta = cameraData.zoomLevel() - d->m_intZoomLevel;
+ d->m_linearScaling = qAbs(delta) > 0.05;
+ d->m_sideLength = 1 << d->m_intZoomLevel;
+}
+
+void QGeoTiledMapScene::setVisibleTiles(const QSet<QGeoTileSpec> &tiles)
+{
+ Q_D(QGeoTiledMapScene);
+ d->setVisibleTiles(tiles);
+}
+
+const QSet<QGeoTileSpec> &QGeoTiledMapScene::visibleTiles() const
+{
+ Q_D(const QGeoTiledMapScene);
+ return d->m_visibleTiles;
+}
+
+void QGeoTiledMapScene::addTile(const QGeoTileSpec &spec, QSharedPointer<QGeoTileTexture> texture)
+{
+ Q_D(QGeoTiledMapScene);
+ d->addTile(spec, texture);
+}
+
+QDoubleVector2D QGeoTiledMapScene::itemPositionToMercator(const QDoubleVector2D &pos) const
+{
+ Q_D(const QGeoTiledMapScene);
+ return d->itemPositionToMercator(pos);
+}
+
+QDoubleVector2D QGeoTiledMapScene::mercatorToItemPosition(const QDoubleVector2D &mercator) const
+{
+ Q_D(const QGeoTiledMapScene);
+ return d->mercatorToItemPosition(mercator);
+}
+
+bool QGeoTiledMapScene::verticalLock() const
+{
+ Q_D(const QGeoTiledMapScene);
+ return d->m_verticalLock;
+}
+
+QSet<QGeoTileSpec> QGeoTiledMapScene::texturedTiles()
+{
+ Q_D(QGeoTiledMapScene);
+ QSet<QGeoTileSpec> textured;
+ foreach (const QGeoTileSpec &tile, d->m_textures.keys()) {
+ textured += tile;
+ }
+ return textured;
+}
+
+void QGeoTiledMapScene::clearTexturedTiles()
+{
+ Q_D(QGeoTiledMapScene);
+ d->m_textures.clear();
+}
+
+QGeoTiledMapScenePrivate::QGeoTiledMapScenePrivate()
+ : QObjectPrivate(),
+ m_tileSize(0),
+ m_scaleFactor(10.0),
+ m_intZoomLevel(0),
+ m_sideLength(0),
+ m_minTileX(-1),
+ m_minTileY(-1),
+ m_maxTileX(-1),
+ m_maxTileY(-1),
+ m_tileXWrapsBelow(0),
+ m_mercatorCenterX(0.0),
+ m_mercatorCenterY(0.0),
+ m_mercatorWidth(0.0),
+ m_mercatorHeight(0.0),
+ m_screenOffsetX(0.0),
+ m_screenOffsetY(0.0),
+ m_screenWidth(0.0),
+ m_screenHeight(0.0),
+ m_useVerticalLock(false),
+ m_verticalLock(false),
+ m_linearScaling(false)
+{
+}
+
+QGeoTiledMapScenePrivate::~QGeoTiledMapScenePrivate()
+{
+}
+
+QDoubleVector2D QGeoTiledMapScenePrivate::itemPositionToMercator(const QDoubleVector2D &pos) const
+{
+ double x = m_mercatorWidth * (((pos.x() - m_screenOffsetX) / m_screenWidth) - 0.5);
+ x += m_mercatorCenterX;
+
+ if (x > 1.0 * m_sideLength)
+ x -= 1.0 * m_sideLength;
+ if (x < 0.0)
+ x += 1.0 * m_sideLength;
+
+ x /= 1.0 * m_sideLength;
+
+ double y = m_mercatorHeight * (((pos.y() - m_screenOffsetY) / m_screenHeight) - 0.5);
+ y += m_mercatorCenterY;
+ y /= 1.0 * m_sideLength;
+
+ return QDoubleVector2D(x, y);
+}
+
+QDoubleVector2D QGeoTiledMapScenePrivate::mercatorToItemPosition(const QDoubleVector2D &mercator) const
+{
+ double mx = m_sideLength * mercator.x();
+
+ double lb = m_mercatorCenterX - m_mercatorWidth / 2.0;
+ if (lb < 0.0)
+ lb += m_sideLength;
+ double ub = m_mercatorCenterX + m_mercatorWidth / 2.0;
+ if (m_sideLength < ub)
+ ub -= m_sideLength;
+
+ double m = (mx - m_mercatorCenterX) / m_mercatorWidth;
+
+ double mWrapLower = (mx - m_mercatorCenterX - m_sideLength) / m_mercatorWidth;
+ double mWrapUpper = (mx - m_mercatorCenterX + m_sideLength) / m_mercatorWidth;
+
+ // correct for crossing dateline
+ if (qFuzzyCompare(ub - lb + 1.0, 1.0) || (ub < lb) ) {
+ if (m_mercatorCenterX < ub) {
+ if (lb < mx) {
+ m = mWrapLower;
+ }
+ } else if (lb < m_mercatorCenterX) {
+ if (mx <= ub) {
+ m = mWrapUpper;
+ }
+ }
+ }
+
+ // apply wrapping if necessary so we don't return unreasonably large pos/neg screen positions
+ // also allows map items to be drawn properly if some of their coords are out of the screen
+ if ( qAbs(mWrapLower) < qAbs(m) )
+ m = mWrapLower;
+ if ( qAbs(mWrapUpper) < qAbs(m) )
+ m = mWrapUpper;
+
+ double x = m_screenWidth * (0.5 + m);
+ double y = m_screenHeight * (0.5 + (m_sideLength * mercator.y() - m_mercatorCenterY) / m_mercatorHeight);
+
+ return QDoubleVector2D(x + m_screenOffsetX, y + m_screenOffsetY);
+}
+
+bool QGeoTiledMapScenePrivate::buildGeometry(const QGeoTileSpec &spec, QSGGeometry::TexturedPoint2D *vertices)
+{
+ int x = spec.x();
+
+ if (x < m_tileXWrapsBelow)
+ x += m_sideLength;
+
+ if ((x < m_minTileX)
+ || (m_maxTileX < x)
+ || (spec.y() < m_minTileY)
+ || (m_maxTileY < spec.y())
+ || (spec.zoom() != m_intZoomLevel)) {
+ return false;
+ }
+
+ double edge = m_scaleFactor * m_tileSize;
+
+ double x1 = (x - m_minTileX);
+ double x2 = x1 + 1.0;
+
+ double y1 = (m_minTileY - spec.y());
+ double y2 = y1 - 1.0;
+
+ x1 *= edge;
+ x2 *= edge;
+ y1 *= edge;
+ y2 *= edge;
+
+ //Texture coordinate order for veritcal flip of texture
+ vertices[0].set(x1, y1, 0, 0);
+ vertices[1].set(x1, y2, 0, 1);
+ vertices[2].set(x2, y1, 1, 0);
+ vertices[3].set(x2, y2, 1, 1);
+
+ return true;
+}
+
+void QGeoTiledMapScenePrivate::addTile(const QGeoTileSpec &spec, QSharedPointer<QGeoTileTexture> texture)
+{
+ if (!m_visibleTiles.contains(spec)) // Don't add the geometry if it isn't visible
+ return;
+
+ m_textures.insert(spec, texture);
+}
+
+void QGeoTiledMapScenePrivate::setVisibleTiles(const QSet<QGeoTileSpec> &tiles)
+{
+ // work out the tile bounds for the new scene
+ setTileBounds(tiles);
+
+ // set up the gl camera for the new scene
+ setupCamera();
+
+ QSet<QGeoTileSpec> toRemove = m_visibleTiles - tiles;
+ if (!toRemove.isEmpty())
+ removeTiles(toRemove);
+
+ m_visibleTiles = tiles;
+}
+
+void QGeoTiledMapScenePrivate::removeTiles(const QSet<QGeoTileSpec> &oldTiles)
+{
+ typedef QSet<QGeoTileSpec>::const_iterator iter;
+ iter i = oldTiles.constBegin();
+ iter end = oldTiles.constEnd();
+
+ for (; i != end; ++i) {
+ QGeoTileSpec tile = *i;
+ m_textures.remove(tile);
+ }
+}
+
+void QGeoTiledMapScenePrivate::setTileBounds(const QSet<QGeoTileSpec> &tiles)
+{
+ if (tiles.isEmpty()) {
+ m_minTileX = -1;
+ m_minTileY = -1;
+ m_maxTileX = -1;
+ m_maxTileY = -1;
+ return;
+ }
+
+ typedef QSet<QGeoTileSpec>::const_iterator iter;
+ iter i = tiles.constBegin();
+ iter end = tiles.constEnd();
+
+ // determine whether the set of map tiles crosses the dateline.
+ // A gap in the tiles indicates dateline crossing
+ bool hasFarLeft = false;
+ bool hasFarRight = false;
+ bool hasMidLeft = false;
+ bool hasMidRight = false;
+
+ for (; i != end; ++i) {
+ if ((*i).zoom() != m_intZoomLevel)
+ continue;
+ int x = (*i).x();
+ if (x == 0)
+ hasFarLeft = true;
+ else if (x == (m_sideLength - 1))
+ hasFarRight = true;
+ else if (x == ((m_sideLength / 2) - 1)) {
+ hasMidLeft = true;
+ } else if (x == (m_sideLength / 2)) {
+ hasMidRight = true;
+ }
+ }
+
+ // if dateline crossing is detected we wrap all x pos of tiles
+ // that are in the left half of the map.
+ m_tileXWrapsBelow = 0;
+
+ if (hasFarLeft && hasFarRight) {
+ if (!hasMidRight) {
+ m_tileXWrapsBelow = m_sideLength / 2;
+ } else if (!hasMidLeft) {
+ m_tileXWrapsBelow = (m_sideLength / 2) - 1;
+ }
+ }
+
+ // finally, determine the min and max bounds
+ i = tiles.constBegin();
+
+ QGeoTileSpec tile = *i;
+
+ int x = tile.x();
+ if (tile.x() < m_tileXWrapsBelow)
+ x += m_sideLength;
+
+ m_minTileX = x;
+ m_maxTileX = x;
+ m_minTileY = tile.y();
+ m_maxTileY = tile.y();
+
+ ++i;
+
+ for (; i != end; ++i) {
+ tile = *i;
+ if (tile.zoom() != m_intZoomLevel)
+ continue;
+
+ int x = tile.x();
+ if (tile.x() < m_tileXWrapsBelow)
+ x += m_sideLength;
+
+ m_minTileX = qMin(m_minTileX, x);
+ m_maxTileX = qMax(m_maxTileX, x);
+ m_minTileY = qMin(m_minTileY, tile.y());
+ m_maxTileY = qMax(m_maxTileY, tile.y());
+ }
+}
+
+void QGeoTiledMapScenePrivate::setupCamera()
+{
+ double f = 1.0 * qMin(m_screenSize.width(), m_screenSize.height());
+
+ // fraction of zoom level
+ double z = std::pow(2.0, m_cameraData.zoomLevel() - m_intZoomLevel) * m_tileSize;
+
+ // calculate altitdue that allows the visible map tiles
+ // 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) ;
+
+ // mercatorWidth_ and mercatorHeight_ define the ratio for
+ // mercator and screen coordinate conversion,
+ // see mercatorToItemPosition() and itemPositionToMercator()
+ m_mercatorHeight = m_screenSize.height() / z;
+ m_mercatorWidth = m_screenSize.width() / 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)
+ QDoubleVector3D center = (m_sideLength * QGeoProjection::coordToMercator(m_cameraData.center()));
+
+ // wrap the center if necessary (due to dateline crossing)
+ if (center.x() < m_tileXWrapsBelow)
+ center.setX(center.x() + 1.0 * m_sideLength);
+
+ m_mercatorCenterX = center.x();
+ m_mercatorCenterY = center.y();
+
+ // work out where the camera center is w.r.t minimum tile bounds
+ center.setX(center.x() - 1.0 * m_minTileX);
+ center.setY(1.0 * m_minTileY - center.y());
+
+ // letter box vertically
+ if (m_useVerticalLock && (m_mercatorHeight > 1.0 * m_sideLength)) {
+ center.setY(-1.0 * m_sideLength / 2.0);
+ m_mercatorCenterY = m_sideLength / 2.0;
+ m_screenOffsetY = m_screenSize.height() * (0.5 - m_sideLength / (2 * m_mercatorHeight));
+ m_screenHeight = m_screenSize.height() - 2 * m_screenOffsetY;
+ m_mercatorHeight = 1.0 * m_sideLength;
+ m_verticalLock = true;
+ } else {
+ m_screenOffsetY = 0.0;
+ m_screenHeight = m_screenSize.height();
+ m_verticalLock = false;
+ }
+
+ if (m_mercatorWidth > 1.0 * m_sideLength) {
+ m_screenOffsetX = m_screenSize.width() * (0.5 - (m_sideLength / (2 * m_mercatorWidth)));
+ m_screenWidth = m_screenSize.width() - 2 * m_screenOffsetX;
+ m_mercatorWidth = 1.0 * m_sideLength;
+ } else {
+ m_screenOffsetX = 0.0;
+ m_screenWidth = m_screenSize.width();
+ }
+
+ // apply necessary scaling to the camera center
+ center *= edge;
+
+ // calculate eye
+
+ QDoubleVector3D eye = center;
+ eye.setZ(altitude * edge);
+
+ // calculate up
+
+ QDoubleVector3D view = eye - center;
+ 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;
+
+ // view = eye - center;
+ // side = QDoubleVector3D::normal(view, QDoubleVector3D(0.0, 1.0, 0.0));
+ // up = QDoubleVector3D::normal(view, side2);
+
+ // QMatrix4x4 mRoll;
+ // mRoll.rotate(camera.roll(), view);
+ // up = mRoll * up;
+
+ // near plane and far plane
+
+ double nearPlane = 1.0;
+ double farPlane = (altitude + 1.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;
+ if (aspectRatio > 1.0) {
+ halfWidth *= aspectRatio;
+ } else if (aspectRatio > 0.0f && aspectRatio < 1.0f) {
+ halfHeight /= aspectRatio;
+ }
+ m_projectionMatrix.setToIdentity();
+ m_projectionMatrix.frustum(-halfWidth, halfWidth, -halfHeight, halfHeight, nearPlane, farPlane);
+}
+
+class QGeoTiledMapTileContainerNode : public QSGTransformNode
+{
+public:
+ void addChild(const QGeoTileSpec &spec, QSGSimpleTextureNode *node)
+ {
+ tiles.insert(spec, node);
+ appendChildNode(node);
+ }
+ QHash<QGeoTileSpec, QSGSimpleTextureNode *> tiles;
+};
+
+class QGeoTiledMapRootNode : public QSGClipNode
+{
+public:
+ QGeoTiledMapRootNode()
+ : isTextureLinear(false)
+ , geometry(QSGGeometry::defaultAttributes_Point2D(), 4)
+ , root(new QSGTransformNode())
+ , tiles(new QGeoTiledMapTileContainerNode())
+ , wrapLeft(new QGeoTiledMapTileContainerNode())
+ , wrapRight(new QGeoTiledMapTileContainerNode())
+ {
+ setIsRectangular(true);
+ setGeometry(&geometry);
+ root->appendChildNode(tiles);
+ root->appendChildNode(wrapLeft);
+ root->appendChildNode(wrapRight);
+ appendChildNode(root);
+ }
+
+ ~QGeoTiledMapRootNode()
+ {
+ qDeleteAll(textures);
+ }
+
+ void setClipRect(const QRect &rect)
+ {
+ if (rect != clipRect) {
+ QSGGeometry::updateRectGeometry(&geometry, rect);
+ QSGClipNode::setClipRect(rect);
+ clipRect = rect;
+ markDirty(DirtyGeometry);
+ }
+ }
+
+ void updateTiles(QGeoTiledMapTileContainerNode *root, QGeoTiledMapScenePrivate *d, double camAdjust);
+
+ bool isTextureLinear;
+
+ QSGGeometry geometry;
+ QRect clipRect;
+
+ QSGTransformNode *root;
+
+ QGeoTiledMapTileContainerNode *tiles; // The majority of the tiles
+ QGeoTiledMapTileContainerNode *wrapLeft; // When zoomed out, the tiles that wrap around on the left.
+ QGeoTiledMapTileContainerNode *wrapRight; // When zoomed out, the tiles that wrap around on the right
+
+ QHash<QGeoTileSpec, QSGTexture *> textures;
+};
+
+static bool qgeotiledmapscene_isTileInViewport(const QSGGeometry::TexturedPoint2D *tp, const QMatrix4x4 &matrix) {
+ QPolygonF polygon; polygon.reserve(4);
+ for (int i=0; i<4; ++i)
+ polygon << matrix * QPointF(tp[i].x, tp[i].y);
+ return QRectF(-1, -1, 2, 2).intersects(polygon.boundingRect());
+}
+
+static QVector3D toVector3D(const QDoubleVector3D& in)
+{
+ return QVector3D(in.x(), in.y(), in.z());
+}
+
+void QGeoTiledMapRootNode::updateTiles(QGeoTiledMapTileContainerNode *root,
+ QGeoTiledMapScenePrivate *d,
+ double camAdjust)
+{
+ // Set up the matrix...
+ QDoubleVector3D eye = d->m_cameraEye;
+ eye.setX(eye.x() + camAdjust);
+ QDoubleVector3D center = d->m_cameraCenter;
+ center.setX(center.x() + camAdjust);
+ QMatrix4x4 cameraMatrix;
+ cameraMatrix.lookAt(toVector3D(eye), toVector3D(center), toVector3D(d->m_cameraUp));
+ root->setMatrix(d->m_projectionMatrix * cameraMatrix);
+
+ QSet<QGeoTileSpec> tilesInSG = QSet<QGeoTileSpec>::fromList(root->tiles.keys());
+ QSet<QGeoTileSpec> toRemove = tilesInSG - d->m_visibleTiles;
+ QSet<QGeoTileSpec> toAdd = d->m_visibleTiles - tilesInSG;
+
+ foreach (const QGeoTileSpec &s, toRemove)
+ delete root->tiles.take(s);
+
+ for (QHash<QGeoTileSpec, QSGSimpleTextureNode *>::iterator it = root->tiles.begin();
+ it != root->tiles.end(); ) {
+ QSGGeometry visualGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4);
+ QSGGeometry::TexturedPoint2D *v = visualGeometry.vertexDataAsTexturedPoint2D();
+ bool ok = d->buildGeometry(it.key(), v) && qgeotiledmapscene_isTileInViewport(v, root->matrix());
+ QSGSimpleTextureNode *node = it.value();
+ QSGNode::DirtyState dirtyBits = 0;
+
+ // Check and handle changes to vertex data.
+ if (ok && memcmp(node->geometry()->vertexData(), v, 4 * sizeof(QSGGeometry::TexturedPoint2D)) != 0) {
+ if (v[0].x == v[3].x || v[0].y == v[3].y) { // top-left == bottom-right => invalid => remove
+ ok = false;
+ } else {
+ memcpy(node->geometry()->vertexData(), v, 4 * sizeof(QSGGeometry::TexturedPoint2D));
+ dirtyBits |= QSGNode::DirtyGeometry;
+ }
+ }
+
+ if (!ok) {
+ it = root->tiles.erase(it);
+ delete node;
+ } else {
+ if (isTextureLinear != d->m_linearScaling) {
+ node->setFiltering(d->m_linearScaling ? QSGTexture::Linear : QSGTexture::Nearest);
+ dirtyBits |= QSGNode::DirtyMaterial;
+ }
+ if (dirtyBits != 0)
+ node->markDirty(dirtyBits);
+ it++;
+ }
+ }
+
+ foreach (const QGeoTileSpec &s, toAdd) {
+ QGeoTileTexture *tileTexture = d->m_textures.value(s).data();
+ if (!tileTexture || tileTexture->image.isNull())
+ continue;
+ QSGSimpleTextureNode *tileNode = new QSGSimpleTextureNode();
+ // note: setTexture will update coordinates so do it here, before we buildGeometry
+ tileNode->setTexture(textures.value(s));
+ Q_ASSERT(tileNode->geometry());
+ Q_ASSERT(tileNode->geometry()->attributes() == QSGGeometry::defaultAttributes_TexturedPoint2D().attributes);
+ Q_ASSERT(tileNode->geometry()->vertexCount() == 4);
+ if (d->buildGeometry(s, tileNode->geometry()->vertexDataAsTexturedPoint2D())
+ && qgeotiledmapscene_isTileInViewport(tileNode->geometry()->vertexDataAsTexturedPoint2D(), root->matrix())) {
+ tileNode->setFiltering(d->m_linearScaling ? QSGTexture::Linear : QSGTexture::Nearest);
+ root->addChild(s, tileNode);
+ } else {
+ delete tileNode;
+ }
+ }
+}
+
+QSGNode *QGeoTiledMapScene::updateSceneGraph(QSGNode *oldNode, QQuickWindow *window)
+{
+ Q_D(QGeoTiledMapScene);
+ float w = d->m_screenSize.width();
+ float h = d->m_screenSize.height();
+ if (w <= 0 || h <= 0) {
+ delete oldNode;
+ return 0;
+ }
+
+ QGeoTiledMapRootNode *mapRoot = static_cast<QGeoTiledMapRootNode *>(oldNode);
+ if (!mapRoot)
+ mapRoot = new QGeoTiledMapRootNode();
+
+ mapRoot->setClipRect(QRect(d->m_screenOffsetX, d->m_screenOffsetY, d->m_screenWidth, d->m_screenHeight));
+
+ QMatrix4x4 itemSpaceMatrix;
+ itemSpaceMatrix.scale(w / 2, h / 2);
+ itemSpaceMatrix.translate(1, 1);
+ itemSpaceMatrix.scale(1, -1);
+ mapRoot->root->setMatrix(itemSpaceMatrix);
+
+ QSet<QGeoTileSpec> textures = QSet<QGeoTileSpec>::fromList(mapRoot->textures.keys());
+ QSet<QGeoTileSpec> toRemove = textures - d->m_visibleTiles;
+ QSet<QGeoTileSpec> toAdd = d->m_visibleTiles - textures;
+
+ foreach (const QGeoTileSpec &spec, toRemove)
+ mapRoot->textures.take(spec)->deleteLater();
+ foreach (const QGeoTileSpec &spec, toAdd) {
+ QGeoTileTexture *tileTexture = d->m_textures.value(spec).data();
+ if (!tileTexture || tileTexture->image.isNull())
+ continue;
+ mapRoot->textures.insert(spec, window->createTextureFromImage(tileTexture->image));
+ }
+
+ double sideLength = d->m_scaleFactor * d->m_tileSize * d->m_sideLength;
+ mapRoot->updateTiles(mapRoot->tiles, d, 0);
+ mapRoot->updateTiles(mapRoot->wrapLeft, d, +sideLength);
+ mapRoot->updateTiles(mapRoot->wrapRight, d, -sideLength);
+
+ mapRoot->isTextureLinear = d->m_linearScaling;
+
+ return mapRoot;
+}
+
+QT_END_NAMESPACE