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
52static QVector3D toVector3D(const QDoubleVector3D& in)
53{
54 return QVector3D(in.x(), in.y(), in.z());
55}
56
57QT_BEGIN_NAMESPACE
58
59QGeoTiledMapScene::QGeoTiledMapScene(QObject *parent)
60 : QObject(*new QGeoTiledMapScenePrivate(),parent)
61{
62}
63
64QGeoTiledMapScene::~QGeoTiledMapScene()
65{
66}
67
68void QGeoTiledMapScene::setScreenSize(const QSize &size)
69{
70 Q_D(QGeoTiledMapScene);
71 d->m_screenSize = size;
72}
73
74void QGeoTiledMapScene::updateSceneParameters()
75{
76 Q_D(QGeoTiledMapScene);
77 d->m_intZoomLevel = static_cast<int>(std::floor(x: d->m_cameraData.zoomLevel()));
78 const float delta = d->m_cameraData.zoomLevel() - d->m_intZoomLevel;
79 d->m_linearScaling = qAbs(t: delta) > 0.05 || d->isTiltedOrRotated();
80 d->m_sideLength = 1 << d->m_intZoomLevel;
81 d->m_mapEdgeSize = std::pow(x: 2.0, y: d->m_cameraData.zoomLevel()) * d->m_tileSize;
82}
83
84void 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
94void QGeoTiledMapScene::setCameraData(const QGeoCameraData &cameraData)
95{
96 Q_D(QGeoTiledMapScene);
97 d->m_cameraData = cameraData;
98 updateSceneParameters();
99}
100
101void 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
110void QGeoTiledMapScene::setVisibleTiles(const QSet<QGeoTileSpec> &tiles)
111{
112 Q_D(QGeoTiledMapScene);
113 d->setVisibleTiles(tiles);
114}
115
116const QSet<QGeoTileSpec> &QGeoTiledMapScene::visibleTiles() const
117{
118 Q_D(const QGeoTiledMapScene);
119 return d->m_visibleTiles;
120}
121
122void QGeoTiledMapScene::addTile(const QGeoTileSpec &spec, QSharedPointer<QGeoTileTexture> texture)
123{
124 Q_D(QGeoTiledMapScene);
125 d->addTile(spec, texture);
126}
127
128QSet<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
138void QGeoTiledMapScene::clearTexturedTiles()
139{
140 Q_D(QGeoTiledMapScene);
141 d->m_textures.clear();
142 d->m_dropTextures = true;
143}
144
145QGeoTiledMapScenePrivate::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
165QGeoTiledMapScenePrivate::~QGeoTiledMapScenePrivate()
166{
167}
168
169bool 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(akey: 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
223void QGeoTiledMapScenePrivate::addTile(const QGeoTileSpec &spec, QSharedPointer<QGeoTileTexture> texture)
224{
225 if (!m_visibleTiles.contains(value: spec)) // Don't add the geometry if it isn't visible
226 return;
227
228 if (m_textures.contains(akey: spec))
229 m_updatedTextures.append(t: spec);
230 m_textures.insert(akey: spec, avalue: texture);
231}
232
233void QGeoTiledMapScenePrivate::setVisibleTiles(const QSet<QGeoTileSpec> &visibleTiles)
234{
235 // work out the tile bounds for the new scene
236 updateTileBounds(tiles: 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(oldTiles: toRemove);
244
245 m_visibleTiles = visibleTiles;
246}
247
248void 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(akey: tile);
257 }
258}
259
260void 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(a: m_minTileX, b: x);
334 m_maxTileX = qMax(a: m_maxTileX, b: x);
335 m_minTileY = qMin(a: m_minTileY, b: tile.y());
336 m_maxTileY = qMax(a: m_maxTileY, b: tile.y());
337 }
338}
339
340void 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(x: 2.0, y: 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(coord: 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(x: QLocationUtils::radians(degrees: 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(v1: view, v2: QDoubleVector3D(0.0, 1.0, 0.0));
383 QDoubleVector3D up = QDoubleVector3D::normal(v1: side, v2: 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(angle: -1.0 * m_cameraData.bearing(), vector: view);
390 up = mBearing * up;
391
392 QDoubleVector3D side2 = QDoubleVector3D::normal(v1: up, v2: view);
393 if (m_cameraData.tilt() > 0.01) {
394 QDoubleMatrix4x4 mTilt;
395 mTilt.rotate(angle: m_cameraData.tilt(), vector: side2);
396 eye = mTilt * view + center;
397 }
398
399 view = eye - center;
400 view.normalize();
401 side = QDoubleVector3D::normal(v1: view, v2: QDoubleVector3D(0.0, 1.0, 0.0));
402 up = QDoubleVector3D::normal(v1: view, v2: 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(left: l,
448 right: r,
449 bottom: b,
450 top: t,
451 nearPlane, farPlane);
452}
453
454static 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(r: boundingRect);
458}
459
460static 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(a: qMin(a: qMin(a: tlt.x(), b: trt.x()), b: blt.x()), b: brt.x())
469 ,qMax(a: qMax(a: qMax(a: tlt.y(), b: trt.y()), b: blt.y()), b: brt.y()))
470 ,QPointF(qMax(a: qMax(a: qMax(a: tlt.x(), b: trt.x()), b: blt.x()), b: brt.x())
471 ,qMin(a: qMin(a: qMin(a: tlt.y(), b: trt.y()), b: blt.y()), b: brt.y()))
472 );
473 return QRectF(-1, -1, 2, 2).intersects(r: boundingRect);
474}
475
476static 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
483void 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(eye: toVector3D(in: eye), center: toVector3D(in: center), up: toVector3D(in: 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(value: 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(akey: 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(spec: it.key(), imageNode: node, overzooming)
516 && qgeotiledmapscene_isTileInViewport(tileRect: node->rect(), matrix: 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(bits: dirtyBits);
544 it++;
545 }
546 }
547
548 for (const QGeoTileSpec &s : toAdd) {
549 QGeoTileTexture *tileTexture = d->m_textures.value(akey: 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(akey: s));
559 if (d->buildGeometry(spec: s, imageNode: tileNode, overzooming)
560 && qgeotiledmapscene_isTileInViewport(tileRect: tileNode->rect(), matrix: 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(spec: s, node: 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
585QSGNode *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(x: w / 2, y: h / 2);
610 itemSpaceMatrix.translate(x: 1, y: 1);
611 itemSpaceMatrix.scale(x: 1, y: -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(akey: s);
617 for (const QGeoTileSpec &s : mapRoot->wrapLeft->tiles.keys())
618 delete mapRoot->wrapLeft->tiles.take(akey: s);
619 for (const QGeoTileSpec &s : mapRoot->wrapRight->tiles.keys())
620 delete mapRoot->wrapRight->tiles.take(akey: s);
621 for (const QGeoTileSpec &spec : mapRoot->textures.keys())
622 mapRoot->textures.take(akey: 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(akey: s))
631 delete mapRoot->tiles->tiles.take(akey: s);
632
633 if (mapRoot->wrapLeft->tiles.contains(akey: s))
634 delete mapRoot->wrapLeft->tiles.take(akey: s);
635
636 if (mapRoot->wrapRight->tiles.contains(akey: s))
637 delete mapRoot->wrapRight->tiles.take(akey: s);
638
639 if (mapRoot->textures.contains(akey: s))
640 mapRoot->textures.take(akey: 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(value: 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(akey: spec)->deleteLater();
653 for (const QGeoTileSpec &spec : toAdd) {
654 QGeoTileTexture *tileTexture = d->m_textures.value(akey: spec).data();
655 if (!tileTexture || tileTexture->image.isNull())
656 continue;
657 mapRoot->textures.insert(akey: spec, avalue: window->createTextureFromImage(image: 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(root: mapRoot->tiles, d, camAdjust: 0, window, ogl: isOpenGL);
665 mapRoot->updateTiles(root: mapRoot->wrapLeft, d, camAdjust: +sideLength, window, ogl: isOpenGL);
666 mapRoot->updateTiles(root: mapRoot->wrapRight, d, camAdjust: -sideLength, window, ogl: isOpenGL);
667
668 mapRoot->isTextureLinear = d->m_linearScaling;
669
670 return mapRoot;
671}
672
673QT_END_NAMESPACE
674

source code of qtlocation/src/location/maps/qgeotiledmapscene.cpp