1 /****************************************************************************
2 **
3 ** Copyright (C) 2017 Mapbox, Inc.
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 
37 #include "qmapboxglstylechange_p.h"
38 
39 #include <QtCore/QDebug>
40 #include <QtCore/QMetaProperty>
41 #include <QtCore/QRegularExpression>
42 #include <QtCore/QStringList>
43 #include <QtPositioning/QGeoPath>
44 #include <QtPositioning/QGeoPolygon>
45 #include <QtQml/QJSValue>
46 #include <QtLocation/private/qdeclarativecirclemapitem_p_p.h>
47 
48 namespace {
49 
formatPropertyName(const QByteArray & name)50 QByteArray formatPropertyName(const QByteArray &name)
51 {
52     QString nameAsString = QString::fromLatin1(name);
53     static const QRegularExpression camelCaseRegex(QStringLiteral("([a-z0-9])([A-Z])"));
54     return nameAsString.replace(camelCaseRegex, QStringLiteral("\\1-\\2")).toLower().toLatin1();
55 }
56 
isImmutableProperty(const QByteArray & name)57 bool isImmutableProperty(const QByteArray &name)
58 {
59     return name == QStringLiteral("type") || name == QStringLiteral("layer");
60 }
61 
getId(QDeclarativeGeoMapItemBase * mapItem)62 QString getId(QDeclarativeGeoMapItemBase *mapItem)
63 {
64     return QStringLiteral("QtLocation-") +
65             ((mapItem->objectName().isEmpty()) ? QString::number(quint64(mapItem)) : mapItem->objectName());
66 }
67 
68 // Mapbox GL supports geometry segments that spans above 180 degrees in
69 // longitude. To keep visual expectations in parity with Qt, we need to adapt
70 // the coordinates to always use the shortest path when in ambiguity.
geoRectangleCrossesDateLine(const QGeoRectangle & rect)71 static bool geoRectangleCrossesDateLine(const QGeoRectangle &rect) {
72     return rect.topLeft().longitude() > rect.bottomRight().longitude();
73 }
74 
featureFromMapRectangle(QDeclarativeRectangleMapItem * mapItem)75 QMapbox::Feature featureFromMapRectangle(QDeclarativeRectangleMapItem *mapItem)
76 {
77     const QGeoRectangle *rect = static_cast<const QGeoRectangle *>(&mapItem->geoShape());
78     QMapbox::Coordinate bottomLeft { rect->bottomLeft().latitude(), rect->bottomLeft().longitude() };
79     QMapbox::Coordinate topLeft { rect->topLeft().latitude(), rect->topLeft().longitude() };
80     QMapbox::Coordinate bottomRight { rect->bottomRight().latitude(), rect->bottomRight().longitude() };
81     QMapbox::Coordinate topRight { rect->topRight().latitude(), rect->topRight().longitude() };
82     if (geoRectangleCrossesDateLine(*rect)) {
83         bottomRight.second += 360.0;
84         topRight.second += 360.0;
85     }
86     QMapbox::CoordinatesCollections geometry { { { bottomLeft, bottomRight, topRight, topLeft, bottomLeft } } };
87 
88     return QMapbox::Feature(QMapbox::Feature::PolygonType, geometry, {}, getId(mapItem));
89 }
90 
featureFromMapCircle(QDeclarativeCircleMapItem * mapItem)91 QMapbox::Feature featureFromMapCircle(QDeclarativeCircleMapItem *mapItem)
92 {
93     static const int circleSamples = 128;
94     const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(mapItem->map()->geoProjection());
95     QList<QGeoCoordinate> path;
96     QGeoCoordinate leftBound;
97     QDeclarativeCircleMapItemPrivateCPU::calculatePeripheralPoints(path, mapItem->center(), mapItem->radius(), circleSamples, leftBound);
98     QList<QDoubleVector2D> pathProjected;
99     for (const QGeoCoordinate &c : qAsConst(path))
100         pathProjected << p.geoToMapProjection(c);
101     if (QDeclarativeCircleMapItemPrivateCPU::crossEarthPole(mapItem->center(), mapItem->radius()))
102         QDeclarativeCircleMapItemPrivateCPU::preserveCircleGeometry(pathProjected, mapItem->center(), mapItem->radius(), p);
103     path.clear();
104     for (const QDoubleVector2D &c : qAsConst(pathProjected))
105         path << p.mapProjectionToGeo(c);
106 
107 
108     QMapbox::Coordinates coordinates;
109     for (const QGeoCoordinate &coordinate : path) {
110         coordinates << QMapbox::Coordinate { coordinate.latitude(), coordinate.longitude() };
111     }
112     coordinates.append(coordinates.first());  // closing the path
113     QMapbox::CoordinatesCollections geometry { { coordinates } };
114     return QMapbox::Feature(QMapbox::Feature::PolygonType, geometry, {}, getId(mapItem));
115 }
116 
qgeocoordinate2mapboxcoordinate(const QList<QGeoCoordinate> & crds,const bool crossesDateline,bool closed=false)117 static QMapbox::Coordinates qgeocoordinate2mapboxcoordinate(const QList<QGeoCoordinate> &crds, const bool crossesDateline, bool closed = false)
118 {
119     QMapbox::Coordinates coordinates;
120     for (const QGeoCoordinate &coordinate : crds) {
121         if (!coordinates.empty() && crossesDateline && qAbs(coordinate.longitude() - coordinates.last().second) > 180.0) {
122             coordinates << QMapbox::Coordinate { coordinate.latitude(), coordinate.longitude() + (coordinate.longitude() >= 0 ? -360.0 : 360.0) };
123         } else {
124             coordinates << QMapbox::Coordinate { coordinate.latitude(), coordinate.longitude() };
125         }
126     }
127     if (closed && !coordinates.empty() && coordinates.last() != coordinates.first())
128         coordinates.append(coordinates.first());  // closing the path
129     return coordinates;
130 }
131 
featureFromMapPolygon(QDeclarativePolygonMapItem * mapItem)132 QMapbox::Feature featureFromMapPolygon(QDeclarativePolygonMapItem *mapItem)
133 {
134     const QGeoPolygon *polygon = static_cast<const QGeoPolygon *>(&mapItem->geoShape());
135     const bool crossesDateline = geoRectangleCrossesDateLine(polygon->boundingGeoRectangle());
136     QMapbox::CoordinatesCollections geometry;
137     QMapbox::CoordinatesCollection poly;
138     QMapbox::Coordinates coordinates = qgeocoordinate2mapboxcoordinate(polygon->path(), crossesDateline, true);
139     poly.push_back(coordinates);
140     for (int i = 0; i < polygon->holesCount(); ++i) {
141         coordinates = qgeocoordinate2mapboxcoordinate(polygon->holePath(i), crossesDateline, true);
142         poly.push_back(coordinates);
143     }
144 
145     geometry.push_back(poly);
146     return QMapbox::Feature(QMapbox::Feature::PolygonType, geometry, {}, getId(mapItem));
147 }
148 
featureFromMapPolyline(QDeclarativePolylineMapItem * mapItem)149 QMapbox::Feature featureFromMapPolyline(QDeclarativePolylineMapItem *mapItem)
150 {
151     const QGeoPath *path = static_cast<const QGeoPath *>(&mapItem->geoShape());
152     QMapbox::Coordinates coordinates;
153     const bool crossesDateline = geoRectangleCrossesDateLine(path->boundingGeoRectangle());
154     for (const QGeoCoordinate &coordinate : path->path()) {
155         if (!coordinates.empty() && crossesDateline && qAbs(coordinate.longitude() - coordinates.last().second) > 180.0) {
156             coordinates << QMapbox::Coordinate { coordinate.latitude(), coordinate.longitude() + (coordinate.longitude() >= 0 ? -360.0 : 360.0) };
157         } else {
158             coordinates << QMapbox::Coordinate { coordinate.latitude(), coordinate.longitude() };
159         }
160     }
161     QMapbox::CoordinatesCollections geometry { { coordinates } };
162 
163     return QMapbox::Feature(QMapbox::Feature::LineStringType, geometry, {}, getId(mapItem));
164 }
165 
featureFromMapItem(QDeclarativeGeoMapItemBase * item)166 QMapbox::Feature featureFromMapItem(QDeclarativeGeoMapItemBase *item)
167 {
168     switch (item->itemType()) {
169     case QGeoMap::MapRectangle:
170         return featureFromMapRectangle(static_cast<QDeclarativeRectangleMapItem *>(item));
171     case QGeoMap::MapCircle:
172         return featureFromMapCircle(static_cast<QDeclarativeCircleMapItem *>(item));
173     case QGeoMap::MapPolygon:
174         return featureFromMapPolygon(static_cast<QDeclarativePolygonMapItem *>(item));
175     case QGeoMap::MapPolyline:
176         return featureFromMapPolyline(static_cast<QDeclarativePolylineMapItem *>(item));
177     default:
178         qWarning() << "Unsupported QGeoMap item type: " << item->itemType();
179         return QMapbox::Feature();
180     }
181 }
182 
getAllPropertyNamesList(QObject * object)183 QList<QByteArray> getAllPropertyNamesList(QObject *object)
184 {
185     const QMetaObject *metaObject = object->metaObject();
186     QList<QByteArray> propertyNames(object->dynamicPropertyNames());
187     for (int i = metaObject->propertyOffset(); i < metaObject->propertyCount(); ++i) {
188         propertyNames.append(metaObject->property(i).name());
189     }
190     return propertyNames;
191 }
192 
193 } // namespace
194 
195 
196 // QMapboxGLStyleChange
197 
addMapParameter(QGeoMapParameter * param)198 QList<QSharedPointer<QMapboxGLStyleChange>> QMapboxGLStyleChange::addMapParameter(QGeoMapParameter *param)
199 {
200     static const QStringList acceptedParameterTypes = QStringList()
201         << QStringLiteral("paint") << QStringLiteral("layout") << QStringLiteral("filter")
202         << QStringLiteral("layer") << QStringLiteral("source") << QStringLiteral("image");
203 
204     QList<QSharedPointer<QMapboxGLStyleChange>> changes;
205 
206     switch (acceptedParameterTypes.indexOf(param->type())) {
207     case -1:
208         qWarning() << "Invalid value for property 'type': " + param->type();
209         break;
210     case 0: // paint
211         changes << QMapboxGLStyleSetPaintProperty::fromMapParameter(param);
212         break;
213     case 1: // layout
214         changes << QMapboxGLStyleSetLayoutProperty::fromMapParameter(param);
215         break;
216     case 2: // filter
217         changes << QMapboxGLStyleSetFilter::fromMapParameter(param);
218         break;
219     case 3: // layer
220         changes << QMapboxGLStyleAddLayer::fromMapParameter(param);
221         break;
222     case 4: // source
223         changes << QMapboxGLStyleAddSource::fromMapParameter(param);
224         break;
225     case 5: // image
226         changes << QMapboxGLStyleAddImage::fromMapParameter(param);
227         break;
228     }
229 
230     return changes;
231 }
232 
addMapItem(QDeclarativeGeoMapItemBase * item,const QString & before)233 QList<QSharedPointer<QMapboxGLStyleChange>> QMapboxGLStyleChange::addMapItem(QDeclarativeGeoMapItemBase *item, const QString &before)
234 {
235     QList<QSharedPointer<QMapboxGLStyleChange>> changes;
236 
237     switch (item->itemType()) {
238     case QGeoMap::MapRectangle:
239     case QGeoMap::MapCircle:
240     case QGeoMap::MapPolygon:
241     case QGeoMap::MapPolyline:
242         break;
243     default:
244         qWarning() << "Unsupported QGeoMap item type: " << item->itemType();
245         return changes;
246     }
247 
248     QMapbox::Feature feature = featureFromMapItem(item);
249 
250     changes << QMapboxGLStyleAddLayer::fromFeature(feature, before);
251     changes << QMapboxGLStyleAddSource::fromFeature(feature);
252     changes << QMapboxGLStyleSetPaintProperty::fromMapItem(item);
253     changes << QMapboxGLStyleSetLayoutProperty::fromMapItem(item);
254 
255     return changes;
256 }
257 
removeMapParameter(QGeoMapParameter * param)258 QList<QSharedPointer<QMapboxGLStyleChange>> QMapboxGLStyleChange::removeMapParameter(QGeoMapParameter *param)
259 {
260     static const QStringList acceptedParameterTypes = QStringList()
261         << QStringLiteral("paint") << QStringLiteral("layout") << QStringLiteral("filter")
262         << QStringLiteral("layer") << QStringLiteral("source") << QStringLiteral("image");
263 
264     QList<QSharedPointer<QMapboxGLStyleChange>> changes;
265 
266     switch (acceptedParameterTypes.indexOf(param->type())) {
267     case -1:
268         qWarning() << "Invalid value for property 'type': " + param->type();
269         break;
270     case 0: // paint
271     case 1: // layout
272     case 2: // filter
273         break;
274     case 3: // layer
275         changes << QSharedPointer<QMapboxGLStyleChange>(new QMapboxGLStyleRemoveLayer(param->property("name").toString()));
276         break;
277     case 4: // source
278         changes << QSharedPointer<QMapboxGLStyleChange>(new QMapboxGLStyleRemoveSource(param->property("name").toString()));
279         break;
280     case 5: // image
281         break;
282     }
283 
284     return changes;
285 }
286 
removeMapItem(QDeclarativeGeoMapItemBase * item)287 QList<QSharedPointer<QMapboxGLStyleChange>> QMapboxGLStyleChange::removeMapItem(QDeclarativeGeoMapItemBase *item)
288 {
289     QList<QSharedPointer<QMapboxGLStyleChange>> changes;
290 
291     const QString id = getId(item);
292 
293     changes << QSharedPointer<QMapboxGLStyleChange>(new QMapboxGLStyleRemoveLayer(id));
294     changes << QSharedPointer<QMapboxGLStyleChange>(new QMapboxGLStyleRemoveSource(id));
295 
296     return changes;
297 }
298 
299 // QMapboxGLStyleSetLayoutProperty
300 
apply(QMapboxGL * map)301 void QMapboxGLStyleSetLayoutProperty::apply(QMapboxGL *map)
302 {
303     map->setLayoutProperty(m_layer, m_property, m_value);
304 }
305 
fromMapParameter(QGeoMapParameter * param)306 QList<QSharedPointer<QMapboxGLStyleChange>> QMapboxGLStyleSetLayoutProperty::fromMapParameter(QGeoMapParameter *param)
307 {
308     Q_ASSERT(param->type() == "layout");
309 
310     QList<QSharedPointer<QMapboxGLStyleChange>> changes;
311 
312     QList<QByteArray> propertyNames = getAllPropertyNamesList(param);
313     for (const QByteArray &propertyName : propertyNames) {
314         if (isImmutableProperty(propertyName))
315             continue;
316 
317         auto layout = new QMapboxGLStyleSetLayoutProperty();
318 
319         layout->m_value = param->property(propertyName);
320         if (layout->m_value.canConvert<QJSValue>()) {
321             layout->m_value = layout->m_value.value<QJSValue>().toVariant();
322         }
323 
324         layout->m_layer = param->property("layer").toString();
325         layout->m_property = formatPropertyName(propertyName);
326 
327         changes << QSharedPointer<QMapboxGLStyleChange>(layout);
328     }
329 
330     return changes;
331 }
332 
fromMapItem(QDeclarativeGeoMapItemBase * item)333 QList<QSharedPointer<QMapboxGLStyleChange>> QMapboxGLStyleSetLayoutProperty::fromMapItem(QDeclarativeGeoMapItemBase *item)
334 {
335     QList<QSharedPointer<QMapboxGLStyleChange>> changes;
336 
337     switch (item->itemType()) {
338     case QGeoMap::MapPolyline:
339         changes = fromMapItem(static_cast<QDeclarativePolylineMapItem *>(item));
340     default:
341         break;
342     }
343 
344     changes << QSharedPointer<QMapboxGLStyleChange>(
345         new QMapboxGLStyleSetLayoutProperty(getId(item), QStringLiteral("visibility"),
346             item->isVisible() ? QStringLiteral("visible") : QStringLiteral("none")));
347 
348     return changes;
349 }
350 
fromMapItem(QDeclarativePolylineMapItem * item)351 QList<QSharedPointer<QMapboxGLStyleChange>> QMapboxGLStyleSetLayoutProperty::fromMapItem(QDeclarativePolylineMapItem *item)
352 {
353     QList<QSharedPointer<QMapboxGLStyleChange>> changes;
354     changes.reserve(2);
355 
356     const QString id = getId(item);
357 
358     changes << QSharedPointer<QMapboxGLStyleChange>(
359         new QMapboxGLStyleSetLayoutProperty(id, QStringLiteral("line-cap"), QStringLiteral("square")));
360     changes << QSharedPointer<QMapboxGLStyleChange>(
361         new QMapboxGLStyleSetLayoutProperty(id, QStringLiteral("line-join"), QStringLiteral("bevel")));
362 
363     return changes;
364 }
365 
QMapboxGLStyleSetLayoutProperty(const QString & layer,const QString & property,const QVariant & value)366 QMapboxGLStyleSetLayoutProperty::QMapboxGLStyleSetLayoutProperty(const QString& layer, const QString& property, const QVariant &value)
367     : m_layer(layer), m_property(property), m_value(value)
368 {
369 }
370 
371 // QMapboxGLStyleSetPaintProperty
372 
QMapboxGLStyleSetPaintProperty(const QString & layer,const QString & property,const QVariant & value)373 QMapboxGLStyleSetPaintProperty::QMapboxGLStyleSetPaintProperty(const QString& layer, const QString& property, const QVariant &value)
374     : m_layer(layer), m_property(property), m_value(value)
375 {
376 }
377 
apply(QMapboxGL * map)378 void QMapboxGLStyleSetPaintProperty::apply(QMapboxGL *map)
379 {
380     map->setPaintProperty(m_layer, m_property, m_value);
381 }
382 
fromMapParameter(QGeoMapParameter * param)383 QList<QSharedPointer<QMapboxGLStyleChange>> QMapboxGLStyleSetPaintProperty::fromMapParameter(QGeoMapParameter *param)
384 {
385     Q_ASSERT(param->type() == "paint");
386 
387     QList<QSharedPointer<QMapboxGLStyleChange>> changes;
388 
389     QList<QByteArray> propertyNames = getAllPropertyNamesList(param);
390     for (const QByteArray &propertyName : propertyNames) {
391         if (isImmutableProperty(propertyName))
392             continue;
393 
394         auto paint = new QMapboxGLStyleSetPaintProperty();
395 
396         paint->m_value = param->property(propertyName);
397         if (paint->m_value.canConvert<QJSValue>()) {
398             paint->m_value = paint->m_value.value<QJSValue>().toVariant();
399         }
400 
401         paint->m_layer = param->property("layer").toString();
402         paint->m_property = formatPropertyName(propertyName);
403 
404         changes << QSharedPointer<QMapboxGLStyleChange>(paint);
405     }
406 
407     return changes;
408 }
409 
fromMapItem(QDeclarativeGeoMapItemBase * item)410 QList<QSharedPointer<QMapboxGLStyleChange>> QMapboxGLStyleSetPaintProperty::fromMapItem(QDeclarativeGeoMapItemBase *item)
411 {
412     switch (item->itemType()) {
413     case QGeoMap::MapRectangle:
414         return fromMapItem(static_cast<QDeclarativeRectangleMapItem *>(item));
415     case QGeoMap::MapCircle:
416         return fromMapItem(static_cast<QDeclarativeCircleMapItem *>(item));
417     case QGeoMap::MapPolygon:
418         return fromMapItem(static_cast<QDeclarativePolygonMapItem *>(item));
419     case QGeoMap::MapPolyline:
420         return fromMapItem(static_cast<QDeclarativePolylineMapItem *>(item));
421     default:
422         qWarning() << "Unsupported QGeoMap item type: " << item->itemType();
423         return QList<QSharedPointer<QMapboxGLStyleChange>>();
424     }
425 }
426 
fromMapItem(QDeclarativeRectangleMapItem * item)427 QList<QSharedPointer<QMapboxGLStyleChange>> QMapboxGLStyleSetPaintProperty::fromMapItem(QDeclarativeRectangleMapItem *item)
428 {
429     QList<QSharedPointer<QMapboxGLStyleChange>> changes;
430     changes.reserve(3);
431 
432     const QString id = getId(item);
433 
434     changes << QSharedPointer<QMapboxGLStyleChange>(
435         new QMapboxGLStyleSetPaintProperty(id, QStringLiteral("fill-opacity"), item->color().alphaF() * item->mapItemOpacity()));
436     changes << QSharedPointer<QMapboxGLStyleChange>(
437         new QMapboxGLStyleSetPaintProperty(id, QStringLiteral("fill-color"), item->color()));
438     changes << QSharedPointer<QMapboxGLStyleChange>(
439         new QMapboxGLStyleSetPaintProperty(id, QStringLiteral("fill-outline-color"), item->border()->color()));
440 
441     return changes;
442 }
443 
fromMapItem(QDeclarativeCircleMapItem * item)444 QList<QSharedPointer<QMapboxGLStyleChange>> QMapboxGLStyleSetPaintProperty::fromMapItem(QDeclarativeCircleMapItem *item)
445 {
446     QList<QSharedPointer<QMapboxGLStyleChange>> changes;
447     changes.reserve(3);
448 
449     const QString id = getId(item);
450 
451     changes << QSharedPointer<QMapboxGLStyleChange>(
452         new QMapboxGLStyleSetPaintProperty(id, QStringLiteral("fill-opacity"), item->color().alphaF() * item->mapItemOpacity()));
453     changes << QSharedPointer<QMapboxGLStyleChange>(
454         new QMapboxGLStyleSetPaintProperty(id, QStringLiteral("fill-color"), item->color()));
455     changes << QSharedPointer<QMapboxGLStyleChange>(
456         new QMapboxGLStyleSetPaintProperty(id, QStringLiteral("fill-outline-color"), item->border()->color()));
457 
458     return changes;
459 }
460 
fromMapItem(QDeclarativePolygonMapItem * item)461 QList<QSharedPointer<QMapboxGLStyleChange>> QMapboxGLStyleSetPaintProperty::fromMapItem(QDeclarativePolygonMapItem *item)
462 {
463     QList<QSharedPointer<QMapboxGLStyleChange>> changes;
464     changes.reserve(3);
465 
466     const QString id = getId(item);
467 
468     changes << QSharedPointer<QMapboxGLStyleChange>(
469         new QMapboxGLStyleSetPaintProperty(id, QStringLiteral("fill-opacity"), item->color().alphaF() * item->mapItemOpacity()));
470     changes << QSharedPointer<QMapboxGLStyleChange>(
471         new QMapboxGLStyleSetPaintProperty(id, QStringLiteral("fill-color"), item->color()));
472     changes << QSharedPointer<QMapboxGLStyleChange>(
473         new QMapboxGLStyleSetPaintProperty(id, QStringLiteral("fill-outline-color"), item->border()->color()));
474 
475     return changes;
476 }
477 
fromMapItem(QDeclarativePolylineMapItem * item)478 QList<QSharedPointer<QMapboxGLStyleChange>> QMapboxGLStyleSetPaintProperty::fromMapItem(QDeclarativePolylineMapItem *item)
479 {
480     QList<QSharedPointer<QMapboxGLStyleChange>> changes;
481     changes.reserve(3);
482 
483     const QString id = getId(item);
484 
485     changes << QSharedPointer<QMapboxGLStyleChange>(
486         new QMapboxGLStyleSetPaintProperty(id, QStringLiteral("line-opacity"), item->line()->color().alphaF() * item->mapItemOpacity()));
487     changes << QSharedPointer<QMapboxGLStyleChange>(
488         new QMapboxGLStyleSetPaintProperty(id, QStringLiteral("line-color"), item->line()->color()));
489     changes << QSharedPointer<QMapboxGLStyleChange>(
490         new QMapboxGLStyleSetPaintProperty(id, QStringLiteral("line-width"), item->line()->width()));
491 
492     return changes;
493 }
494 
495 // QMapboxGLStyleAddLayer
496 
apply(QMapboxGL * map)497 void QMapboxGLStyleAddLayer::apply(QMapboxGL *map)
498 {
499     map->addLayer(m_params, m_before);
500 }
501 
fromMapParameter(QGeoMapParameter * param)502 QSharedPointer<QMapboxGLStyleChange> QMapboxGLStyleAddLayer::fromMapParameter(QGeoMapParameter *param)
503 {
504     Q_ASSERT(param->type() == "layer");
505 
506     auto layer = new QMapboxGLStyleAddLayer();
507 
508     static const QStringList layerProperties = QStringList()
509         << QStringLiteral("name") << QStringLiteral("layerType") << QStringLiteral("before");
510 
511     QList<QByteArray> propertyNames = getAllPropertyNamesList(param);
512     for (const QByteArray &propertyName : propertyNames) {
513         if (isImmutableProperty(propertyName))
514             continue;
515 
516         const QVariant value = param->property(propertyName);
517 
518         switch (layerProperties.indexOf(propertyName)) {
519         case -1:
520             layer->m_params[formatPropertyName(propertyName)] = value;
521             break;
522         case 0: // name
523             layer->m_params[QStringLiteral("id")] = value;
524             break;
525         case 1: // layerType
526             layer->m_params[QStringLiteral("type")] = value;
527             break;
528         case 2: // before
529             layer->m_before = value.toString();
530             break;
531         }
532     }
533 
534     return QSharedPointer<QMapboxGLStyleChange>(layer);
535 }
536 
fromFeature(const QMapbox::Feature & feature,const QString & before)537 QSharedPointer<QMapboxGLStyleChange> QMapboxGLStyleAddLayer::fromFeature(const QMapbox::Feature &feature, const QString &before)
538 {
539     auto layer = new QMapboxGLStyleAddLayer();
540     layer->m_params[QStringLiteral("id")] = feature.id;
541     layer->m_params[QStringLiteral("source")] = feature.id;
542 
543     switch (feature.type) {
544     case QMapbox::Feature::PointType:
545         layer->m_params[QStringLiteral("type")] = QStringLiteral("circle");
546         break;
547     case QMapbox::Feature::LineStringType:
548         layer->m_params[QStringLiteral("type")] = QStringLiteral("line");
549         break;
550     case QMapbox::Feature::PolygonType:
551         layer->m_params[QStringLiteral("type")] = QStringLiteral("fill");
552         break;
553     }
554 
555     layer->m_before = before;
556 
557     return QSharedPointer<QMapboxGLStyleChange>(layer);
558 }
559 
560 
561 // QMapboxGLStyleRemoveLayer
562 
apply(QMapboxGL * map)563 void QMapboxGLStyleRemoveLayer::apply(QMapboxGL *map)
564 {
565     map->removeLayer(m_id);
566 }
567 
QMapboxGLStyleRemoveLayer(const QString & id)568 QMapboxGLStyleRemoveLayer::QMapboxGLStyleRemoveLayer(const QString &id) : m_id(id)
569 {
570 }
571 
572 
573 // QMapboxGLStyleAddSource
574 
apply(QMapboxGL * map)575 void QMapboxGLStyleAddSource::apply(QMapboxGL *map)
576 {
577     map->updateSource(m_id, m_params);
578 }
579 
fromMapParameter(QGeoMapParameter * param)580 QSharedPointer<QMapboxGLStyleChange> QMapboxGLStyleAddSource::fromMapParameter(QGeoMapParameter *param)
581 {
582     Q_ASSERT(param->type() == "source");
583 
584     static const QStringList acceptedSourceTypes = QStringList()
585         << QStringLiteral("vector") << QStringLiteral("raster") << QStringLiteral("raster-dem") << QStringLiteral("geojson") << QStringLiteral("image");
586 
587     QString sourceType = param->property("sourceType").toString();
588 
589     auto source = new QMapboxGLStyleAddSource();
590     source->m_id = param->property("name").toString();
591     source->m_params[QStringLiteral("type")] = sourceType;
592 
593     switch (acceptedSourceTypes.indexOf(sourceType)) {
594     case -1:
595         qWarning() << "Invalid value for property 'sourceType': " + sourceType;
596         break;
597     case 0: // vector
598     case 1: // raster
599     case 2: // raster-dem
600         source->m_params[QStringLiteral("url")] = param->property("url");
601         break;
602     case 3: { // geojson
603         auto data = param->property("data").toString();
604         if (data.startsWith(':')) {
605             QFile geojson(data);
606             geojson.open(QIODevice::ReadOnly);
607             source->m_params[QStringLiteral("data")] = geojson.readAll();
608         } else {
609             source->m_params[QStringLiteral("data")] = data.toUtf8();
610         }
611     } break;
612     case 4: { // image
613         source->m_params[QStringLiteral("url")] = param->property("url");
614         source->m_params[QStringLiteral("coordinates")] = param->property("coordinates");
615     } break;
616     }
617 
618     return QSharedPointer<QMapboxGLStyleChange>(source);
619 }
620 
fromFeature(const QMapbox::Feature & feature)621 QSharedPointer<QMapboxGLStyleChange> QMapboxGLStyleAddSource::fromFeature(const QMapbox::Feature &feature)
622 {
623     auto source = new QMapboxGLStyleAddSource();
624 
625     source->m_id = feature.id.toString();
626     source->m_params[QStringLiteral("type")] = QStringLiteral("geojson");
627     source->m_params[QStringLiteral("data")] = QVariant::fromValue<QMapbox::Feature>(feature);
628 
629     return QSharedPointer<QMapboxGLStyleChange>(source);
630 }
631 
fromMapItem(QDeclarativeGeoMapItemBase * item)632 QSharedPointer<QMapboxGLStyleChange> QMapboxGLStyleAddSource::fromMapItem(QDeclarativeGeoMapItemBase *item)
633 {
634     return fromFeature(featureFromMapItem(item));
635 }
636 
637 
638 // QMapboxGLStyleRemoveSource
639 
apply(QMapboxGL * map)640 void QMapboxGLStyleRemoveSource::apply(QMapboxGL *map)
641 {
642     map->removeSource(m_id);
643 }
644 
QMapboxGLStyleRemoveSource(const QString & id)645 QMapboxGLStyleRemoveSource::QMapboxGLStyleRemoveSource(const QString &id) : m_id(id)
646 {
647 }
648 
649 
650 // QMapboxGLStyleSetFilter
651 
apply(QMapboxGL * map)652 void QMapboxGLStyleSetFilter::apply(QMapboxGL *map)
653 {
654     map->setFilter(m_layer, m_filter);
655 }
656 
fromMapParameter(QGeoMapParameter * param)657 QSharedPointer<QMapboxGLStyleChange> QMapboxGLStyleSetFilter::fromMapParameter(QGeoMapParameter *param)
658 {
659     Q_ASSERT(param->type() == "filter");
660 
661     auto filter = new QMapboxGLStyleSetFilter();
662     filter->m_layer = param->property("layer").toString();
663     filter->m_filter = param->property("filter");
664 
665     return QSharedPointer<QMapboxGLStyleChange>(filter);
666 }
667 
668 
669 // QMapboxGLStyleAddImage
670 
apply(QMapboxGL * map)671 void QMapboxGLStyleAddImage::apply(QMapboxGL *map)
672 {
673     map->addImage(m_name, m_sprite);
674 }
675 
fromMapParameter(QGeoMapParameter * param)676 QSharedPointer<QMapboxGLStyleChange> QMapboxGLStyleAddImage::fromMapParameter(QGeoMapParameter *param)
677 {
678     Q_ASSERT(param->type() == "image");
679 
680     auto image = new QMapboxGLStyleAddImage();
681     image->m_name = param->property("name").toString();
682     image->m_sprite = QImage(param->property("sprite").toString());
683 
684     return QSharedPointer<QMapboxGLStyleChange>(image);
685 }
686