1 #pragma once
2 
3 #include <mbgl/style/conversion.hpp>
4 #include <mbgl/style/conversion/geojson.hpp>
5 #include <mbgl/util/optional.hpp>
6 
7 #include <QVariant>
8 #include <QColor>
9 #include <QMapbox>
10 #include "qt_geojson.hpp"
11 
12 namespace mbgl {
13 namespace style {
14 namespace conversion {
15 
16 template <>
17 class ConversionTraits<QVariant> {
18 public:
isUndefined(const QVariant & value)19     static bool isUndefined(const QVariant& value) {
20         return value.isNull() || !value.isValid();
21     }
22 
isArray(const QVariant & value)23     static bool isArray(const QVariant& value) {
24         return value.canConvert(QVariant::List);
25     }
26 
arrayLength(const QVariant & value)27     static std::size_t arrayLength(const QVariant& value) {
28         return value.toList().size();
29     }
30 
arrayMember(const QVariant & value,std::size_t i)31     static QVariant arrayMember(const QVariant& value, std::size_t i) {
32         return value.toList()[i];
33     }
34 
isObject(const QVariant & value)35     static bool isObject(const QVariant& value) {
36         return value.canConvert(QVariant::Map)
37             || value.type() == QVariant::ByteArray
38     #if QT_VERSION >= 0x050000
39             || QString(value.typeName()) == QStringLiteral("QMapbox::Feature");
40     #else
41             || QString(value.typeName()) == QString("QMapbox::Feature");
42     #endif
43     }
44 
objectMember(const QVariant & value,const char * key)45     static optional<QVariant> objectMember(const QVariant& value, const char* key) {
46         auto map = value.toMap();
47         auto iter = map.constFind(key);
48 
49         if (iter != map.constEnd()) {
50             return iter.value();
51         } else {
52             return {};
53         }
54     }
55 
56     template <class Fn>
eachMember(const QVariant & value,Fn && fn)57     static optional<Error> eachMember(const QVariant& value, Fn&& fn) {
58         auto map = value.toMap();
59         auto iter = map.constBegin();
60 
61         while (iter != map.constEnd()) {
62             optional<Error> result = fn(iter.key().toStdString(), QVariant(iter.value()));
63             if (result) {
64                 return result;
65             }
66 
67             ++iter;
68         }
69 
70         return {};
71     }
72 
toBool(const QVariant & value)73     static optional<bool> toBool(const QVariant& value) {
74         if (value.type() == QVariant::Bool) {
75             return value.toBool();
76         } else {
77             return {};
78         }
79     }
80 
toNumber(const QVariant & value)81     static optional<float> toNumber(const QVariant& value) {
82         if (value.type() == QVariant::Int || value.type() == QVariant::Double) {
83             return value.toFloat();
84         } else {
85             return {};
86         }
87     }
88 
toDouble(const QVariant & value)89     static optional<double> toDouble(const QVariant& value) {
90         if (value.type() == QVariant::Int || value.type() == QVariant::Double) {
91             return value.toDouble();
92         } else {
93             return {};
94         }
95     }
96 
toString(const QVariant & value)97     static optional<std::string> toString(const QVariant& value) {
98         if (value.type() == QVariant::String) {
99             return value.toString().toStdString();
100         } else if (value.type() == QVariant::Color) {
101             return value.value<QColor>().name().toStdString();
102         } else {
103             return {};
104         }
105     }
106 
toValue(const QVariant & value)107     static optional<Value> toValue(const QVariant& value) {
108         if (value.type() == QVariant::Bool) {
109             return { value.toBool() };
110         } else if (value.type() == QVariant::String) {
111             return { value.toString().toStdString() };
112         } else if (value.type() == QVariant::Color) {
113             return { value.value<QColor>().name().toStdString() };
114         } else if (value.type() == QVariant::Int) {
115             return { int64_t(value.toInt()) };
116         } else if (value.canConvert(QVariant::Double)) {
117             return { value.toDouble() };
118         } else {
119             return {};
120         }
121     }
122 
toGeoJSON(const QVariant & value,Error & error)123     static optional<GeoJSON> toGeoJSON(const QVariant& value, Error& error) {
124     #if QT_VERSION >= 0x050000
125         if (value.typeName() == QStringLiteral("QMapbox::Feature")) {
126     #else
127         if (value.typeName() == QString("QMapbox::Feature")) {
128     #endif
129             return GeoJSON { asMapboxGLFeature(value.value<QMapbox::Feature>()) };
130         } else if (value.type() != QVariant::ByteArray) {
131             error = { "JSON data must be in QByteArray" };
132             return {};
133         }
134 
135         QByteArray data = value.toByteArray();
136         return parseGeoJSON(std::string(data.constData(), data.size()), error);
137     }
138 };
139 
140 template <class T, class...Args>
convert(const QVariant & value,Error & error,Args &&...args)141 optional<T> convert(const QVariant& value, Error& error, Args&&...args) {
142     return convert<T>(Convertible(value), error, std::forward<Args>(args)...);
143 }
144 
145 } // namespace conversion
146 } // namespace style
147 } // namespace mbgl
148