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