| /**************************************************************************** |
| ** |
| ** 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 "qgeotiledmapscene_p_p.h" |
| #include "qgeocameradata_p.h" |
| #include "qabstractgeotilecache_p.h" |
| #include "qgeotilespec_p.h" |
| #include <QtPositioning/private/qdoublevector3d_p.h> |
| #include <QtPositioning/private/qwebmercator_p.h> |
| #include <QtCore/private/qobject_p.h> |
| #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 |
| |
| QGeoTiledMapScene::QGeoTiledMapScene(QObject *parent) |
| : QObject(*new QGeoTiledMapScenePrivate(),parent) |
| { |
| } |
| |
| QGeoTiledMapScene::~QGeoTiledMapScene() |
| { |
| } |
| |
| void QGeoTiledMapScene::setScreenSize(const QSize &size) |
| { |
| Q_D(QGeoTiledMapScene); |
| d->m_screenSize = size; |
| } |
| |
| void QGeoTiledMapScene::updateSceneParameters() |
| { |
| Q_D(QGeoTiledMapScene); |
| d->m_intZoomLevel = static_cast<int>(std::floor(d->m_cameraData.zoomLevel())); |
| const float delta = d->m_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, d->m_cameraData.zoomLevel()) * d->m_tileSize; |
| } |
| |
| void QGeoTiledMapScene::setTileSize(int tileSize) |
| { |
| Q_D(QGeoTiledMapScene); |
| if (d->m_tileSize == tileSize) |
| return; |
| |
| d->m_tileSize = tileSize; |
| updateSceneParameters(); |
| } |
| |
| void QGeoTiledMapScene::setCameraData(const QGeoCameraData &cameraData) |
| { |
| Q_D(QGeoTiledMapScene); |
| d->m_cameraData = cameraData; |
| updateSceneParameters(); |
| } |
| |
| void QGeoTiledMapScene::setVisibleArea(const QRectF &visibleArea) |
| { |
| Q_D(QGeoTiledMapScene); |
| if (d->m_visibleArea == visibleArea) |
| return; |
| d->m_visibleArea = visibleArea; |
| updateSceneParameters(); |
| } |
| |
| 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); |
| } |
| |
| QSet<QGeoTileSpec> QGeoTiledMapScene::texturedTiles() |
| { |
| Q_D(QGeoTiledMapScene); |
| QSet<QGeoTileSpec> textured; |
| for (auto it = d->m_textures.cbegin(); it != d->m_textures.cend(); ++it) |
| textured += it.value()->spec; |
| |
| return textured; |
| } |
| |
| void QGeoTiledMapScene::clearTexturedTiles() |
| { |
| Q_D(QGeoTiledMapScene); |
| d->m_textures.clear(); |
| d->m_dropTextures = true; |
| } |
| |
| QGeoTiledMapScenePrivate::QGeoTiledMapScenePrivate() |
| : QObjectPrivate(), |
| m_tileSize(0), |
| #ifdef QT_LOCATION_DEBUG |
| m_scaleFactor(1.0), |
| #else |
| m_scaleFactor(10.0), |
| #endif |
| m_intZoomLevel(0), |
| m_sideLength(0), |
| m_minTileX(-1), |
| m_minTileY(-1), |
| m_maxTileX(-1), |
| m_maxTileY(-1), |
| m_tileXWrapsBelow(0), |
| m_linearScaling(false), |
| m_dropTextures(false) |
| { |
| } |
| |
| QGeoTiledMapScenePrivate::~QGeoTiledMapScenePrivate() |
| { |
| } |
| |
| bool QGeoTiledMapScenePrivate::buildGeometry(const QGeoTileSpec &spec, QSGImageNode *imageNode, bool &overzooming) |
| { |
| overzooming = false; |
| 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; |
| |
| imageNode->setRect(QRectF(QPointF(x1, y2), QPointF(x2, y1))); |
| imageNode->setTextureCoordinatesTransform(QSGImageNode::MirrorVertically); |
| |
| // Calculate the texture mapping, in case we are magnifying some lower ZL tile |
| const auto it = m_textures.find(spec); // This should be always found, but apparently sometimes it isn't, possibly due to memory shortage |
| if (it != m_textures.end()) { |
| if (it.value()->spec.zoom() < spec.zoom()) { |
| // Currently only using lower ZL tiles for the overzoom. |
| const int tilesPerTexture = 1 << (spec.zoom() - it.value()->spec.zoom()); |
| const int mappedSize = imageNode->texture()->textureSize().width() / tilesPerTexture; |
| const int x = (spec.x() % tilesPerTexture) * mappedSize; |
| const int y = (spec.y() % tilesPerTexture) * mappedSize; |
| imageNode->setSourceRect(QRectF(x, y, mappedSize, mappedSize)); |
| overzooming = true; |
| } else { |
| imageNode->setSourceRect(QRectF(QPointF(0,0), imageNode->texture()->textureSize())); |
| } |
| } else { |
| qWarning() << "!! buildGeometry: tileSpec not present in m_textures !!"; |
| imageNode->setSourceRect(QRectF(QPointF(0,0), imageNode->texture()->textureSize())); |
| } |
| |
| 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; |
| |
| if (m_textures.contains(spec)) |
| m_updatedTextures.append(spec); |
| m_textures.insert(spec, texture); |
| } |
| |
| void QGeoTiledMapScenePrivate::setVisibleTiles(const QSet<QGeoTileSpec> &visibleTiles) |
| { |
| // work out the tile bounds for the new scene |
| updateTileBounds(visibleTiles); |
| |
| // set up the gl camera for the new scene |
| setupCamera(); |
| |
| QSet<QGeoTileSpec> toRemove = m_visibleTiles - visibleTiles; |
| if (!toRemove.isEmpty()) |
| removeTiles(toRemove); |
| |
| m_visibleTiles = visibleTiles; |
| } |
| |
| 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::updateTileBounds(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() |
| { |
| // NOTE: The following instruction is correct only because WebMercator is a square projection! |
| double f = m_screenSize.height(); |
| |
| // 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; |
| |
| // calculate altitude 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); |
| |
| // 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); |
| |
| // wrap the center if necessary (due to dateline crossing) |
| if (center.x() < m_tileXWrapsBelow) |
| center.setX(center.x() + 1.0 * m_sideLength); |
| |
| // 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()); |
| |
| // 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 / apertureSize); |
| |
| // 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. |
| // 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; |
| 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); |
| // up = mRoll * up; |
| |
| // near plane and far plane |
| |
| double nearPlane = 1.0; |
| // 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 * apertureSize; |
| float halfHeight = 1 * apertureSize; |
| halfWidth *= aspectRatio; |
| |
| // m_projectionMatrix.setToIdentity(); |
| // m_projectionMatrix.frustum(-halfWidth, halfWidth, -halfHeight, halfHeight, nearPlane, farPlane); |
| |
| QRectF va = m_visibleArea; |
| if (va.isNull()) |
| va = QRectF(0, 0, m_screenSize.width(), m_screenSize.height()); |
| |
| QRectF screen = QRectF(QPointF(0,0),m_screenSize); |
| QPointF vaCenter = va.center(); |
| |
| QPointF screenCenter = screen.center(); |
| QPointF diff = screenCenter - vaCenter; |
| float xdiffpct = diff.x() / m_screenSize.width(); |
| float ydiffpct = -(diff.y() / m_screenSize.height()); |
| |
| m_projectionMatrix.setToIdentity(); |
| float l = -halfWidth + (2 * halfWidth) * xdiffpct; |
| float r = halfWidth + (2 * halfWidth) * xdiffpct; |
| float t = halfHeight + (2 * halfHeight) * ydiffpct; |
| float b = -halfHeight + (2 * halfHeight) * ydiffpct; |
| |
| m_projectionMatrix.frustum(l, |
| r, |
| b, |
| t, |
| nearPlane, farPlane); |
| } |
| |
| 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 bool qgeotiledmapscene_isTileInViewport_rotationTilt(const QRectF &tileRect, const QMatrix4x4 &matrix) |
| { |
| // 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, |
| QGeoTiledMapScenePrivate *d, |
| double camAdjust, |
| QQuickWindow *window, |
| bool ogl) |
| { |
| // 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; |
| for (auto it = root->tiles.cbegin(), end = root->tiles.cend(); it != end; ++it) |
| tilesInSG.insert(it.key()); |
| const QSet<QGeoTileSpec> toRemove = tilesInSG - d->m_visibleTiles; |
| const QSet<QGeoTileSpec> toAdd = d->m_visibleTiles - tilesInSG; |
| |
| for (const QGeoTileSpec &s : toRemove) |
| delete root->tiles.take(s); |
| bool straight = !d->isTiltedOrRotated(); |
| bool overzooming; |
| qreal pixelRatio = window->effectiveDevicePixelRatio(); |
| #ifdef QT_LOCATION_DEBUG |
| QList<QGeoTileSpec> droppedTiles; |
| #endif |
| for (QHash<QGeoTileSpec, QSGImageNode *>::iterator it = root->tiles.begin(); |
| it != root->tiles.end(); ) { |
| QSGImageNode *node = it.value(); |
| bool ok = d->buildGeometry(it.key(), node, overzooming) |
| && qgeotiledmapscene_isTileInViewport(node->rect(), root->matrix(), straight); |
| |
| QSGNode::DirtyState dirtyBits = 0; |
| |
| if (!ok) { |
| #ifdef QT_LOCATION_DEBUG |
| droppedTiles.append(it.key()); |
| #endif |
| it = root->tiles.erase(it); |
| delete node; |
| } else { |
| if (isTextureLinear != d->m_linearScaling) { |
| if (node->texture()->textureSize().width() > d->m_tileSize * pixelRatio) { |
| node->setFiltering(QSGTexture::Linear); // With mipmapping QSGTexture::Nearest generates artifacts |
| node->setMipmapFiltering(QSGTexture::Linear); |
| } else { |
| node->setFiltering((d->m_linearScaling || overzooming) ? QSGTexture::Linear : QSGTexture::Nearest); |
| } |
| #if QT_CONFIG(opengl) |
| if (ogl) |
| static_cast<QSGDefaultImageNode *>(node)->setAnisotropyLevel(QSGTexture::Anisotropy16x); |
| #else |
| Q_UNUSED(ogl); |
| #endif |
| dirtyBits |= QSGNode::DirtyMaterial; |
| } |
| if (dirtyBits != 0) |
| node->markDirty(dirtyBits); |
| it++; |
| } |
| } |
| |
| for (const QGeoTileSpec &s : toAdd) { |
| QGeoTileTexture *tileTexture = d->m_textures.value(s).data(); |
| if (!tileTexture || tileTexture->image.isNull()) { |
| #ifdef QT_LOCATION_DEBUG |
| droppedTiles.append(s); |
| #endif |
| continue; |
| } |
| 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, overzooming) |
| && qgeotiledmapscene_isTileInViewport(tileNode->rect(), root->matrix(), straight)) { |
| if (tileNode->texture()->textureSize().width() > d->m_tileSize * pixelRatio) { |
| tileNode->setFiltering(QSGTexture::Linear); // with mipmapping QSGTexture::Nearest generates artifacts |
| tileNode->setMipmapFiltering(QSGTexture::Linear); |
| } else { |
| tileNode->setFiltering((d->m_linearScaling || overzooming) ? QSGTexture::Linear : QSGTexture::Nearest); |
| } |
| #if QT_CONFIG(opengl) |
| if (ogl) |
| static_cast<QSGDefaultImageNode *>(tileNode)->setAnisotropyLevel(QSGTexture::Anisotropy16x); |
| #endif |
| root->addChild(s, tileNode); |
| } else { |
| #ifdef QT_LOCATION_DEBUG |
| droppedTiles.append(s); |
| #endif |
| delete tileNode; |
| } |
| } |
| |
| #ifdef QT_LOCATION_DEBUG |
| m_droppedTiles[camAdjust] = droppedTiles; |
| #endif |
| } |
| |
| 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; |
| } |
| |
| bool isOpenGL = (window->rendererInterface()->graphicsApi() == QSGRendererInterface::OpenGL); |
| QGeoTiledMapRootNode *mapRoot = static_cast<QGeoTiledMapRootNode *>(oldNode); |
| if (!mapRoot) |
| mapRoot = new QGeoTiledMapRootNode(); |
| |
| #ifdef QT_LOCATION_DEBUG |
| mapRoot->m_droppedTiles.clear(); |
| d->m_mapRoot = mapRoot; |
| #endif |
| |
| // Setting clip rect to fullscreen, as now the map can never be smaller than the viewport. |
| mapRoot->setClipRect(QRect(0, 0, w, h)); |
| |
| QMatrix4x4 itemSpaceMatrix; |
| itemSpaceMatrix.scale(w / 2, h / 2); |
| itemSpaceMatrix.translate(1, 1); |
| itemSpaceMatrix.scale(1, -1); |
| mapRoot->root->setMatrix(itemSpaceMatrix); |
| |
| if (d->m_dropTextures) { |
| for (const QGeoTileSpec &s : mapRoot->tiles->tiles.keys()) |
| delete mapRoot->tiles->tiles.take(s); |
| for (const QGeoTileSpec &s : mapRoot->wrapLeft->tiles.keys()) |
| delete mapRoot->wrapLeft->tiles.take(s); |
| for (const QGeoTileSpec &s : mapRoot->wrapRight->tiles.keys()) |
| delete mapRoot->wrapRight->tiles.take(s); |
| for (const QGeoTileSpec &spec : mapRoot->textures.keys()) |
| mapRoot->textures.take(spec)->deleteLater(); |
| d->m_dropTextures = false; |
| } |
| |
| // Evicting loZL tiles temporarily used in place of hiZL ones |
| if (d->m_updatedTextures.size()) { |
| const QVector<QGeoTileSpec> &toRemove = d->m_updatedTextures; |
| for (const QGeoTileSpec &s : toRemove) { |
| if (mapRoot->tiles->tiles.contains(s)) |
| delete mapRoot->tiles->tiles.take(s); |
| |
| if (mapRoot->wrapLeft->tiles.contains(s)) |
| delete mapRoot->wrapLeft->tiles.take(s); |
| |
| if (mapRoot->wrapRight->tiles.contains(s)) |
| delete mapRoot->wrapRight->tiles.take(s); |
| |
| if (mapRoot->textures.contains(s)) |
| mapRoot->textures.take(s)->deleteLater(); |
| } |
| d->m_updatedTextures.clear(); |
| } |
| |
| QSet<QGeoTileSpec> textures; |
| for (auto it = mapRoot->textures.cbegin(), end = mapRoot->textures.cend(); it != end; ++it) |
| textures.insert(it.key()); |
| const QSet<QGeoTileSpec> toRemove = textures - d->m_visibleTiles; |
| const QSet<QGeoTileSpec> toAdd = d->m_visibleTiles - textures; |
| |
| for (const QGeoTileSpec &spec : toRemove) |
| mapRoot->textures.take(spec)->deleteLater(); |
| for (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; |
| #ifdef QT_LOCATION_DEBUG |
| d->m_sideLengthPixel = sideLength; |
| #endif |
| mapRoot->updateTiles(mapRoot->tiles, d, 0, window, isOpenGL); |
| mapRoot->updateTiles(mapRoot->wrapLeft, d, +sideLength, window, isOpenGL); |
| mapRoot->updateTiles(mapRoot->wrapRight, d, -sideLength, window, isOpenGL); |
| |
| mapRoot->isTextureLinear = d->m_linearScaling; |
| |
| return mapRoot; |
| } |
| |
| QT_END_NAMESPACE |