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