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

source code of qtlocation/src/plugins/geoservices/mapboxgl/qgeomapmapboxgl.cpp