1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Copyright (C) 2014 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
5 ** Contact: http://www.qt.io/licensing/
6 **
7 ** This file is part of the QtLocation module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL3$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see http://www.qt.io/terms-conditions. For further
16 ** information use the contact form at http://www.qt.io/contact-us.
17 **
18 ** GNU Lesser General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU Lesser
20 ** General Public License version 3 as published by the Free Software
21 ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
22 ** packaging of this file. Please review the following information to
23 ** ensure the GNU Lesser General Public License version 3 requirements
24 ** will be met: https://www.gnu.org/licenses/lgpl.html.
25 **
26 ** GNU General Public License Usage
27 ** Alternatively, this file may be used under the terms of the GNU
28 ** General Public License version 2.0 or later as published by the Free
29 ** Software Foundation and appearing in the file LICENSE.GPL included in
30 ** the packaging of this file. Please review the following information to
31 ** ensure the GNU General Public License version 2.0 requirements will be
32 ** met: http://www.gnu.org/licenses/gpl-2.0.html.
33 **
34 ** $QT_END_LICENSE$
35 **
36 ****************************************************************************/
37 #include "qgeotiledmapscene_p.h"
38 #include "qgeotiledmapscene_p_p.h"
39 #include "qgeocameradata_p.h"
40 #include "qabstractgeotilecache_p.h"
41 #include "qgeotilespec_p.h"
42 #include <QtPositioning/private/qdoublevector3d_p.h>
43 #include <QtPositioning/private/qwebmercator_p.h>
44 #include <QtCore/private/qobject_p.h>
45 #include <QtQuick/QQuickWindow>
46 #include <QtGui/QVector3D>
47 #include <cmath>
48 #include <QtPositioning/private/qlocationutils_p.h>
49 #include <QtPositioning/private/qdoublematrix4x4_p.h>
50 #include <QtPositioning/private/qwebmercator_p.h>
51 
toVector3D(const QDoubleVector3D & in)52 static QVector3D toVector3D(const QDoubleVector3D& in)
53 {
54     return QVector3D(in.x(), in.y(), in.z());
55 }
56 
57 QT_BEGIN_NAMESPACE
58 
QGeoTiledMapScene(QObject * parent)59 QGeoTiledMapScene::QGeoTiledMapScene(QObject *parent)
60     : QObject(*new QGeoTiledMapScenePrivate(),parent)
61 {
62 }
63 
~QGeoTiledMapScene()64 QGeoTiledMapScene::~QGeoTiledMapScene()
65 {
66 }
67 
setScreenSize(const QSize & size)68 void QGeoTiledMapScene::setScreenSize(const QSize &size)
69 {
70     Q_D(QGeoTiledMapScene);
71     d->m_screenSize = size;
72 }
73 
updateSceneParameters()74 void QGeoTiledMapScene::updateSceneParameters()
75 {
76     Q_D(QGeoTiledMapScene);
77     d->m_intZoomLevel = static_cast<int>(std::floor(d->m_cameraData.zoomLevel()));
78     const float delta = d->m_cameraData.zoomLevel() - d->m_intZoomLevel;
79     d->m_linearScaling = qAbs(delta) > 0.05 || d->isTiltedOrRotated();
80     d->m_sideLength = 1 << d->m_intZoomLevel;
81     d->m_mapEdgeSize = std::pow(2.0, d->m_cameraData.zoomLevel()) * d->m_tileSize;
82 }
83 
setTileSize(int tileSize)84 void QGeoTiledMapScene::setTileSize(int tileSize)
85 {
86     Q_D(QGeoTiledMapScene);
87     if (d->m_tileSize == tileSize)
88         return;
89 
90     d->m_tileSize = tileSize;
91     updateSceneParameters();
92 }
93 
setCameraData(const QGeoCameraData & cameraData)94 void QGeoTiledMapScene::setCameraData(const QGeoCameraData &cameraData)
95 {
96     Q_D(QGeoTiledMapScene);
97     d->m_cameraData = cameraData;
98     updateSceneParameters();
99 }
100 
setVisibleArea(const QRectF & visibleArea)101 void QGeoTiledMapScene::setVisibleArea(const QRectF &visibleArea)
102 {
103     Q_D(QGeoTiledMapScene);
104     if (d->m_visibleArea == visibleArea)
105         return;
106     d->m_visibleArea = visibleArea;
107     updateSceneParameters();
108 }
109 
setVisibleTiles(const QSet<QGeoTileSpec> & tiles)110 void QGeoTiledMapScene::setVisibleTiles(const QSet<QGeoTileSpec> &tiles)
111 {
112     Q_D(QGeoTiledMapScene);
113     d->setVisibleTiles(tiles);
114 }
115 
visibleTiles() const116 const QSet<QGeoTileSpec> &QGeoTiledMapScene::visibleTiles() const
117 {
118     Q_D(const QGeoTiledMapScene);
119     return d->m_visibleTiles;
120 }
121 
addTile(const QGeoTileSpec & spec,QSharedPointer<QGeoTileTexture> texture)122 void QGeoTiledMapScene::addTile(const QGeoTileSpec &spec, QSharedPointer<QGeoTileTexture> texture)
123 {
124     Q_D(QGeoTiledMapScene);
125     d->addTile(spec, texture);
126 }
127 
texturedTiles()128 QSet<QGeoTileSpec> QGeoTiledMapScene::texturedTiles()
129 {
130     Q_D(QGeoTiledMapScene);
131     QSet<QGeoTileSpec> textured;
132     for (auto it = d->m_textures.cbegin(); it != d->m_textures.cend(); ++it)
133         textured += it.value()->spec;
134 
135     return textured;
136 }
137 
clearTexturedTiles()138 void QGeoTiledMapScene::clearTexturedTiles()
139 {
140     Q_D(QGeoTiledMapScene);
141     d->m_textures.clear();
142     d->m_dropTextures = true;
143 }
144 
QGeoTiledMapScenePrivate()145 QGeoTiledMapScenePrivate::QGeoTiledMapScenePrivate()
146     : QObjectPrivate(),
147       m_tileSize(0),
148 #ifdef QT_LOCATION_DEBUG
149       m_scaleFactor(1.0),
150 #else
151       m_scaleFactor(10.0),
152 #endif
153       m_intZoomLevel(0),
154       m_sideLength(0),
155       m_minTileX(-1),
156       m_minTileY(-1),
157       m_maxTileX(-1),
158       m_maxTileY(-1),
159       m_tileXWrapsBelow(0),
160       m_linearScaling(false),
161       m_dropTextures(false)
162 {
163 }
164 
~QGeoTiledMapScenePrivate()165 QGeoTiledMapScenePrivate::~QGeoTiledMapScenePrivate()
166 {
167 }
168 
buildGeometry(const QGeoTileSpec & spec,QSGImageNode * imageNode,bool & overzooming)169 bool QGeoTiledMapScenePrivate::buildGeometry(const QGeoTileSpec &spec, QSGImageNode *imageNode, bool &overzooming)
170 {
171     overzooming = false;
172     int x = spec.x();
173 
174     if (x < m_tileXWrapsBelow)
175         x += m_sideLength;
176 
177     if ((x < m_minTileX)
178             || (m_maxTileX < x)
179             || (spec.y() < m_minTileY)
180             || (m_maxTileY < spec.y())
181             || (spec.zoom() != m_intZoomLevel)) {
182         return false;
183     }
184 
185     double edge = m_scaleFactor * m_tileSize;
186 
187     double x1 = (x - m_minTileX);
188     double x2 = x1 + 1.0;
189 
190     double y1 = (m_minTileY - spec.y());
191     double y2 = y1 - 1.0;
192 
193     x1 *= edge;
194     x2 *= edge;
195     y1 *= edge;
196     y2 *= edge;
197 
198     imageNode->setRect(QRectF(QPointF(x1, y2), QPointF(x2, y1)));
199     imageNode->setTextureCoordinatesTransform(QSGImageNode::MirrorVertically);
200 
201     // Calculate the texture mapping, in case we are magnifying some lower ZL tile
202     const auto it = m_textures.find(spec); // This should be always found, but apparently sometimes it isn't, possibly due to memory shortage
203     if (it != m_textures.end()) {
204         if (it.value()->spec.zoom() < spec.zoom()) {
205             // Currently only using lower ZL tiles for the overzoom.
206             const int tilesPerTexture = 1 << (spec.zoom() - it.value()->spec.zoom());
207             const int mappedSize = imageNode->texture()->textureSize().width() / tilesPerTexture;
208             const int x = (spec.x() % tilesPerTexture) * mappedSize;
209             const int y = (spec.y() % tilesPerTexture) * mappedSize;
210             imageNode->setSourceRect(QRectF(x, y, mappedSize, mappedSize));
211             overzooming = true;
212         } else {
213             imageNode->setSourceRect(QRectF(QPointF(0,0), imageNode->texture()->textureSize()));
214         }
215     } else {
216         qWarning() << "!! buildGeometry: tileSpec not present in m_textures !!";
217         imageNode->setSourceRect(QRectF(QPointF(0,0), imageNode->texture()->textureSize()));
218     }
219 
220     return true;
221 }
222 
addTile(const QGeoTileSpec & spec,QSharedPointer<QGeoTileTexture> texture)223 void QGeoTiledMapScenePrivate::addTile(const QGeoTileSpec &spec, QSharedPointer<QGeoTileTexture> texture)
224 {
225     if (!m_visibleTiles.contains(spec)) // Don't add the geometry if it isn't visible
226         return;
227 
228     if (m_textures.contains(spec))
229         m_updatedTextures.append(spec);
230     m_textures.insert(spec, texture);
231 }
232 
setVisibleTiles(const QSet<QGeoTileSpec> & visibleTiles)233 void QGeoTiledMapScenePrivate::setVisibleTiles(const QSet<QGeoTileSpec> &visibleTiles)
234 {
235     // work out the tile bounds for the new scene
236     updateTileBounds(visibleTiles);
237 
238     // set up the gl camera for the new scene
239     setupCamera();
240 
241     QSet<QGeoTileSpec> toRemove = m_visibleTiles - visibleTiles;
242     if (!toRemove.isEmpty())
243         removeTiles(toRemove);
244 
245     m_visibleTiles = visibleTiles;
246 }
247 
removeTiles(const QSet<QGeoTileSpec> & oldTiles)248 void QGeoTiledMapScenePrivate::removeTiles(const QSet<QGeoTileSpec> &oldTiles)
249 {
250     typedef QSet<QGeoTileSpec>::const_iterator iter;
251     iter i = oldTiles.constBegin();
252     iter end = oldTiles.constEnd();
253 
254     for (; i != end; ++i) {
255         QGeoTileSpec tile = *i;
256         m_textures.remove(tile);
257     }
258 }
259 
updateTileBounds(const QSet<QGeoTileSpec> & tiles)260 void QGeoTiledMapScenePrivate::updateTileBounds(const QSet<QGeoTileSpec> &tiles)
261 {
262     if (tiles.isEmpty()) {
263         m_minTileX = -1;
264         m_minTileY = -1;
265         m_maxTileX = -1;
266         m_maxTileY = -1;
267         return;
268     }
269 
270     typedef QSet<QGeoTileSpec>::const_iterator iter;
271     iter i = tiles.constBegin();
272     iter end = tiles.constEnd();
273 
274     // determine whether the set of map tiles crosses the dateline.
275     // A gap in the tiles indicates dateline crossing
276     bool hasFarLeft = false;
277     bool hasFarRight = false;
278     bool hasMidLeft = false;
279     bool hasMidRight = false;
280 
281     for (; i != end; ++i) {
282         if ((*i).zoom() != m_intZoomLevel)
283             continue;
284         int x = (*i).x();
285         if (x == 0)
286             hasFarLeft = true;
287         else if (x == (m_sideLength - 1))
288             hasFarRight = true;
289         else if (x == ((m_sideLength / 2) - 1)) {
290             hasMidLeft = true;
291         } else if (x == (m_sideLength / 2)) {
292             hasMidRight = true;
293         }
294     }
295 
296     // if dateline crossing is detected we wrap all x pos of tiles
297     // that are in the left half of the map.
298     m_tileXWrapsBelow = 0;
299 
300     if (hasFarLeft && hasFarRight) {
301         if (!hasMidRight) {
302             m_tileXWrapsBelow = m_sideLength / 2;
303         } else if (!hasMidLeft) {
304             m_tileXWrapsBelow = (m_sideLength / 2) - 1;
305         }
306     }
307 
308     // finally, determine the min and max bounds
309     i = tiles.constBegin();
310 
311     QGeoTileSpec tile = *i;
312 
313     int x = tile.x();
314     if (tile.x() < m_tileXWrapsBelow)
315         x += m_sideLength;
316 
317     m_minTileX = x;
318     m_maxTileX = x;
319     m_minTileY = tile.y();
320     m_maxTileY = tile.y();
321 
322     ++i;
323 
324     for (; i != end; ++i) {
325         tile = *i;
326         if (tile.zoom() != m_intZoomLevel)
327             continue;
328 
329         int x = tile.x();
330         if (tile.x() < m_tileXWrapsBelow)
331             x += m_sideLength;
332 
333         m_minTileX = qMin(m_minTileX, x);
334         m_maxTileX = qMax(m_maxTileX, x);
335         m_minTileY = qMin(m_minTileY, tile.y());
336         m_maxTileY = qMax(m_maxTileY, tile.y());
337     }
338 }
339 
setupCamera()340 void QGeoTiledMapScenePrivate::setupCamera()
341 {
342     // NOTE: The following instruction is correct only because WebMercator is a square projection!
343     double f = m_screenSize.height();
344 
345     // Using fraction of zoom level, z varies between [ m_tileSize , 2 * m_tileSize [
346     double z = std::pow(2.0, m_cameraData.zoomLevel() - m_intZoomLevel) * m_tileSize;
347 
348     // calculate altitude that allows the visible map tiles
349     // to fit in the screen correctly (note that a larger f will cause
350     // the camera be higher, resulting in gray areas displayed around
351     // the tiles)
352     double altitude = f / (2.0 * z);
353 
354     // calculate center
355     double edge = m_scaleFactor * m_tileSize;
356 
357     // first calculate the camera center in map space in the range of 0 <-> sideLength (2^z)
358     QDoubleVector2D camCenterMercator = QWebMercator::coordToMercator(m_cameraData.center());
359     QDoubleVector3D center = (m_sideLength * camCenterMercator);
360 
361     // wrap the center if necessary (due to dateline crossing)
362     if (center.x() < m_tileXWrapsBelow)
363         center.setX(center.x() + 1.0 * m_sideLength);
364 
365     // work out where the camera center is w.r.t minimum tile bounds
366     center.setX(center.x() - 1.0 * m_minTileX);
367     center.setY(1.0 * m_minTileY - center.y());
368 
369     // apply necessary scaling to the camera center
370     center *= edge;
371 
372     // calculate eye
373     double apertureSize = 1.0;
374     if (m_cameraData.fieldOfView() != 90.0) //aperture(90 / 2) = 1
375         apertureSize = tan(QLocationUtils::radians(m_cameraData.fieldOfView()) * 0.5);
376     QDoubleVector3D eye = center;
377     eye.setZ(altitude * edge / apertureSize);
378 
379     // calculate up
380 
381     QDoubleVector3D view = eye - center;
382     QDoubleVector3D side = QDoubleVector3D::normal(view, QDoubleVector3D(0.0, 1.0, 0.0));
383     QDoubleVector3D up = QDoubleVector3D::normal(side, view);
384 
385     // old bearing, tilt and roll code.
386     // Now using double matrices until distilling the transformation to QMatrix4x4
387     QDoubleMatrix4x4 mBearing;
388     // -1.0 * bearing removed, now map north goes in the bearing direction
389     mBearing.rotate(-1.0 * m_cameraData.bearing(), view);
390     up = mBearing * up;
391 
392     QDoubleVector3D side2 = QDoubleVector3D::normal(up, view);
393     if (m_cameraData.tilt() > 0.01) {
394         QDoubleMatrix4x4 mTilt;
395         mTilt.rotate(m_cameraData.tilt(), side2);
396         eye = mTilt * view + center;
397     }
398 
399     view = eye - center;
400     view.normalize();
401     side = QDoubleVector3D::normal(view, QDoubleVector3D(0.0, 1.0, 0.0));
402     up = QDoubleVector3D::normal(view, side2);
403 
404     //    QMatrix4x4 mRoll;
405     //    mRoll.rotate(camera.roll(), view);
406     //    up = mRoll * up;
407 
408     // near plane and far plane
409 
410     double nearPlane = 1.0;
411     // Clip plane. Used to be (altitude + 1.0) * edge. This does not affect the perspective. minimum value would be > 0.0
412     // Since, for some reasons possibly related to how QSG works, this clipping plane is unable to clip part of tiles,
413     // Instead of farPlane =  (altitude + m_cameraData.clipDistance()) * edge , we use a fixed large clipDistance, and
414     // leave the clipping only in QGeoCameraTiles::createFrustum
415     double farPlane =  (altitude + 10000.0) * edge;
416 
417     m_cameraUp = up;
418     m_cameraCenter = center;
419     m_cameraEye = eye;
420 
421     double aspectRatio = 1.0 * m_screenSize.width() / m_screenSize.height();
422     float halfWidth = 1 * apertureSize;
423     float halfHeight = 1 * apertureSize;
424     halfWidth *= aspectRatio;
425 
426 //    m_projectionMatrix.setToIdentity();
427 //    m_projectionMatrix.frustum(-halfWidth, halfWidth, -halfHeight, halfHeight, nearPlane, farPlane);
428 
429     QRectF va = m_visibleArea;
430     if (va.isNull())
431         va = QRectF(0, 0, m_screenSize.width(), m_screenSize.height());
432 
433     QRectF screen = QRectF(QPointF(0,0),m_screenSize);
434     QPointF vaCenter = va.center();
435 
436     QPointF screenCenter = screen.center();
437     QPointF diff = screenCenter - vaCenter;
438     float xdiffpct = diff.x() / m_screenSize.width();
439     float ydiffpct = -(diff.y() / m_screenSize.height());
440 
441     m_projectionMatrix.setToIdentity();
442     float l = -halfWidth + (2 * halfWidth) * xdiffpct;
443     float r =  halfWidth + (2 * halfWidth) * xdiffpct;
444     float t =  halfHeight + (2 * halfHeight) * ydiffpct;
445     float b = -halfHeight + (2 * halfHeight) * ydiffpct;
446 
447     m_projectionMatrix.frustum(l,
448                                r,
449                                b,
450                                t,
451                                nearPlane, farPlane);
452 }
453 
qgeotiledmapscene_isTileInViewport_Straight(const QRectF & tileRect,const QMatrix4x4 & matrix)454 static bool qgeotiledmapscene_isTileInViewport_Straight(const QRectF &tileRect, const QMatrix4x4 &matrix)
455 {
456     const QRectF boundingRect = QRectF(matrix * tileRect.topLeft(), matrix * tileRect.bottomRight());
457     return QRectF(-1, -1, 2, 2).intersects(boundingRect);
458 }
459 
qgeotiledmapscene_isTileInViewport_rotationTilt(const QRectF & tileRect,const QMatrix4x4 & matrix)460 static bool qgeotiledmapscene_isTileInViewport_rotationTilt(const QRectF &tileRect, const QMatrix4x4 &matrix)
461 {
462     // Transformed corners
463     const QPointF tlt = matrix * tileRect.topLeft();
464     const QPointF trt = matrix * tileRect.topRight();
465     const QPointF blt = matrix * tileRect.bottomLeft();
466     const QPointF brt = matrix * tileRect.bottomRight();
467 
468     const QRectF boundingRect = QRectF(QPointF(qMin(qMin(qMin(tlt.x(), trt.x()), blt.x()), brt.x())
469                                               ,qMax(qMax(qMax(tlt.y(), trt.y()), blt.y()), brt.y()))
470                                       ,QPointF(qMax(qMax(qMax(tlt.x(), trt.x()), blt.x()), brt.x())
471                                               ,qMin(qMin(qMin(tlt.y(), trt.y()), blt.y()), brt.y()))
472                                        );
473     return QRectF(-1, -1, 2, 2).intersects(boundingRect);
474 }
475 
qgeotiledmapscene_isTileInViewport(const QRectF & tileRect,const QMatrix4x4 & matrix,const bool straight)476 static bool qgeotiledmapscene_isTileInViewport(const QRectF &tileRect, const QMatrix4x4 &matrix, const bool straight)
477 {
478     if (straight)
479         return qgeotiledmapscene_isTileInViewport_Straight(tileRect, matrix);
480     return qgeotiledmapscene_isTileInViewport_rotationTilt(tileRect, matrix);
481 }
482 
updateTiles(QGeoTiledMapTileContainerNode * root,QGeoTiledMapScenePrivate * d,double camAdjust,QQuickWindow * window,bool ogl)483 void QGeoTiledMapRootNode::updateTiles(QGeoTiledMapTileContainerNode *root,
484                                        QGeoTiledMapScenePrivate *d,
485                                        double camAdjust,
486                                        QQuickWindow *window,
487                                        bool ogl)
488 {
489     // Set up the matrix...
490     QDoubleVector3D eye = d->m_cameraEye;
491     eye.setX(eye.x() + camAdjust);
492     QDoubleVector3D center = d->m_cameraCenter;
493     center.setX(center.x() + camAdjust);
494     QMatrix4x4 cameraMatrix;
495     cameraMatrix.lookAt(toVector3D(eye), toVector3D(center), toVector3D(d->m_cameraUp));
496     root->setMatrix(d->m_projectionMatrix * cameraMatrix);
497 
498     QSet<QGeoTileSpec> tilesInSG;
499     for (auto it = root->tiles.cbegin(), end = root->tiles.cend(); it != end; ++it)
500         tilesInSG.insert(it.key());
501     const QSet<QGeoTileSpec> toRemove = tilesInSG - d->m_visibleTiles;
502     const QSet<QGeoTileSpec> toAdd = d->m_visibleTiles - tilesInSG;
503 
504     for (const QGeoTileSpec &s : toRemove)
505         delete root->tiles.take(s);
506     bool straight = !d->isTiltedOrRotated();
507     bool overzooming;
508     qreal pixelRatio = window->effectiveDevicePixelRatio();
509 #ifdef QT_LOCATION_DEBUG
510     QList<QGeoTileSpec> droppedTiles;
511 #endif
512     for (QHash<QGeoTileSpec, QSGImageNode *>::iterator it = root->tiles.begin();
513          it != root->tiles.end(); ) {
514         QSGImageNode *node = it.value();
515         bool ok = d->buildGeometry(it.key(), node, overzooming)
516                 && qgeotiledmapscene_isTileInViewport(node->rect(), root->matrix(), straight);
517 
518         QSGNode::DirtyState dirtyBits = {};
519 
520         if (!ok) {
521 #ifdef QT_LOCATION_DEBUG
522             droppedTiles.append(it.key());
523 #endif
524             it = root->tiles.erase(it);
525             delete node;
526         } else {
527             if (isTextureLinear != d->m_linearScaling) {
528                 if (node->texture()->textureSize().width() > d->m_tileSize * pixelRatio) {
529                     node->setFiltering(QSGTexture::Linear); // With mipmapping QSGTexture::Nearest generates artifacts
530                     node->setMipmapFiltering(QSGTexture::Linear);
531                 } else {
532                     node->setFiltering((d->m_linearScaling || overzooming) ? QSGTexture::Linear : QSGTexture::Nearest);
533                 }
534 #if QT_CONFIG(opengl)
535                 if (ogl)
536                     static_cast<QSGDefaultImageNode *>(node)->setAnisotropyLevel(QSGTexture::Anisotropy16x);
537 #else
538     Q_UNUSED(ogl);
539 #endif
540                 dirtyBits |= QSGNode::DirtyMaterial;
541             }
542             if (dirtyBits != 0)
543                 node->markDirty(dirtyBits);
544             it++;
545         }
546     }
547 
548     for (const QGeoTileSpec &s : toAdd) {
549         QGeoTileTexture *tileTexture = d->m_textures.value(s).data();
550         if (!tileTexture || tileTexture->image.isNull()) {
551 #ifdef QT_LOCATION_DEBUG
552             droppedTiles.append(s);
553 #endif
554             continue;
555         }
556         QSGImageNode *tileNode = window->createImageNode();
557         // note: setTexture will update coordinates so do it here, before we buildGeometry
558         tileNode->setTexture(textures.value(s));
559         if (d->buildGeometry(s, tileNode, overzooming)
560                 && qgeotiledmapscene_isTileInViewport(tileNode->rect(), root->matrix(), straight)) {
561             if (tileNode->texture()->textureSize().width() > d->m_tileSize * pixelRatio) {
562                 tileNode->setFiltering(QSGTexture::Linear); // with mipmapping QSGTexture::Nearest generates artifacts
563                 tileNode->setMipmapFiltering(QSGTexture::Linear);
564             } else {
565                 tileNode->setFiltering((d->m_linearScaling || overzooming) ? QSGTexture::Linear : QSGTexture::Nearest);
566             }
567 #if QT_CONFIG(opengl)
568             if (ogl)
569                 static_cast<QSGDefaultImageNode *>(tileNode)->setAnisotropyLevel(QSGTexture::Anisotropy16x);
570 #endif
571             root->addChild(s, tileNode);
572         } else {
573 #ifdef QT_LOCATION_DEBUG
574             droppedTiles.append(s);
575 #endif
576             delete tileNode;
577         }
578     }
579 
580 #ifdef QT_LOCATION_DEBUG
581     m_droppedTiles[camAdjust] = droppedTiles;
582 #endif
583 }
584 
updateSceneGraph(QSGNode * oldNode,QQuickWindow * window)585 QSGNode *QGeoTiledMapScene::updateSceneGraph(QSGNode *oldNode, QQuickWindow *window)
586 {
587     Q_D(QGeoTiledMapScene);
588     float w = d->m_screenSize.width();
589     float h = d->m_screenSize.height();
590     if (w <= 0 || h <= 0) {
591         delete oldNode;
592         return 0;
593     }
594 
595     bool isOpenGL = (window->rendererInterface()->graphicsApi() == QSGRendererInterface::OpenGL);
596     QGeoTiledMapRootNode *mapRoot = static_cast<QGeoTiledMapRootNode *>(oldNode);
597     if (!mapRoot)
598         mapRoot = new QGeoTiledMapRootNode();
599 
600 #ifdef QT_LOCATION_DEBUG
601     mapRoot->m_droppedTiles.clear();
602     d->m_mapRoot = mapRoot;
603 #endif
604 
605     // Setting clip rect to fullscreen, as now the map can never be smaller than the viewport.
606     mapRoot->setClipRect(QRect(0, 0, w, h));
607 
608     QMatrix4x4 itemSpaceMatrix;
609     itemSpaceMatrix.scale(w / 2, h / 2);
610     itemSpaceMatrix.translate(1, 1);
611     itemSpaceMatrix.scale(1, -1);
612     mapRoot->root->setMatrix(itemSpaceMatrix);
613 
614     if (d->m_dropTextures) {
615         for (const QGeoTileSpec &s : mapRoot->tiles->tiles.keys())
616             delete mapRoot->tiles->tiles.take(s);
617         for (const QGeoTileSpec &s : mapRoot->wrapLeft->tiles.keys())
618             delete mapRoot->wrapLeft->tiles.take(s);
619         for (const QGeoTileSpec &s : mapRoot->wrapRight->tiles.keys())
620             delete mapRoot->wrapRight->tiles.take(s);
621         for (const QGeoTileSpec &spec : mapRoot->textures.keys())
622             mapRoot->textures.take(spec)->deleteLater();
623         d->m_dropTextures = false;
624     }
625 
626     // Evicting loZL tiles temporarily used in place of hiZL ones
627     if (d->m_updatedTextures.size()) {
628         const QVector<QGeoTileSpec> &toRemove = d->m_updatedTextures;
629         for (const QGeoTileSpec &s : toRemove) {
630             if (mapRoot->tiles->tiles.contains(s))
631                 delete mapRoot->tiles->tiles.take(s);
632 
633             if (mapRoot->wrapLeft->tiles.contains(s))
634                 delete mapRoot->wrapLeft->tiles.take(s);
635 
636             if (mapRoot->wrapRight->tiles.contains(s))
637                 delete mapRoot->wrapRight->tiles.take(s);
638 
639             if (mapRoot->textures.contains(s))
640                 mapRoot->textures.take(s)->deleteLater();
641         }
642         d->m_updatedTextures.clear();
643     }
644 
645     QSet<QGeoTileSpec> textures;
646     for (auto it = mapRoot->textures.cbegin(), end = mapRoot->textures.cend(); it != end; ++it)
647         textures.insert(it.key());
648     const QSet<QGeoTileSpec> toRemove = textures - d->m_visibleTiles;
649     const QSet<QGeoTileSpec> toAdd = d->m_visibleTiles - textures;
650 
651     for (const QGeoTileSpec &spec : toRemove)
652         mapRoot->textures.take(spec)->deleteLater();
653     for (const QGeoTileSpec &spec : toAdd) {
654         QGeoTileTexture *tileTexture = d->m_textures.value(spec).data();
655         if (!tileTexture || tileTexture->image.isNull())
656             continue;
657         mapRoot->textures.insert(spec, window->createTextureFromImage(tileTexture->image));
658     }
659 
660     double sideLength = d->m_scaleFactor * d->m_tileSize * d->m_sideLength;
661 #ifdef QT_LOCATION_DEBUG
662     d->m_sideLengthPixel = sideLength;
663 #endif
664     mapRoot->updateTiles(mapRoot->tiles, d, 0, window, isOpenGL);
665     mapRoot->updateTiles(mapRoot->wrapLeft, d, +sideLength, window, isOpenGL);
666     mapRoot->updateTiles(mapRoot->wrapRight, d, -sideLength, window, isOpenGL);
667 
668     mapRoot->isTextureLinear = d->m_linearScaling;
669 
670     return mapRoot;
671 }
672 
673 QT_END_NAMESPACE
674