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 
66 namespace {
67 
68 // WARNING! The development token is subject to Mapbox Terms of Services
69 // and must not be used in production.
70 static char developmentToken[] =
71     "pk.eyJ1IjoicXRzZGsiLCJhIjoiY2l5azV5MHh5MDAwdTMybzBybjUzZnhxYSJ9.9rfbeqPjX2BusLRDXHCOBA";
72 
73 static const double invLog2 = 1.0 / std::log(2.0);
74 
zoomLevelFrom256(double zoomLevelFor256,double tileSize)75 static double zoomLevelFrom256(double zoomLevelFor256, double tileSize)
76 {
77     return std::log(std::pow(2.0, zoomLevelFor256) * 256.0 / tileSize) * invLog2;
78 }
79 
80 } // namespace
81 
QGeoMapMapboxGLPrivate(QGeoMappingManagerEngineMapboxGL * engine)82 QGeoMapMapboxGLPrivate::QGeoMapMapboxGLPrivate(QGeoMappingManagerEngineMapboxGL *engine)
83     : QGeoMapPrivate(engine, new QGeoProjectionWebMercator)
84 {
85 }
86 
~QGeoMapMapboxGLPrivate()87 QGeoMapMapboxGLPrivate::~QGeoMapMapboxGLPrivate()
88 {
89 }
90 
updateSceneGraph(QSGNode * node,QQuickWindow * window)91 QSGNode *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("QOpenGLContext is NULL!");
105             qWarning() << "You are running on QSG backend " << QSGContext::backend();
106             qWarning("The MapboxGL plugin works with both Desktop and ES 2.0+ OpenGL versions.");
107             qWarning("Verify that your Qt is built with OpenGL, and what kind of OpenGL.");
108             qWarning("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(mbglNode->map(), &QMapboxGL::mapChanged, q, &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(mbglNode->map(), &QMapboxGL::mapChanged, q, &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("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(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(m_viewportSize, window->devicePixelRatio());
158         } else {
159             map->resize(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 
addParameter(QGeoMapParameter * param)178 void QGeoMapMapboxGLPrivate::addParameter(QGeoMapParameter *param)
179 {
180     Q_Q(QGeoMapMapboxGL);
181 
182     QObject::connect(param, &QGeoMapParameter::propertyUpdated, q,
183         &QGeoMapMapboxGL::onParameterPropertyUpdated);
184 
185     if (m_styleLoaded) {
186         m_styleChanges << QMapboxGLStyleChange::addMapParameter(param);
187         emit q->sgNodeChanged();
188     }
189 }
190 
removeParameter(QGeoMapParameter * param)191 void QGeoMapMapboxGLPrivate::removeParameter(QGeoMapParameter *param)
192 {
193     Q_Q(QGeoMapMapboxGL);
194 
195     q->disconnect(param);
196 
197     if (m_styleLoaded) {
198         m_styleChanges << QMapboxGLStyleChange::removeMapParameter(param);
199         emit q->sgNodeChanged();
200     }
201 }
202 
supportedMapItemTypes() const203 QGeoMap::ItemTypes QGeoMapMapboxGLPrivate::supportedMapItemTypes() const
204 {
205     return QGeoMap::MapRectangle | QGeoMap::MapCircle | QGeoMap::MapPolygon | QGeoMap::MapPolyline;
206 }
207 
addMapItem(QDeclarativeGeoMapItemBase * item)208 void 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(mapItem, &QQuickItem::visibleChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged);
220         QObject::connect(mapItem, &QDeclarativeGeoMapItemBase::mapItemOpacityChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged);
221         QObject::connect(mapItem, &QDeclarativeRectangleMapItem::bottomRightChanged, q, &QGeoMapMapboxGL::onMapItemGeometryChanged);
222         QObject::connect(mapItem, &QDeclarativeRectangleMapItem::topLeftChanged, q, &QGeoMapMapboxGL::onMapItemGeometryChanged);
223         QObject::connect(mapItem, &QDeclarativeRectangleMapItem::colorChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged);
224         QObject::connect(mapItem->border(), &QDeclarativeMapLineProperties::colorChanged, q, &QGeoMapMapboxGL::onMapItemSubPropertyChanged);
225         QObject::connect(mapItem->border(), &QDeclarativeMapLineProperties::widthChanged, q, &QGeoMapMapboxGL::onMapItemUnsupportedPropertyChanged);
226     } break;
227     case QGeoMap::MapCircle: {
228         QDeclarativeCircleMapItem *mapItem = static_cast<QDeclarativeCircleMapItem *>(item);
229         QObject::connect(mapItem, &QQuickItem::visibleChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged);
230         QObject::connect(mapItem, &QDeclarativeGeoMapItemBase::mapItemOpacityChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged);
231         QObject::connect(mapItem, &QDeclarativeCircleMapItem::centerChanged, q, &QGeoMapMapboxGL::onMapItemGeometryChanged);
232         QObject::connect(mapItem, &QDeclarativeCircleMapItem::radiusChanged, q, &QGeoMapMapboxGL::onMapItemGeometryChanged);
233         QObject::connect(mapItem, &QDeclarativeCircleMapItem::colorChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged);
234         QObject::connect(mapItem->border(), &QDeclarativeMapLineProperties::colorChanged, q, &QGeoMapMapboxGL::onMapItemSubPropertyChanged);
235         QObject::connect(mapItem->border(), &QDeclarativeMapLineProperties::widthChanged, q, &QGeoMapMapboxGL::onMapItemUnsupportedPropertyChanged);
236     } break;
237     case QGeoMap::MapPolygon: {
238         QDeclarativePolygonMapItem *mapItem = static_cast<QDeclarativePolygonMapItem *>(item);
239         QObject::connect(mapItem, &QQuickItem::visibleChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged);
240         QObject::connect(mapItem, &QDeclarativeGeoMapItemBase::mapItemOpacityChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged);
241         QObject::connect(mapItem, &QDeclarativePolygonMapItem::pathChanged, q, &QGeoMapMapboxGL::onMapItemGeometryChanged);
242         QObject::connect(mapItem, &QDeclarativePolygonMapItem::colorChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged);
243         QObject::connect(mapItem->border(), &QDeclarativeMapLineProperties::colorChanged, q, &QGeoMapMapboxGL::onMapItemSubPropertyChanged);
244         QObject::connect(mapItem->border(), &QDeclarativeMapLineProperties::widthChanged, q, &QGeoMapMapboxGL::onMapItemUnsupportedPropertyChanged);
245     } break;
246     case QGeoMap::MapPolyline: {
247         QDeclarativePolylineMapItem *mapItem = static_cast<QDeclarativePolylineMapItem *>(item);
248         QObject::connect(mapItem, &QQuickItem::visibleChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged);
249         QObject::connect(mapItem, &QDeclarativeGeoMapItemBase::mapItemOpacityChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged);
250         QObject::connect(mapItem, &QDeclarativePolylineMapItem::pathChanged, q, &QGeoMapMapboxGL::onMapItemGeometryChanged);
251         QObject::connect(mapItem->line(), &QDeclarativeMapLineProperties::colorChanged, q, &QGeoMapMapboxGL::onMapItemSubPropertyChanged);
252         QObject::connect(mapItem->line(), &QDeclarativeMapLineProperties::widthChanged, q, &QGeoMapMapboxGL::onMapItemSubPropertyChanged);
253     } break;
254     }
255 
256     QObject::connect(item, &QDeclarativeGeoMapItemBase::mapItemOpacityChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged);
257 
258     m_styleChanges << QMapboxGLStyleChange::addMapItem(item, m_mapItemsBefore);
259 
260     emit q->sgNodeChanged();
261 }
262 
removeMapItem(QDeclarativeGeoMapItemBase * item)263 void 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(static_cast<QDeclarativeRectangleMapItem *>(item)->border());
274         break;
275     case QGeoMap::MapCircle:
276         q->disconnect(static_cast<QDeclarativeCircleMapItem *>(item)->border());
277         break;
278     case QGeoMap::MapPolygon:
279         q->disconnect(static_cast<QDeclarativePolygonMapItem *>(item)->border());
280         break;
281     case QGeoMap::MapPolyline:
282         q->disconnect(static_cast<QDeclarativePolylineMapItem *>(item)->line());
283         break;
284     }
285 
286     q->disconnect(item);
287 
288     m_styleChanges << QMapboxGLStyleChange::removeMapItem(item);
289 
290     emit q->sgNodeChanged();
291 }
292 
changeViewportSize(const QSize &)293 void QGeoMapMapboxGLPrivate::changeViewportSize(const QSize &)
294 {
295     Q_Q(QGeoMapMapboxGL);
296 
297     m_syncState = m_syncState | ViewportSync;
298     emit q->sgNodeChanged();
299 }
300 
changeCameraData(const QGeoCameraData &)301 void QGeoMapMapboxGLPrivate::changeCameraData(const QGeoCameraData &)
302 {
303     Q_Q(QGeoMapMapboxGL);
304 
305     m_syncState = m_syncState | CameraDataSync;
306     emit q->sgNodeChanged();
307 }
308 
changeActiveMapType(const QGeoMapType)309 void QGeoMapMapboxGLPrivate::changeActiveMapType(const QGeoMapType)
310 {
311     Q_Q(QGeoMapMapboxGL);
312 
313     m_syncState = m_syncState | MapTypeSync;
314     emit q->sgNodeChanged();
315 }
316 
setVisibleArea(const QRectF & visibleArea)317 void 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 
visibleArea() const331 QRectF QGeoMapMapboxGLPrivate::visibleArea() const
332 {
333     return m_visibleArea;
334 }
335 
syncStyleChanges(QMapboxGL * map)336 void QGeoMapMapboxGLPrivate::syncStyleChanges(QMapboxGL *map)
337 {
338     for (const auto& change : m_styleChanges) {
339         change->apply(map);
340     }
341 
342     m_styleChanges.clear();
343 }
344 
threadedRenderingHack(QQuickWindow * window,QMapboxGL * map)345 void 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(&m_refresh, "start", Qt::QueuedConnection);
364         } else {
365             QMetaObject::invokeMethod(&m_refresh, "stop", Qt::QueuedConnection);
366         }
367     }
368 }
369 
370 /*
371  * QGeoMapMapboxGL implementation
372  */
373 
QGeoMapMapboxGL(QGeoMappingManagerEngineMapboxGL * engine,QObject * parent)374 QGeoMapMapboxGL::QGeoMapMapboxGL(QGeoMappingManagerEngineMapboxGL *engine, QObject *parent)
375     :   QGeoMap(*new QGeoMapMapboxGLPrivate(engine), parent), m_engine(engine)
376 {
377     Q_D(QGeoMapMapboxGL);
378 
379     connect(&d->m_refresh, &QTimer::timeout, this, &QGeoMap::sgNodeChanged);
380     d->m_refresh.setInterval(250);
381 }
382 
~QGeoMapMapboxGL()383 QGeoMapMapboxGL::~QGeoMapMapboxGL()
384 {
385 }
386 
copyrightsStyleSheet() const387 QString QGeoMapMapboxGL::copyrightsStyleSheet() const
388 {
389     return QStringLiteral("* { vertical-align: middle; font-weight: normal }");
390 }
391 
setMapboxGLSettings(const QMapboxGLSettings & settings,bool useChinaEndpoint)392 void 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("Mapbox China requires an access token: https://www.mapbox.com/contact/sales");
404         } else {
405             d->m_settings.setAccessToken(developmentToken);
406         }
407     }
408 }
409 
setUseFBO(bool useFBO)410 void QGeoMapMapboxGL::setUseFBO(bool useFBO)
411 {
412     Q_D(QGeoMapMapboxGL);
413     d->m_useFBO = useFBO;
414 }
415 
setMapItemsBefore(const QString & before)416 void QGeoMapMapboxGL::setMapItemsBefore(const QString &before)
417 {
418     Q_D(QGeoMapMapboxGL);
419     d->m_mapItemsBefore = before;
420 }
421 
capabilities() const422 QGeoMap::Capabilities QGeoMapMapboxGL::capabilities() const
423 {
424     return Capabilities(SupportsVisibleRegion
425                         | SupportsSetBearing
426                         | SupportsAnchoringCoordinate
427                         | SupportsVisibleArea );
428 }
429 
updateSceneGraph(QSGNode * oldNode,QQuickWindow * window)430 QSGNode *QGeoMapMapboxGL::updateSceneGraph(QSGNode *oldNode, QQuickWindow *window)
431 {
432     Q_D(QGeoMapMapboxGL);
433     return d->updateSceneGraph(oldNode, window);
434 }
435 
onMapChanged(QMapboxGL::MapChange change)436 void 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, d->m_mapItemsBefore);
448 
449         for (QGeoMapParameter *param : d->m_mapParameters)
450             d->m_styleChanges << QMapboxGLStyleChange::addMapParameter(param);
451     }
452 }
453 
onMapItemPropertyChanged()454 void 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 
onMapItemSubPropertyChanged()465 void 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 
onMapItemUnsupportedPropertyChanged()475 void QGeoMapMapboxGL::onMapItemUnsupportedPropertyChanged()
476 {
477     // TODO https://bugreports.qt.io/browse/QTBUG-58872
478     qWarning() << "Unsupported property for managed Map item";
479 }
480 
onMapItemGeometryChanged()481 void 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 
onParameterPropertyUpdated(QGeoMapParameter * param,const char *)491 void QGeoMapMapboxGL::onParameterPropertyUpdated(QGeoMapParameter *param, const char *)
492 {
493     Q_D(QGeoMapMapboxGL);
494 
495     d->m_styleChanges.append(QMapboxGLStyleChange::addMapParameter(param));
496 
497     emit sgNodeChanged();
498 }
499 
copyrightsChanged(const QString & copyrightsHtml)500 void QGeoMapMapboxGL::copyrightsChanged(const QString &copyrightsHtml)
501 {
502     Q_D(QGeoMapMapboxGL);
503 
504     QString copyrightsHtmlFinal = copyrightsHtml;
505 
506     if (d->m_developmentMode) {
507         copyrightsHtmlFinal.prepend("<a href='https://www.mapbox.com/pricing'>"
508             + tr("Development access token, do not use in production.") + "</a> - ");
509     }
510 
511     if (d->m_activeMapType.name().startsWith("mapbox://")) {
512         copyrightsHtmlFinal = "<table><tr><th><img src='qrc:/mapboxgl/logo.png'/></th><th>"
513             + copyrightsHtmlFinal + "</th></tr></table>";
514     }
515 
516     QGeoMap::copyrightsChanged(copyrightsHtmlFinal);
517 }
518