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