Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qgeotiledmap.cpp
Go to the documentation of this file.
1// Copyright (C) 2015 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
10
15#include <cmath>
16
17QT_BEGIN_NAMESPACE
18#define PREFETCH_FRUSTUM_SCALE 2.0
19
20QGeoTiledMap::QGeoTiledMap(QGeoTiledMappingManagerEngine *engine, QObject *parent)
21 : QGeoMap(*new QGeoTiledMapPrivate(engine), parent)
22{
23 Q_D(QGeoTiledMap);
24
25 d->m_tileRequests = new QGeoTileRequestManager(this, engine);
26
27 QObject::connect(engine,&QGeoTiledMappingManagerEngine::tileVersionChanged,
28 this,&QGeoTiledMap::handleTileVersionChanged);
29 QObject::connect(this, &QGeoMap::cameraCapabilitiesChanged, this,
30 [d](const QGeoCameraCapabilities &oldCameraCapabilities) {
31 d->onCameraCapabilitiesChanged(oldCameraCapabilities);
32 });
33}
34
35QGeoTiledMap::QGeoTiledMap(QGeoTiledMapPrivate &dd, QGeoTiledMappingManagerEngine *engine, QObject *parent)
36 : QGeoMap(dd, parent)
37{
38 Q_D(QGeoTiledMap);
39
40 d->m_tileRequests = new QGeoTileRequestManager(this, engine);
41
42 QObject::connect(engine,&QGeoTiledMappingManagerEngine::tileVersionChanged,
43 this,&QGeoTiledMap::handleTileVersionChanged);
44 QObject::connect(this, &QGeoMap::cameraCapabilitiesChanged, this,
45 [d](const QGeoCameraCapabilities &oldCameraCapabilities) {
46 d->onCameraCapabilitiesChanged(oldCameraCapabilities);
47 });
48}
49
50QGeoTiledMap::~QGeoTiledMap()
51{
52 Q_D(QGeoTiledMap);
53 delete d->m_tileRequests;
54 d->m_tileRequests = nullptr;
55
56 if (!d->m_engine.isNull()) {
57 QGeoTiledMappingManagerEngine *engine = qobject_cast<QGeoTiledMappingManagerEngine*>(d->m_engine);
58 Q_ASSERT(engine);
59 engine->releaseMap(this);
60 }
61}
62
63QGeoTileRequestManager *QGeoTiledMap::requestManager()
64{
65 Q_D(QGeoTiledMap);
66 return d->m_tileRequests;
67}
68
69void QGeoTiledMap::updateTile(const QGeoTileSpec &spec)
70{
71 Q_D(QGeoTiledMap);
72 d->updateTile(spec);
73}
74
75void QGeoTiledMap::setPrefetchStyle(QGeoTiledMap::PrefetchStyle style)
76{
77 Q_D(QGeoTiledMap);
78 d->m_prefetchStyle = style;
79}
80
81QAbstractGeoTileCache *QGeoTiledMap::tileCache()
82{
83 Q_D(QGeoTiledMap);
84 return d->m_cache;
85}
86
87QSGNode *QGeoTiledMap::updateSceneGraph(QSGNode *oldNode, QQuickWindow *window)
88{
89 Q_D(QGeoTiledMap);
90 return d->updateSceneGraph(oldNode, window);
91}
92
93void QGeoTiledMap::prefetchData()
94{
95 Q_D(QGeoTiledMap);
96 d->prefetchTiles();
97}
98
99void QGeoTiledMap::clearData()
100{
101 Q_D(QGeoTiledMap);
102 d->m_cache->clearAll();
103 d->m_mapScene->clearTexturedTiles();
104 d->updateScene();
105 sgNodeChanged();
106}
107
108QGeoMap::Capabilities QGeoTiledMap::capabilities() const
109{
110 return Capabilities(SupportsVisibleRegion
111 | SupportsSetBearing
112 | SupportsAnchoringCoordinate
113 | SupportsVisibleArea);
114}
115
116void QGeoTiledMap::setCopyrightVisible(bool visible)
117{
118 Q_D(QGeoTiledMap);
119 if (visible == d->m_copyrightVisible)
120 return;
121
122 QGeoMap::setCopyrightVisible(visible);
123 if (visible)
124 evaluateCopyrights(d->m_mapScene->visibleTiles());
125}
126
127void QGeoTiledMap::clearScene(int mapId)
128{
129 Q_D(QGeoTiledMap);
130 if (activeMapType().mapId() == mapId)
131 d->clearScene();
132}
133
134void QGeoTiledMap::handleTileVersionChanged()
135{
136 Q_D(QGeoTiledMap);
137 if (!d->m_engine.isNull()) {
138 QGeoTiledMappingManagerEngine* engine = qobject_cast<QGeoTiledMappingManagerEngine*>(d->m_engine);
139 Q_ASSERT(engine);
140 d->changeTileVersion(engine->tileVersion());
141 }
142}
143
144void QGeoTiledMap::evaluateCopyrights(const QSet<QGeoTileSpec> &visibleTiles)
145{
146 Q_UNUSED(visibleTiles);
147}
148
149QGeoTiledMapPrivate::QGeoTiledMapPrivate(QGeoTiledMappingManagerEngine *engine)
150 : QGeoMapPrivate(engine, new QGeoProjectionWebMercator),
151 m_cache(engine->tileCache()),
152 m_visibleTiles(new QGeoCameraTiles()),
153 m_prefetchTiles(new QGeoCameraTiles()),
154 m_mapScene(new QGeoTiledMapScene()),
155 m_tileRequests(nullptr),
156 m_maxZoomLevel(static_cast<int>(std::ceil(m_cameraCapabilities.maximumZoomLevel()))),
157 m_minZoomLevel(static_cast<int>(std::ceil(m_cameraCapabilities.minimumZoomLevel()))),
158 m_prefetchStyle(QGeoTiledMap::PrefetchTwoNeighbourLayers)
159{
160 int tileSize = m_cameraCapabilities.tileSize();
161 QString pluginString(engine->managerName() + QLatin1Char('_') + QString::number(engine->managerVersion()));
162 m_visibleTiles->setTileSize(tileSize);
163 m_prefetchTiles->setTileSize(tileSize);
164 m_visibleTiles->setPluginString(pluginString);
165 m_prefetchTiles->setPluginString(pluginString);
166 m_mapScene->setTileSize(tileSize);
167}
168
169QGeoTiledMapPrivate::~QGeoTiledMapPrivate()
170{
171 // controller_ is a child of map_, don't need to delete it here
172
173 delete m_mapScene;
174 delete m_visibleTiles;
175 delete m_prefetchTiles;
176
177 // TODO map items are not deallocated!
178 // However: how to ensure this is done in rendering thread?
179}
180
181void QGeoTiledMapPrivate::prefetchTiles()
182{
183 if (m_tileRequests && m_prefetchStyle != QGeoTiledMap::NoPrefetching) {
184
185 QSet<QGeoTileSpec> tiles;
186 QGeoCameraData camera = m_visibleTiles->cameraData();
187 int currentIntZoom = static_cast<int>(std::floor(camera.zoomLevel()));
188
189 m_prefetchTiles->setCameraData(camera);
190 m_prefetchTiles->setViewExpansion(PREFETCH_FRUSTUM_SCALE);
191 tiles = m_prefetchTiles->createTiles();
192
193 switch (m_prefetchStyle) {
194
195 case QGeoTiledMap::PrefetchNeighbourLayer: {
196 double zoomFraction = camera.zoomLevel() - currentIntZoom;
197 int nearestNeighbourLayer = zoomFraction > 0.5 ? currentIntZoom + 1 : currentIntZoom - 1;
198 if (nearestNeighbourLayer <= m_maxZoomLevel && nearestNeighbourLayer >= m_minZoomLevel) {
199 camera.setZoomLevel(nearestNeighbourLayer);
200 // Approx heuristic, keeping total # prefetched tiles roughly independent of the
201 // fractional zoom level.
202 double neighbourScale = (1.0 + zoomFraction)/2.0;
203 m_prefetchTiles->setCameraData(camera);
204 m_prefetchTiles->setViewExpansion(PREFETCH_FRUSTUM_SCALE * neighbourScale);
205 tiles += m_prefetchTiles->createTiles();
206 }
207 }
208 break;
209
210 case QGeoTiledMap::PrefetchTwoNeighbourLayers: {
211 // This is a simpler strategy, we just prefetch from layer above and below
212 // for the layer below we only use half the size as this fills the screen
213 if (currentIntZoom > m_minZoomLevel) {
214 camera.setZoomLevel(currentIntZoom - 1);
215 m_prefetchTiles->setCameraData(camera);
216 m_prefetchTiles->setViewExpansion(0.5);
217 tiles += m_prefetchTiles->createTiles();
218 }
219
220 if (currentIntZoom < m_maxZoomLevel) {
221 camera.setZoomLevel(currentIntZoom + 1);
222 m_prefetchTiles->setCameraData(camera);
223 m_prefetchTiles->setViewExpansion(1.0);
224 tiles += m_prefetchTiles->createTiles();
225 }
226 }
227 break;
228
229 default:
230 break;
231 }
232
233 m_tileRequests->requestTiles(tiles - m_mapScene->texturedTiles());
234 }
235}
236
237QGeoMapType QGeoTiledMapPrivate::activeMapType() const
238{
239 return m_visibleTiles->activeMapType();
240}
241
242// Called before changeCameraData
243void QGeoTiledMapPrivate::onCameraCapabilitiesChanged(const QGeoCameraCapabilities &oldCameraCapabilities)
244{
245 // Handle varying min/maxZoomLevel
246 if (oldCameraCapabilities.minimumZoomLevel() != m_cameraCapabilities.minimumZoomLevel())
247 m_minZoomLevel = static_cast<int>(std::ceil(m_cameraCapabilities.minimumZoomLevel()));
248 if (oldCameraCapabilities.maximumZoomLevel() != m_cameraCapabilities.maximumZoomLevel())
249 m_maxZoomLevel = static_cast<int>(std::ceil(m_cameraCapabilities.maximumZoomLevel()));
250
251 // Handle varying tile size
252 if (oldCameraCapabilities.tileSize() != m_cameraCapabilities.tileSize()) {
253 m_visibleTiles->setTileSize(oldCameraCapabilities.tileSize());
254 m_prefetchTiles->setTileSize(oldCameraCapabilities.tileSize());
255 m_mapScene->setTileSize(oldCameraCapabilities.tileSize());
256 }
257}
258
259void QGeoTiledMapPrivate::changeCameraData(const QGeoCameraData &cameraData)
260{
261 Q_Q(QGeoTiledMap);
262
263 QGeoCameraData cam = cameraData;
264
265 // The incoming zoom level is intended for a tileSize of 256.
266 // Adapt it to the current tileSize
267 double zoomLevel = cameraData.zoomLevel();
268 if (m_visibleTiles->tileSize() != 256)
269 zoomLevel = std::log(std::pow(2.0, zoomLevel) * 256.0 / m_visibleTiles->tileSize()) * (1.0 / std::log(2.0));
270 cam.setZoomLevel(zoomLevel);
271
272 // For zoomlevel, "snap" 0.01 either side of a whole number.
273 // This is so that when we turn off bilinear scaling, we're
274 // snapped to the exact pixel size of the tiles
275 int izl = static_cast<int>(std::floor(cam.zoomLevel()));
276 float delta = cam.zoomLevel() - izl;
277
278 if (delta > 0.5) {
279 izl++;
280 delta -= 1.0;
281 }
282
283 // TODO: Don't do this if there's tilt or bearing.
284 if (qAbs(delta) < 0.01) {
285 cam.setZoomLevel(izl);
286 }
287
288 m_visibleTiles->setCameraData(cam);
289 m_mapScene->setCameraData(cam);
290
291 updateScene();
292 q->sgNodeChanged(); // ToDo: explain why emitting twice
293}
294
295void QGeoTiledMapPrivate::updateScene()
296{
297 Q_Q(QGeoTiledMap);
298 // detect if new tiles introduced
299 const QSet<QGeoTileSpec>& tiles = m_visibleTiles->createTiles();
300 bool newTilesIntroduced = !m_mapScene->visibleTiles().contains(tiles);
301 m_mapScene->setVisibleTiles(tiles);
302
303 if (newTilesIntroduced && m_copyrightVisible)
304 q->evaluateCopyrights(tiles);
305
306 // don't request tiles that are already built and textured
307 QMap<QGeoTileSpec, QSharedPointer<QGeoTileTexture> > cachedTiles =
308 m_tileRequests->requestTiles(m_visibleTiles->createTiles() - m_mapScene->texturedTiles());
309
310 for (auto it = cachedTiles.cbegin(); it != cachedTiles.cend(); ++it)
311 m_mapScene->addTile(it.key(), it.value());
312
313 if (!cachedTiles.isEmpty())
314 emit q->sgNodeChanged();
315}
316
317void QGeoTiledMapPrivate::setVisibleArea(const QRectF &visibleArea)
318{
319 Q_Q(QGeoTiledMap);
320 const QRectF va = clampVisibleArea(visibleArea);
321 if (va == m_visibleArea)
322 return;
323
324 m_visibleArea = va;
325 m_geoProjection->setVisibleArea(va);
326
327 m_visibleTiles->setVisibleArea(va);
328 m_prefetchTiles->setVisibleArea(va);
329 m_mapScene->setVisibleArea(va);
330
331 if (m_copyrightVisible)
332 q->evaluateCopyrights(m_mapScene->visibleTiles());
333 updateScene();
334 q->sgNodeChanged(); // ToDo: explain why emitting twice
335}
336
337QRectF QGeoTiledMapPrivate::visibleArea() const
338{
339 return m_visibleArea;
340}
341
342void QGeoTiledMapPrivate::changeActiveMapType(const QGeoMapType &mapType)
343{
344 m_visibleTiles->setTileSize(m_cameraCapabilities.tileSize());
345 m_prefetchTiles->setTileSize(m_cameraCapabilities.tileSize());
346 m_mapScene->setTileSize(m_cameraCapabilities.tileSize());
347 m_visibleTiles->setMapType(mapType);
348 m_prefetchTiles->setMapType(mapType);
349 changeCameraData(m_cameraData); // Updates the zoom level to the possibly new tile size
350 // updateScene called in changeCameraData()
351}
352
353void QGeoTiledMapPrivate::changeTileVersion(int version)
354{
355 m_visibleTiles->setMapVersion(version);
356 m_prefetchTiles->setMapVersion(version);
357 updateScene();
358}
359
360void QGeoTiledMapPrivate::clearScene()
361{
362 m_mapScene->clearTexturedTiles();
363 m_mapScene->setVisibleTiles(QSet<QGeoTileSpec>());
364 updateScene();
365}
366
367void QGeoTiledMapPrivate::changeViewportSize(const QSize& size)
368{
369 Q_Q(QGeoTiledMap);
370
371 m_visibleTiles->setScreenSize(size);
372 m_prefetchTiles->setScreenSize(size);
373 m_mapScene->setScreenSize(size);
374
375
376 if (!size.isEmpty() && m_cache) {
377 // absolute minimum size: one tile each side of display, 32-bit colour
378 int texCacheSize = (size.width() + m_visibleTiles->tileSize() * 2) *
379 (size.height() + m_visibleTiles->tileSize() * 2) * 4;
380
381 // multiply by 3 so the 'recent' list in the cache is big enough for
382 // an entire display of tiles
383 texCacheSize *= 3;
384 // TODO: move this reasoning into the tilecache
385
386 int newSize = qMax(m_cache->minTextureUsage(), texCacheSize);
387 m_cache->setMinTextureUsage(newSize);
388 }
389
390 if (m_copyrightVisible)
391 q->evaluateCopyrights(m_mapScene->visibleTiles());
392 updateScene();
393}
394
395void QGeoTiledMapPrivate::updateTile(const QGeoTileSpec &spec)
396{
397 Q_Q(QGeoTiledMap);
398 // Only promote the texture up to GPU if it is visible
399 if (m_visibleTiles->createTiles().contains(spec)){
400 QSharedPointer<QGeoTileTexture> tex = m_tileRequests->tileTexture(spec);
401 if (!tex.isNull() && !tex->image.isNull()) {
402 m_mapScene->addTile(spec, tex);
403 emit q->sgNodeChanged();
404 }
405 }
406}
407
408QSGNode *QGeoTiledMapPrivate::updateSceneGraph(QSGNode *oldNode, QQuickWindow *window)
409{
410 return m_mapScene->updateSceneGraph(oldNode, window);
411}
412
413QT_END_NAMESPACE
#define PREFETCH_FRUSTUM_SCALE