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
qgeomapmapboxgl.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// Copyright (C) 2017 Mapbox, Inc.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
9
10#include <QtCore/QByteArray>
11#include <QtCore/QCoreApplication>
12#include <QtGui/QOpenGLContext>
13#include <QtOpenGL/QOpenGLFramebufferObject>
14#include <QtLocation/private/qdeclarativecirclemapitem_p.h>
15#include <QtLocation/private/qdeclarativegeomapitembase_p.h>
16#include <QtLocation/private/qdeclarativepolygonmapitem_p.h>
17#include <QtLocation/private/qdeclarativepolylinemapitem_p.h>
18#include <QtLocation/private/qdeclarativerectanglemapitem_p.h>
19#include <QtLocation/private/qgeoprojection_p.h>
20#include <QtQuick/QQuickWindow>
21#include <QtQuick/QSGImageNode>
22#include <QtQuick/private/qsgcontext_p.h> // for debugging the context name
23
24#include <QMapboxGL>
25
26#include <cmath>
27
28// FIXME: Expose from Mapbox GL constants
29#define MBGL_TILE_SIZE 512.0
30
31namespace {
32
33// WARNING! The development token is subject to Mapbox Terms of Services
34// and must not be used in production.
35static char developmentToken[] =
36 "pk.eyJ1IjoicXRzZGsiLCJhIjoiY2l5azV5MHh5MDAwdTMybzBybjUzZnhxYSJ9.9rfbeqPjX2BusLRDXHCOBA";
37
38static const double invLog2 = 1.0 / std::log(2.0);
39
40static double zoomLevelFrom256(double zoomLevelFor256, double tileSize)
41{
42 return std::log(std::pow(2.0, zoomLevelFor256) * 256.0 / tileSize) * invLog2;
43}
44
45} // namespace
46
47QGeoMapMapboxGLPrivate::QGeoMapMapboxGLPrivate(QGeoMappingManagerEngineMapboxGL *engine)
48 : QGeoMapPrivate(engine, new QGeoProjectionWebMercator)
49{
50}
51
52QGeoMapMapboxGLPrivate::~QGeoMapMapboxGLPrivate()
53{
54}
55
56QSGNode *QGeoMapMapboxGLPrivate::updateSceneGraph(QSGNode *node, QQuickWindow *window)
57{
58 Q_Q(QGeoMapMapboxGL);
59
60 if (m_viewportSize.isEmpty()) {
61 delete node;
62 return nullptr;
63 }
64
65 QMapboxGL *map = nullptr;
66 if (!node) {
67 QOpenGLContext *currentCtx = QOpenGLContext::currentContext();
68 if (!currentCtx) {
69 qWarning("QOpenGLContext is NULL!");
70 qWarning() << "You are running on QSG backend " << QSGContext::backend();
71 qWarning("The MapboxGL plugin works with both Desktop and ES 2.0+ OpenGL versions.");
72 qWarning("Verify that your Qt is built with OpenGL, and what kind of OpenGL.");
73 qWarning("To force using a specific OpenGL version, check QSurfaceFormat::setRenderableType and QSurfaceFormat::setDefaultFormat");
74
75 return node;
76 }
77 if (m_useFBO) {
78 QSGMapboxGLTextureNode *mbglNode = new QSGMapboxGLTextureNode(m_settings, m_viewportSize, window->devicePixelRatio(), q);
79 QObject::connect(mbglNode->map(), &QMapboxGL::mapChanged, q, &QGeoMapMapboxGL::onMapChanged);
80 m_syncState = MapTypeSync | CameraDataSync | ViewportSync | VisibleAreaSync;
81 node = mbglNode;
82 } else {
83 QSGMapboxGLRenderNode *mbglNode = new QSGMapboxGLRenderNode(m_settings, m_viewportSize, window->devicePixelRatio(), q);
84 QObject::connect(mbglNode->map(), &QMapboxGL::mapChanged, q, &QGeoMapMapboxGL::onMapChanged);
85 m_syncState = MapTypeSync | CameraDataSync | ViewportSync | VisibleAreaSync;
86 node = mbglNode;
87 }
88 }
89 map = (m_useFBO) ? static_cast<QSGMapboxGLTextureNode *>(node)->map()
90 : static_cast<QSGMapboxGLRenderNode *>(node)->map();
91
92 if (m_syncState & MapTypeSync) {
93 m_developmentMode = m_activeMapType.name().startsWith("mapbox://")
94 && m_settings.accessToken() == developmentToken;
95
96 map->setStyleUrl(m_activeMapType.name());
97 }
98
99 if (m_syncState & VisibleAreaSync) {
100 if (m_visibleArea.isEmpty()) {
101 map->setMargins(QMargins());
102 } else {
103 QMargins margins(m_visibleArea.x(), // left
104 m_visibleArea.y(), // top
105 m_viewportSize.width() - m_visibleArea.width() - m_visibleArea.x(), // right
106 m_viewportSize.height() - m_visibleArea.height() - m_visibleArea.y()); // bottom
107 map->setMargins(margins);
108 }
109 }
110
111 if (m_syncState & CameraDataSync || m_syncState & VisibleAreaSync) {
112 map->setZoom(zoomLevelFrom256(m_cameraData.zoomLevel() , MBGL_TILE_SIZE));
113 map->setBearing(m_cameraData.bearing());
114 map->setPitch(m_cameraData.tilt());
115
116 QGeoCoordinate coordinate = m_cameraData.center();
117 map->setCoordinate(QMapbox::Coordinate(coordinate.latitude(), coordinate.longitude()));
118 }
119
120 if (m_syncState & ViewportSync) {
121 if (m_useFBO) {
122 static_cast<QSGMapboxGLTextureNode *>(node)->resize(m_viewportSize, window->devicePixelRatio());
123 } else {
124 map->resize(m_viewportSize);
125 }
126 }
127
128 if (m_styleLoaded) {
129 syncStyleChanges(map);
130 }
131
132 if (m_useFBO) {
133 static_cast<QSGMapboxGLTextureNode *>(node)->render(window);
134 }
135
136 threadedRenderingHack(window, map);
137
138 m_syncState = NoSync;
139
140 return node;
141}
142
143QGeoMap::ItemTypes QGeoMapMapboxGLPrivate::supportedMapItemTypes() const
144{
145 return QGeoMap::MapRectangle | QGeoMap::MapCircle | QGeoMap::MapPolygon | QGeoMap::MapPolyline;
146}
147
148void QGeoMapMapboxGLPrivate::addMapItem(QDeclarativeGeoMapItemBase *item)
149{
150 Q_Q(QGeoMapMapboxGL);
151
152 switch (item->itemType()) {
153 case QGeoMap::NoItem:
154 case QGeoMap::MapQuickItem:
155 case QGeoMap::CustomMapItem:
156 return;
157 case QGeoMap::MapRectangle: {
158 QDeclarativeRectangleMapItem *mapItem = static_cast<QDeclarativeRectangleMapItem *>(item);
159 QObject::connect(mapItem, &QQuickItem::visibleChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged);
160 QObject::connect(mapItem, &QDeclarativeGeoMapItemBase::mapItemOpacityChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged);
161 QObject::connect(mapItem, &QDeclarativeRectangleMapItem::bottomRightChanged, q, &QGeoMapMapboxGL::onMapItemGeometryChanged);
162 QObject::connect(mapItem, &QDeclarativeRectangleMapItem::topLeftChanged, q, &QGeoMapMapboxGL::onMapItemGeometryChanged);
163 QObject::connect(mapItem, &QDeclarativeRectangleMapItem::colorChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged);
164 QObject::connect(mapItem->border(), &QDeclarativeMapLineProperties::colorChanged, q, &QGeoMapMapboxGL::onMapItemSubPropertyChanged);
165 QObject::connect(mapItem->border(), &QDeclarativeMapLineProperties::widthChanged, q, &QGeoMapMapboxGL::onMapItemUnsupportedPropertyChanged);
166 } break;
167 case QGeoMap::MapCircle: {
168 QDeclarativeCircleMapItem *mapItem = static_cast<QDeclarativeCircleMapItem *>(item);
169 QObject::connect(mapItem, &QQuickItem::visibleChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged);
170 QObject::connect(mapItem, &QDeclarativeGeoMapItemBase::mapItemOpacityChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged);
171 QObject::connect(mapItem, &QDeclarativeCircleMapItem::centerChanged, q, &QGeoMapMapboxGL::onMapItemGeometryChanged);
172 QObject::connect(mapItem, &QDeclarativeCircleMapItem::radiusChanged, q, &QGeoMapMapboxGL::onMapItemGeometryChanged);
173 QObject::connect(mapItem, &QDeclarativeCircleMapItem::colorChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged);
174 QObject::connect(mapItem->border(), &QDeclarativeMapLineProperties::colorChanged, q, &QGeoMapMapboxGL::onMapItemSubPropertyChanged);
175 QObject::connect(mapItem->border(), &QDeclarativeMapLineProperties::widthChanged, q, &QGeoMapMapboxGL::onMapItemUnsupportedPropertyChanged);
176 } break;
177 case QGeoMap::MapPolygon: {
178 QDeclarativePolygonMapItem *mapItem = static_cast<QDeclarativePolygonMapItem *>(item);
179 QObject::connect(mapItem, &QQuickItem::visibleChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged);
180 QObject::connect(mapItem, &QDeclarativeGeoMapItemBase::mapItemOpacityChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged);
181 QObject::connect(mapItem, &QDeclarativePolygonMapItem::pathChanged, q, &QGeoMapMapboxGL::onMapItemGeometryChanged);
182 QObject::connect(mapItem, &QDeclarativePolygonMapItem::colorChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged);
183 QObject::connect(mapItem->border(), &QDeclarativeMapLineProperties::colorChanged, q, &QGeoMapMapboxGL::onMapItemSubPropertyChanged);
184 QObject::connect(mapItem->border(), &QDeclarativeMapLineProperties::widthChanged, q, &QGeoMapMapboxGL::onMapItemUnsupportedPropertyChanged);
185 } break;
186 case QGeoMap::MapPolyline: {
187 QDeclarativePolylineMapItem *mapItem = static_cast<QDeclarativePolylineMapItem *>(item);
188 QObject::connect(mapItem, &QQuickItem::visibleChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged);
189 QObject::connect(mapItem, &QDeclarativeGeoMapItemBase::mapItemOpacityChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged);
190 QObject::connect(mapItem, &QDeclarativePolylineMapItem::pathChanged, q, &QGeoMapMapboxGL::onMapItemGeometryChanged);
191 QObject::connect(mapItem->line(), &QDeclarativeMapLineProperties::colorChanged, q, &QGeoMapMapboxGL::onMapItemSubPropertyChanged);
192 QObject::connect(mapItem->line(), &QDeclarativeMapLineProperties::widthChanged, q, &QGeoMapMapboxGL::onMapItemSubPropertyChanged);
193 } break;
194 }
195
196 QObject::connect(item, &QDeclarativeGeoMapItemBase::mapItemOpacityChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged);
197
198 m_styleChanges << QMapboxGLStyleChange::addMapItem(item, m_mapItemsBefore);
199
200 emit q->sgNodeChanged();
201}
202
203void QGeoMapMapboxGLPrivate::removeMapItem(QDeclarativeGeoMapItemBase *item)
204{
205 Q_Q(QGeoMapMapboxGL);
206
207 switch (item->itemType()) {
208 case QGeoMap::NoItem:
209 case QGeoMap::MapQuickItem:
210 case QGeoMap::CustomMapItem:
211 return;
212 case QGeoMap::MapRectangle:
213 q->disconnect(static_cast<QDeclarativeRectangleMapItem *>(item)->border());
214 break;
215 case QGeoMap::MapCircle:
216 q->disconnect(static_cast<QDeclarativeCircleMapItem *>(item)->border());
217 break;
218 case QGeoMap::MapPolygon:
219 q->disconnect(static_cast<QDeclarativePolygonMapItem *>(item)->border());
220 break;
221 case QGeoMap::MapPolyline:
222 q->disconnect(static_cast<QDeclarativePolylineMapItem *>(item)->line());
223 break;
224 }
225
226 q->disconnect(item);
227
228 m_styleChanges << QMapboxGLStyleChange::removeMapItem(item);
229
230 emit q->sgNodeChanged();
231}
232
233void QGeoMapMapboxGLPrivate::changeViewportSize(const QSize &)
234{
235 Q_Q(QGeoMapMapboxGL);
236
237 m_syncState = m_syncState | ViewportSync;
238 emit q->sgNodeChanged();
239}
240
241void QGeoMapMapboxGLPrivate::changeCameraData(const QGeoCameraData &)
242{
243 Q_Q(QGeoMapMapboxGL);
244
245 m_syncState = m_syncState | CameraDataSync;
246 emit q->sgNodeChanged();
247}
248
249void QGeoMapMapboxGLPrivate::changeActiveMapType(const QGeoMapType)
250{
251 Q_Q(QGeoMapMapboxGL);
252
253 m_syncState = m_syncState | MapTypeSync;
254 emit q->sgNodeChanged();
255}
256
257void QGeoMapMapboxGLPrivate::setVisibleArea(const QRectF &visibleArea)
258{
259 Q_Q(QGeoMapMapboxGL);
260 const QRectF va = clampVisibleArea(visibleArea);
261 if (va == m_visibleArea)
262 return;
263
264 m_visibleArea = va;
265 m_geoProjection->setVisibleArea(va);
266
267 m_syncState = m_syncState | VisibleAreaSync;
268 emit q->sgNodeChanged();
269}
270
271QRectF QGeoMapMapboxGLPrivate::visibleArea() const
272{
273 return m_visibleArea;
274}
275
276void QGeoMapMapboxGLPrivate::syncStyleChanges(QMapboxGL *map)
277{
278 for (const auto& change : m_styleChanges) {
279 change->apply(map);
280 }
281
282 m_styleChanges.clear();
283}
284
285void QGeoMapMapboxGLPrivate::threadedRenderingHack(QQuickWindow *window, QMapboxGL *map)
286{
287 // FIXME: Optimal support for threaded rendering needs core changes
288 // in Mapbox GL Native. Meanwhile we need to set a timer to update
289 // the map until all the resources are loaded, which is not exactly
290 // battery friendly, because might trigger more paints than we need.
291 if (!m_warned) {
292 m_threadedRendering = window->openglContext()->thread() != QCoreApplication::instance()->thread();
293
294 if (m_threadedRendering) {
295 qWarning() << "Threaded rendering is not optimal in the Mapbox GL plugin.";
296 }
297
298 m_warned = true;
299 }
300
301 if (m_threadedRendering) {
302 if (!map->isFullyLoaded()) {
303 QMetaObject::invokeMethod(&m_refresh, "start", Qt::QueuedConnection);
304 } else {
305 QMetaObject::invokeMethod(&m_refresh, "stop", Qt::QueuedConnection);
306 }
307 }
308}
309
310/*
311 * QGeoMapMapboxGL implementation
312 */
313
314QGeoMapMapboxGL::QGeoMapMapboxGL(QGeoMappingManagerEngineMapboxGL *engine, QObject *parent)
315 : QGeoMap(*new QGeoMapMapboxGLPrivate(engine), parent), m_engine(engine)
316{
317 Q_D(QGeoMapMapboxGL);
318
319 connect(&d->m_refresh, &QTimer::timeout, this, &QGeoMap::sgNodeChanged);
320 d->m_refresh.setInterval(250);
321}
322
323QGeoMapMapboxGL::~QGeoMapMapboxGL()
324{
325}
326
327QString QGeoMapMapboxGL::copyrightsStyleSheet() const
328{
329 return QStringLiteral("* { vertical-align: middle; font-weight: normal }");
330}
331
332void QGeoMapMapboxGL::setMapboxGLSettings(const QMapboxGLSettings& settings, bool useChinaEndpoint)
333{
334 Q_D(QGeoMapMapboxGL);
335
336 d->m_settings = settings;
337
338 // If the access token is not set, use the development access token.
339 // This will only affect mapbox:// styles.
340 // Mapbox China requires a China-specific access token.
341 if (d->m_settings.accessToken().isEmpty()) {
342 if (useChinaEndpoint) {
343 qWarning("Mapbox China requires an access token: https://www.mapbox.com/contact/sales");
344 } else {
345 d->m_settings.setAccessToken(developmentToken);
346 }
347 }
348}
349
350void QGeoMapMapboxGL::setUseFBO(bool useFBO)
351{
352 Q_D(QGeoMapMapboxGL);
353 d->m_useFBO = useFBO;
354}
355
356void QGeoMapMapboxGL::setMapItemsBefore(const QString &before)
357{
358 Q_D(QGeoMapMapboxGL);
359 d->m_mapItemsBefore = before;
360}
361
362QGeoMap::Capabilities QGeoMapMapboxGL::capabilities() const
363{
364 return Capabilities(SupportsVisibleRegion
365 | SupportsSetBearing
366 | SupportsAnchoringCoordinate
367 | SupportsVisibleArea );
368}
369
370QSGNode *QGeoMapMapboxGL::updateSceneGraph(QSGNode *oldNode, QQuickWindow *window)
371{
372 Q_D(QGeoMapMapboxGL);
373 return d->updateSceneGraph(oldNode, window);
374}
375
376void QGeoMapMapboxGL::onMapChanged(QMapboxGL::MapChange change)
377{
378 Q_D(QGeoMapMapboxGL);
379
380 if (change == QMapboxGL::MapChangeDidFinishLoadingStyle || change == QMapboxGL::MapChangeDidFailLoadingMap) {
381 d->m_styleLoaded = true;
382 } else if (change == QMapboxGL::MapChangeWillStartLoadingMap) {
383 d->m_styleLoaded = false;
384 d->m_styleChanges.clear();
385
386 for (QDeclarativeGeoMapItemBase *item : d->m_mapItems)
387 d->m_styleChanges << QMapboxGLStyleChange::addMapItem(item, d->m_mapItemsBefore);
388 }
389}
390
391void QGeoMapMapboxGL::onMapItemPropertyChanged()
392{
393 Q_D(QGeoMapMapboxGL);
394
395 QDeclarativeGeoMapItemBase *item = static_cast<QDeclarativeGeoMapItemBase *>(sender());
396 d->m_styleChanges << QMapboxGLStyleSetPaintProperty::fromMapItem(item);
397 d->m_styleChanges << QMapboxGLStyleSetLayoutProperty::fromMapItem(item);
398
399 emit sgNodeChanged();
400}
401
402void QGeoMapMapboxGL::onMapItemSubPropertyChanged()
403{
404 Q_D(QGeoMapMapboxGL);
405
406 QDeclarativeGeoMapItemBase *item = static_cast<QDeclarativeGeoMapItemBase *>(sender()->parent());
407 d->m_styleChanges << QMapboxGLStyleSetPaintProperty::fromMapItem(item);
408
409 emit sgNodeChanged();
410}
411
412void QGeoMapMapboxGL::onMapItemUnsupportedPropertyChanged()
413{
414 // TODO https://bugreports.qt.io/browse/QTBUG-58872
415 qWarning() << "Unsupported property for managed Map item";
416}
417
418void QGeoMapMapboxGL::onMapItemGeometryChanged()
419{
420 Q_D(QGeoMapMapboxGL);
421
422 QDeclarativeGeoMapItemBase *item = static_cast<QDeclarativeGeoMapItemBase *>(sender());
423 d->m_styleChanges << QMapboxGLStyleAddSource::fromMapItem(item);
424
425 emit sgNodeChanged();
426}
427
428void QGeoMapMapboxGL::copyrightsChanged(const QString &copyrightsHtml)
429{
430 Q_D(QGeoMapMapboxGL);
431
432 QString copyrightsHtmlFinal = copyrightsHtml;
433
434 if (d->m_developmentMode) {
435 copyrightsHtmlFinal.prepend("<a href='https://www.mapbox.com/pricing'>"
436 + tr("Development access token, do not use in production.") + "</a> - ");
437 }
438
439 if (d->m_activeMapType.name().startsWith("mapbox://")) {
440 copyrightsHtmlFinal = "<table><tr><th><img src='qrc:/mapboxgl/logo.png'/></th><th>"
441 + copyrightsHtmlFinal + "</th></tr></table>";
442 }
443
444 QGeoMap::copyrightsChanged(copyrightsHtmlFinal);
445}
#define MBGL_TILE_SIZE