diff options
author | John Firebaugh <john.firebaugh@gmail.com> | 2017-09-26 14:14:44 -0700 |
---|---|---|
committer | John Firebaugh <john.firebaugh@gmail.com> | 2017-10-23 09:56:43 -0700 |
commit | 4b2e1cddb4645fb6d2c5f9634dbeb7c21516cede (patch) | |
tree | f11bd97ea5abf17bc6956f254796f1e8cf4a96f9 /platform | |
parent | bb58ddaea8c2d9da9551601318944d9d143ee247 (diff) | |
download | qtlocation-mapboxgl-4b2e1cddb4645fb6d2c5f9634dbeb7c21516cede.tar.gz |
Replace compile-time polymorphism with runtime polymorphism in the conversion system
Diffstat (limited to 'platform')
-rw-r--r-- | platform/android/config.cmake | 1 | ||||
-rw-r--r-- | platform/android/src/style/android_conversion.hpp | 147 | ||||
-rw-r--r-- | platform/android/src/style/conversion/filter.hpp | 3 | ||||
-rw-r--r-- | platform/android/src/style/conversion/geojson.hpp | 24 | ||||
-rw-r--r-- | platform/android/src/style/conversion/url_or_tileset.hpp | 9 | ||||
-rw-r--r-- | platform/android/src/style/layers/layer.cpp | 21 | ||||
-rw-r--r-- | platform/android/src/style/sources/geojson_source.cpp | 10 | ||||
-rw-r--r-- | platform/android/src/style/value.cpp | 2 | ||||
-rw-r--r-- | platform/android/src/style/value.hpp | 8 | ||||
-rw-r--r-- | platform/darwin/src/MGLConversion.h | 223 | ||||
-rw-r--r-- | platform/darwin/src/MGLStyleValue_Private.h | 4 | ||||
-rw-r--r-- | platform/node/src/node_conversion.hpp | 183 | ||||
-rw-r--r-- | platform/node/src/node_geojson.hpp | 17 | ||||
-rw-r--r-- | platform/node/src/node_map.cpp | 14 | ||||
-rw-r--r-- | platform/qt/qt.cmake | 3 | ||||
-rw-r--r-- | platform/qt/src/qmapboxgl.cpp | 10 | ||||
-rw-r--r-- | platform/qt/src/qt_conversion.hpp | 191 | ||||
-rw-r--r-- | platform/qt/src/qt_geojson.cpp | 166 | ||||
-rw-r--r-- | platform/qt/src/qt_geojson.hpp | 194 |
19 files changed, 644 insertions, 586 deletions
diff --git a/platform/android/config.cmake b/platform/android/config.cmake index 8dd537d36e..db75b850f1 100644 --- a/platform/android/config.cmake +++ b/platform/android/config.cmake @@ -146,7 +146,6 @@ add_library(mbgl-android STATIC # Style conversion Java -> C++ platform/android/src/style/android_conversion.hpp - platform/android/src/style/conversion/geojson.hpp platform/android/src/style/value.cpp platform/android/src/style/value.hpp platform/android/src/style/conversion/url_or_tileset.hpp diff --git a/platform/android/src/style/android_conversion.hpp b/platform/android/src/style/android_conversion.hpp index 082fe411e2..510a9f8444 100644 --- a/platform/android/src/style/android_conversion.hpp +++ b/platform/android/src/style/android_conversion.hpp @@ -4,8 +4,9 @@ #include <mbgl/util/feature.hpp> #include <mbgl/util/logging.hpp> -#include <mbgl/style/conversion.hpp> #include <mbgl/util/optional.hpp> +#include <mbgl/style/conversion.hpp> +#include <mbgl/style/conversion/geojson.hpp> #include <jni/jni.hpp> @@ -13,89 +14,105 @@ namespace mbgl { namespace style { namespace conversion { -inline bool isUndefined(const mbgl::android::Value& value) { - return value.isNull(); -} +template <> +class ConversionTraits<mbgl::android::Value> { +public: + static bool isUndefined(const mbgl::android::Value& value) { + return value.isNull(); + } -inline bool isArray(const mbgl::android::Value& value) { - return value.isArray(); -} + static bool isArray(const mbgl::android::Value& value) { + return value.isArray(); + } -inline bool isObject(const mbgl::android::Value& value) { - return value.isObject(); -} + static bool isObject(const mbgl::android::Value& value) { + return value.isObject(); + } -inline std::size_t arrayLength(const mbgl::android::Value& value) { - return value.getLength();; -} + static std::size_t arrayLength(const mbgl::android::Value& value) { + return value.getLength();; + } -inline mbgl::android::Value arrayMember(const mbgl::android::Value& value, std::size_t i) { - return value.get(i); -} + static mbgl::android::Value arrayMember(const mbgl::android::Value& value, std::size_t i) { + return value.get(i); + } -inline optional<mbgl::android::Value> objectMember(const mbgl::android::Value& value, const char* key) { - mbgl::android::Value member = value.get(key); + static optional<mbgl::android::Value> objectMember(const mbgl::android::Value& value, const char* key) { + mbgl::android::Value member = value.get(key); + if (!member.isNull()) { + return member; + } else { + return {}; + } + } - if (!member.isNull()) { - return member; - } else { + template <class Fn> + static optional<Error> eachMember(const mbgl::android::Value&, Fn&&) { + // TODO + mbgl::Log::Warning(mbgl::Event::Android, "eachMember not implemented"); return {}; } -} -template <class Fn> -optional<Error> eachMember(const mbgl::android::Value&, Fn&&) { - // TODO - mbgl::Log::Warning(mbgl::Event::Android, "eachMember not implemented"); - return {}; -} + static optional<bool> toBool(const mbgl::android::Value& value) { + if (value.isBool()) { + return value.toBool(); + } else { + return {}; + } + } -inline optional<bool> toBool(const mbgl::android::Value& value) { - if (value.isBool()) { - return value.toBool(); - } else { - return {}; + static optional<float> toNumber(const mbgl::android::Value& value) { + if (value.isNumber()) { + auto num = value.toFloat(); + return num; + } else { + return {}; + } } -} -inline optional<float> toNumber(const mbgl::android::Value& value) { - if (value.isNumber()) { - auto num = value.toFloat(); - return num; - } else { - return {}; + static optional<double> toDouble(const mbgl::android::Value& value) { + if (value.isNumber()) { + return value.toDouble(); + } else { + return {}; + } } -} -inline optional<double> toDouble(const mbgl::android::Value& value) { - if (value.isNumber()) { - return value.toDouble(); - } else { - return {}; + static optional<std::string> toString(const mbgl::android::Value& value) { + if (value.isString()) { + return value.toString(); + } else { + return {}; + } } -} -inline optional<std::string> toString(const mbgl::android::Value& value) { - if (value.isString()) { - return value.toString(); - } else { - return {}; + static optional<Value> toValue(const mbgl::android::Value& value) { + if (value.isNull()) { + return {}; + } else if (value.isBool()) { + return { value.toBool() }; + } else if (value.isString()) { + return { value.toString() }; + } else if (value.isNumber()) { + auto doubleVal = value.toDouble(); + return { doubleVal - (int) doubleVal > 0.0 ? doubleVal : value.toLong() }; + } else { + return {}; + } } -} -inline optional<Value> toValue(const mbgl::android::Value& value) { - if (value.isNull()) { - return {}; - } else if (value.isBool()) { - return { value.toBool() }; - } else if (value.isString()) { - return { value.toString() }; - } else if (value.isNumber()) { - auto doubleVal = value.toDouble(); - return { doubleVal - (int) doubleVal > 0.0 ? doubleVal : value.toLong() }; - } else { - return {}; + static optional<GeoJSON> toGeoJSON(const mbgl::android::Value &value, Error &error) { + if (value.isNull() || !value.isString()) { + error = { "no json data found" }; + return {}; + } + return parseGeoJSON(value.toString(), error); } +}; + +template <class T, class...Args> +optional<T> convert(mbgl::android::Value&& value, Error& error, Args&&...args) { + return convert<T>(Convertible(std::move(value)), error, std::forward<Args>(args)...); } } // namespace conversion diff --git a/platform/android/src/style/conversion/filter.hpp b/platform/android/src/style/conversion/filter.hpp index 1f0abcf3a4..c154e88e7c 100644 --- a/platform/android/src/style/conversion/filter.hpp +++ b/platform/android/src/style/conversion/filter.hpp @@ -16,9 +16,8 @@ namespace conversion { inline optional<mbgl::style::Filter> toFilter(jni::JNIEnv& env, jni::Array<jni::Object<>> jfilter) { mbgl::optional<mbgl::style::Filter> filter; if (jfilter) { - Value filterValue(env, jfilter); mbgl::style::conversion::Error error; - auto converted = mbgl::style::conversion::convert<mbgl::style::Filter>(filterValue, error); + auto converted = mbgl::style::conversion::convert<mbgl::style::Filter>(Value(env, jfilter), error); if (!converted) { mbgl::Log::Error(mbgl::Event::JNI, "Error converting filter: " + error.message); } diff --git a/platform/android/src/style/conversion/geojson.hpp b/platform/android/src/style/conversion/geojson.hpp deleted file mode 100644 index 748fe7361e..0000000000 --- a/platform/android/src/style/conversion/geojson.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include <mapbox/geojson.hpp> -#include <mbgl/style/conversion.hpp> -#include <mbgl/style/conversion/geojson.hpp> -#include <jni/jni.hpp> - -namespace mbgl { -namespace style { -namespace conversion { - -template <> -optional<GeoJSON> Converter<GeoJSON>::operator()(const mbgl::android::Value& value, Error& error) const { - if(value.isNull() || !value.isString()) { - error = { "no json data found" }; - return {}; - } - - return convert<GeoJSON>(value.toString(), error); -} - -} // namespace conversion -} // namespace style -} // namespace mbgl diff --git a/platform/android/src/style/conversion/url_or_tileset.hpp b/platform/android/src/style/conversion/url_or_tileset.hpp index 00ef913d41..92c1182a63 100644 --- a/platform/android/src/style/conversion/url_or_tileset.hpp +++ b/platform/android/src/style/conversion/url_or_tileset.hpp @@ -17,18 +17,19 @@ namespace android { // This conversion is expected not to fail because it's used only in contexts where // the value was originally a String or TileSet object on the Java side. If it fails // to convert, it's a bug in our serialization or Java-side static typing. -inline variant<std::string, Tileset> convertURLOrTileset(const Value& value) { +inline variant<std::string, Tileset> convertURLOrTileset(mbgl::android::Value&& value) { using namespace mbgl::style::conversion; - if (isObject(value)) { + const Convertible convertible(std::move(value)); + if (isObject(convertible)) { Error error; - optional<Tileset> tileset = convert<Tileset>(value, error); + optional<Tileset> tileset = convert<Tileset>(convertible, error); if (!tileset) { throw std::logic_error(error.message); } return { *tileset }; } else { - return { *toString(value) }; + return { *toString(convertible) }; } } diff --git a/platform/android/src/style/layers/layer.cpp b/platform/android/src/style/layers/layer.cpp index 02a1f0be82..31032b117f 100644 --- a/platform/android/src/style/layers/layer.cpp +++ b/platform/android/src/style/layers/layer.cpp @@ -4,11 +4,20 @@ #include <jni/jni.hpp> #include <mbgl/style/style.hpp> +#include <mbgl/style/filter.hpp> #include <mbgl/style/transition_options.hpp> +#include <mbgl/style/layers/background_layer.hpp> +#include <mbgl/style/layers/circle_layer.hpp> +#include <mbgl/style/layers/fill_layer.hpp> +#include <mbgl/style/layers/fill_extrusion_layer.hpp> +#include <mbgl/style/layers/line_layer.hpp> +#include <mbgl/style/layers/raster_layer.hpp> +#include <mbgl/style/layers/symbol_layer.hpp> #include <mbgl/util/logging.hpp> // Java -> C++ conversion #include <mbgl/style/conversion.hpp> +#include <mbgl/style/conversion/filter.hpp> #include <mbgl/style/conversion/layer.hpp> #include <mbgl/style/conversion/source.hpp> @@ -78,10 +87,8 @@ namespace android { } void Layer::setLayoutProperty(jni::JNIEnv& env, jni::String jname, jni::Object<> jvalue) { - Value value(env, jvalue); - // Convert and set property - optional<mbgl::style::conversion::Error> error = mbgl::style::conversion::setLayoutProperty(layer, jni::Make<std::string>(env, jname), value); + optional<mbgl::style::conversion::Error> error = mbgl::style::conversion::setLayoutProperty(layer, jni::Make<std::string>(env, jname), Value(env, jvalue)); if (error) { mbgl::Log::Error(mbgl::Event::JNI, "Error setting property: " + jni::Make<std::string>(env, jname) + " " + error->message); return; @@ -89,10 +96,8 @@ namespace android { } void Layer::setPaintProperty(jni::JNIEnv& env, jni::String jname, jni::Object<> jvalue) { - Value value(env, jvalue); - // Convert and set property - optional<mbgl::style::conversion::Error> error = mbgl::style::conversion::setPaintProperty(layer, jni::Make<std::string>(env, jname), value); + optional<mbgl::style::conversion::Error> error = mbgl::style::conversion::setPaintProperty(layer, jni::Make<std::string>(env, jname), Value(env, jvalue)); if (error) { mbgl::Log::Error(mbgl::Event::JNI, "Error setting property: " + jni::Make<std::string>(env, jname) + " " + error->message); return; @@ -116,10 +121,8 @@ namespace android { using namespace mbgl::style; using namespace mbgl::style::conversion; - Value wrapped(env, jfilter); - Error error; - optional<Filter> converted = convert<Filter>(wrapped, error); + optional<Filter> converted = convert<Filter>(Value(env, jfilter), error); if (!converted) { mbgl::Log::Error(mbgl::Event::JNI, "Error setting filter: " + error.message); return; diff --git a/platform/android/src/style/sources/geojson_source.cpp b/platform/android/src/style/sources/geojson_source.cpp index 90ef851eba..4468b453f3 100644 --- a/platform/android/src/style/sources/geojson_source.cpp +++ b/platform/android/src/style/sources/geojson_source.cpp @@ -5,15 +5,15 @@ // Java -> C++ conversion #include "../android_conversion.hpp" #include "../conversion/filter.hpp" -#include "../conversion/geojson.hpp" +#include <mbgl/style/conversion.hpp> +#include <mbgl/style/conversion/geojson.hpp> +#include <mbgl/style/conversion/geojson_options.hpp> // C++ -> Java conversion #include "../../conversion/conversion.hpp" #include "../../conversion/collection.hpp" #include "../../geojson/conversion/feature.hpp" #include "../conversion/url_or_tileset.hpp" -#include <mbgl/style/conversion.hpp> -#include <mbgl/style/conversion/geojson_options.hpp> #include <string> @@ -29,7 +29,7 @@ namespace android { return style::GeoJSONOptions(); } Error error; - optional<style::GeoJSONOptions> result = convert<style::GeoJSONOptions>(Value(env, options), error); + optional<style::GeoJSONOptions> result = convert<style::GeoJSONOptions>(mbgl::android::Value(env, options), error); if (!result) { throw std::logic_error(error.message); } @@ -54,7 +54,7 @@ namespace android { // Convert the jni object Error error; - optional<GeoJSON> converted = convert<GeoJSON>(Value(env, json), error); + optional<GeoJSON> converted = convert<GeoJSON>(mbgl::android::Value(env, json), error); if(!converted) { mbgl::Log::Error(mbgl::Event::JNI, "Error setting geo json: " + error.message); return; diff --git a/platform/android/src/style/value.cpp b/platform/android/src/style/value.cpp index e1cd81d7fd..70bdea6677 100644 --- a/platform/android/src/style/value.cpp +++ b/platform/android/src/style/value.cpp @@ -24,8 +24,6 @@ namespace android { Value::Value(jni::JNIEnv& _env, jni::jobject* _value) : env(_env), value(_value, ObjectDeleter(env)) {} - Value::~Value() = default; - bool Value::isNull() const { return value == nullptr; } diff --git a/platform/android/src/style/value.hpp b/platform/android/src/style/value.hpp index 7464bae832..2057b93454 100644 --- a/platform/android/src/style/value.hpp +++ b/platform/android/src/style/value.hpp @@ -9,9 +9,13 @@ namespace android { class Value { public: - Value(jni::JNIEnv&, jni::jobject*); - virtual ~Value(); + + Value(Value&&) = default; + Value& operator=(Value&&) = default; + + Value(const Value&) = delete; + Value& operator=(const Value&) = delete; bool isNull() const; bool isArray() const; diff --git a/platform/darwin/src/MGLConversion.h b/platform/darwin/src/MGLConversion.h index d6363b28eb..0d18d4e716 100644 --- a/platform/darwin/src/MGLConversion.h +++ b/platform/darwin/src/MGLConversion.h @@ -1,9 +1,4 @@ -#import <Foundation/Foundation.h> - -#include <mbgl/util/logging.hpp> #include <mbgl/style/conversion.hpp> -#include <mbgl/util/feature.hpp> -#include <mbgl/util/optional.hpp> NS_ASSUME_NONNULL_BEGIN @@ -11,128 +6,147 @@ namespace mbgl { namespace style { namespace conversion { -/** - A minimal wrapper class conforming to the requirements for `objectMember(v, name)` (see mbgl/style/conversion.hpp) - This is necessary because using `NSObject*` as the value type in `optional<NSObject*>` causes problems for the ARC, - due to things like `optional(const value_type& __v)` - */ -class OptionalNSObjectValue { +// A wrapper class for `id`, so as not to confuse ARC. +class Holder { public: - OptionalNSObjectValue(NSObject * _Nullable _value) : value(_value) {} - - explicit operator bool() const { - return value; + Holder(const id v) : value(v) {} + const id value; +}; + +template <> +class ConversionTraits<Holder> { +public: + static bool isUndefined(const Holder& holder) { + const id value = holder.value; + return !value || value == [NSNull null]; } - - NSObject * _Nullable operator*() { - NSCAssert(this, @"Expected non-null value."); - return value; + + static bool isArray(const Holder& holder) { + const id value = holder.value; + return [value isKindOfClass:[NSArray class]]; } -private: - NSObject * _Nullable value; -}; -inline bool isUndefined(const id value) { - return !value || value == [NSNull null]; -} + static bool isObject(const Holder& holder) { + const id value = holder.value; + return [value isKindOfClass:[NSDictionary class]]; + } -inline bool isArray(const id value) { - return [value isKindOfClass:[NSArray class]]; -} + static std::size_t arrayLength(const Holder& holder) { + const id value = holder.value; + NSCAssert([value isKindOfClass:[NSArray class]], @"Value must be an NSArray for getLength()."); + NSArray *array = value; + auto length = [array count]; + NSCAssert(length <= std::numeric_limits<size_t>::max(), @"Array length out of bounds."); + return length; + } -inline bool isObject(const id value) { - return [value isKindOfClass:[NSDictionary class]]; -} + static Holder arrayMember(const Holder& holder, std::size_t i) { + const id value = holder.value; + NSCAssert([value isKindOfClass:[NSArray class]], @"Value must be an NSArray for get(int)."); + NSCAssert(i < NSUIntegerMax, @"Index must be less than NSUIntegerMax"); + return {[value objectAtIndex: i]}; + } -inline std::size_t arrayLength(const id value) { - NSCAssert([value isKindOfClass:[NSArray class]], @"Value must be an NSArray for getLength()."); - NSArray *array = value; - auto length = [array count]; - NSCAssert(length <= std::numeric_limits<size_t>::max(), @"Array length out of bounds."); - return length; -} + static optional<Holder> objectMember(const Holder& holder, const char *key) { + const id value = holder.value; + NSCAssert([value isKindOfClass:[NSDictionary class]], @"Value must be an NSDictionary for get(string)."); + NSObject *member = [value objectForKey: @(key)]; + if (member && member != [NSNull null]) { + return {member}; + } else { + return {}; + } + } -inline NSObject *arrayMember(const id value, std::size_t i) { - NSCAssert([value isKindOfClass:[NSArray class]], @"Value must be an NSArray for get(int)."); - NSCAssert(i < NSUIntegerMax, @"Index must be less than NSUIntegerMax"); - return [value objectAtIndex: i]; -} +// Compiler is wrong about `Fn` parameter missing a nullability specifier. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnullability-completeness" + template <class Fn> + static optional<Error> eachMember(const Holder&, Fn&&) { +#pragma clang diagnostic pop + // Not implemented (unneeded for MGLStyleFunction conversion). + NSCAssert(NO, @"eachMember not implemented"); + return {}; + } -inline OptionalNSObjectValue objectMember(const id value, const char *key) { - NSCAssert([value isKindOfClass:[NSDictionary class]], @"Value must be an NSDictionary for get(string)."); - NSObject *member = [value objectForKey: @(key)]; - if (member && member != [NSNull null]) { - return { member }; - } else { - return { nullptr }; + static optional<bool> toBool(const Holder& holder) { + const id value = holder.value; + if (_isBool(value)) { + return ((NSNumber *)value).boolValue; + } else { + return {}; + } } -} -// Not implemented (unneeded for MGLStyleFunction conversion): -// optional<Error> eachMember(const NSObject*, Fn&&) + static optional<float> toNumber(const Holder& holder) { + const id value = holder.value; + if (_isNumber(value)) { + return ((NSNumber *)value).floatValue; + } else { + return {}; + } + } -inline bool _isBool(const id value) { - if (![value isKindOfClass:[NSNumber class]]) return false; - // char: 32-bit boolean - // BOOL: 64-bit boolean - NSNumber *number = value; - return ((strcmp([number objCType], @encode(char)) == 0) || - (strcmp([number objCType], @encode(BOOL)) == 0)); -} - -inline bool _isNumber(const id value) { - return [value isKindOfClass:[NSNumber class]] && !_isBool(value); -} - -inline bool _isString(const id value) { - return [value isKindOfClass:[NSString class]]; -} + static optional<double> toDouble(const Holder& holder) { + const id value = holder.value; + if (_isNumber(value)) { + return ((NSNumber *)value).doubleValue; + } else { + return {}; + } + } -inline optional<bool> toBool(const id value) { - if (_isBool(value)) { - return ((NSNumber *)value).boolValue; - } else { - return {}; + static optional<std::string> toString(const Holder& holder) { + const id value = holder.value; + if (_isString(value)) { + return std::string(static_cast<const char *>([value UTF8String])); + } else { + return {}; + } } -} -inline optional<float> toNumber(const id value) { - if (_isNumber(value)) { - return ((NSNumber *)value).floatValue; - } else { - return {}; + static optional<mbgl::Value> toValue(const Holder& holder) { + const id value = holder.value; + if (isUndefined(value)) { + return {}; + } else if (_isBool(value)) { + return { *toBool(holder) }; + } else if ( _isString(value)) { + return { *toString(holder) }; + } else if (_isNumber(value)) { + // Need to cast to a double here as the float is otherwise considered a bool... + return { static_cast<double>(*toNumber(holder)) }; + } else { + return {}; + } } -} -inline optional<double> toDouble(const id value) { - if (_isNumber(value)) { - return ((NSNumber *)value).doubleValue; - } else { + static optional<GeoJSON> toGeoJSON(const Holder& holder, Error& error) { + error = { "toGeoJSON not implemented" }; return {}; } -} -inline optional<std::string> toString(const id value) { - if (_isString(value)) { - return std::string(static_cast<const char *>([value UTF8String])); - } else { - return {}; +private: + static bool _isBool(const id value) { + if (![value isKindOfClass:[NSNumber class]]) return false; + // char: 32-bit boolean + // BOOL: 64-bit boolean + NSNumber *number = value; + return ((strcmp([number objCType], @encode(char)) == 0) || + (strcmp([number objCType], @encode(BOOL)) == 0)); } -} -inline optional<mbgl::Value> toValue(const id value) { - if (isUndefined(value)) { - return {}; - } else if (_isBool(value)) { - return { *toBool(value) }; - } else if ( _isString(value)) { - return { *toString(value) }; - } else if (_isNumber(value)) { - // Need to cast to a double here as the float is otherwise considered a bool... - return { static_cast<double>(*toNumber(value)) }; - } else { - return {}; + static bool _isNumber(const id value) { + return [value isKindOfClass:[NSNumber class]] && !_isBool(value); } + + static bool _isString(const id value) { + return [value isKindOfClass:[NSString class]]; + } +}; + +inline Convertible makeConvertible(const id value) { + return Convertible(Holder(value)); } } // namespace conversion @@ -140,4 +154,3 @@ inline optional<mbgl::Value> toValue(const id value) { } // namespace mbgl NS_ASSUME_NONNULL_END - diff --git a/platform/darwin/src/MGLStyleValue_Private.h b/platform/darwin/src/MGLStyleValue_Private.h index 2155c657bd..5914e0a2aa 100644 --- a/platform/darwin/src/MGLStyleValue_Private.h +++ b/platform/darwin/src/MGLStyleValue_Private.h @@ -124,9 +124,9 @@ public: if ([value isKindOfClass:[MGLConstantStyleValue class]]) { return toMBGLConstantValue((MGLConstantStyleValue<ObjCType> *)value); } else if ([value isKindOfClass:[MGLStyleFunction class]]) { - auto rawValue = toRawStyleSpecValue((MGLStyleFunction<ObjCType> *) value); mbgl::style::conversion::Error error; - auto result = mbgl::style::conversion::convert<mbgl::style::DataDrivenPropertyValue<MBGLType>>(rawValue, error); + auto result = mbgl::style::conversion::convert<mbgl::style::DataDrivenPropertyValue<MBGLType>>( + mbgl::style::conversion::makeConvertible(toRawStyleSpecValue((MGLStyleFunction<ObjCType> *) value)), error); NSCAssert(result, @(error.message.c_str())); return *result; } else { diff --git a/platform/node/src/node_conversion.hpp b/platform/node/src/node_conversion.hpp index d266745548..7c5bbf4386 100644 --- a/platform/node/src/node_conversion.hpp +++ b/platform/node/src/node_conversion.hpp @@ -9,111 +9,136 @@ #include <mbgl/util/optional.hpp> #include <mbgl/util/feature.hpp> #include <mbgl/style/conversion.hpp> +#include <mbgl/style/conversion/geojson.hpp> namespace mbgl { namespace style { namespace conversion { -inline bool isUndefined(v8::Local<v8::Value> value) { - Nan::HandleScope scope; - return value->IsUndefined() || value->IsNull(); -} +template <> +class ConversionTraits<v8::Local<v8::Value>> { +public: + static bool isUndefined(const v8::Local<v8::Value>& value) { + Nan::HandleScope scope; + return value->IsUndefined() || value->IsNull(); + } -inline bool isArray(v8::Local<v8::Value> value) { - Nan::HandleScope scope; - return value->IsArray(); -} + static bool isArray(const v8::Local<v8::Value>& value) { + Nan::HandleScope scope; + return value->IsArray(); + } -inline std::size_t arrayLength(v8::Local<v8::Value> value) { - Nan::HandleScope scope; - return value.As<v8::Array>()->Length(); -} + static std::size_t arrayLength(const v8::Local<v8::Value>& value) { + Nan::HandleScope scope; + // const_cast because v8::Local<T>::As is not marked const until node v8.0 + v8::Local<v8::Array> array = const_cast<v8::Local<v8::Value>&>(value).As<v8::Array>(); + return array->Length(); + } -inline v8::Local<v8::Value> arrayMember(v8::Local<v8::Value> value, std::size_t i) { - Nan::EscapableHandleScope scope; - return scope.Escape(Nan::Get(value.As<v8::Array>(), i).ToLocalChecked()); -} + static v8::Local<v8::Value> arrayMember(const v8::Local<v8::Value>& value, std::size_t i) { + Nan::EscapableHandleScope scope; + // const_cast because v8::Local<T>::As is not marked const until node v8.0 + v8::Local<v8::Array> array = const_cast<v8::Local<v8::Value>&>(value).As<v8::Array>(); + return scope.Escape(Nan::Get(array, i).ToLocalChecked()); + } -inline bool isObject(v8::Local<v8::Value> value) { - Nan::HandleScope scope; - return value->IsObject() && !value->IsArray(); -} + static bool isObject(const v8::Local<v8::Value>& value) { + Nan::HandleScope scope; + return value->IsObject() && !value->IsArray(); + } -inline optional<v8::Local<v8::Value>> objectMember(v8::Local<v8::Value> value, const char * name) { - Nan::EscapableHandleScope scope; - if (!Nan::Has(Nan::To<v8::Object>(value).ToLocalChecked(), Nan::New(name).ToLocalChecked()).FromJust()) { - return {}; + static optional<v8::Local<v8::Value>> objectMember(const v8::Local<v8::Value>& value, const char * name) { + Nan::EscapableHandleScope scope; + if (!Nan::Has(Nan::To<v8::Object>(value).ToLocalChecked(), Nan::New(name).ToLocalChecked()).FromJust()) { + return {}; + } + Nan::MaybeLocal<v8::Value> result = Nan::Get(Nan::To<v8::Object>(value).ToLocalChecked(), Nan::New(name).ToLocalChecked()); + if (result.IsEmpty()) { + return {}; + } + return {scope.Escape(result.ToLocalChecked())}; } - Nan::MaybeLocal<v8::Value> result = Nan::Get(Nan::To<v8::Object>(value).ToLocalChecked(), Nan::New(name).ToLocalChecked()); - if (result.IsEmpty()) { + + template <class Fn> + static optional<Error> eachMember(const v8::Local<v8::Value>& value, Fn&& fn) { + Nan::HandleScope scope; + v8::Local<v8::Array> names = Nan::GetOwnPropertyNames(Nan::To<v8::Object>(value).ToLocalChecked()).ToLocalChecked(); + for (uint32_t i = 0; i < names->Length(); ++i) { + v8::Local<v8::Value> k = Nan::Get(names, i).ToLocalChecked(); + v8::Local<v8::Value> v = Nan::Get(Nan::To<v8::Object>(value).ToLocalChecked(), k).ToLocalChecked(); + optional<Error> result = fn(*Nan::Utf8String(k), std::move(v)); + if (result) { + return result; + } + } return {}; } - return scope.Escape(result.ToLocalChecked()); -} -template <class Fn> -optional<Error> eachMember(v8::Local<v8::Value> value, Fn&& fn) { - Nan::HandleScope scope; - v8::Local<v8::Array> names = Nan::GetOwnPropertyNames(Nan::To<v8::Object>(value).ToLocalChecked()).ToLocalChecked(); - for (uint32_t i = 0; i < names->Length(); ++i) { - v8::Local<v8::Value> k = Nan::Get(names, i).ToLocalChecked(); - v8::Local<v8::Value> v = Nan::Get(Nan::To<v8::Object>(value).ToLocalChecked(), k).ToLocalChecked(); - optional<Error> result = fn(*Nan::Utf8String(k), v); - if (result) { - return result; + static optional<bool> toBool(const v8::Local<v8::Value>& value) { + Nan::HandleScope scope; + if (!value->IsBoolean()) { + return {}; } + return value->BooleanValue(); } - return {}; -} -inline optional<bool> toBool(v8::Local<v8::Value> value) { - Nan::HandleScope scope; - if (!value->IsBoolean()) { - return {}; + static optional<float> toNumber(const v8::Local<v8::Value>& value) { + Nan::HandleScope scope; + if (!value->IsNumber()) { + return {}; + } + return value->NumberValue(); } - return value->BooleanValue(); -} -inline optional<float> toNumber(v8::Local<v8::Value> value) { - Nan::HandleScope scope; - if (!value->IsNumber()) { - return {}; + static optional<double> toDouble(const v8::Local<v8::Value>& value) { + Nan::HandleScope scope; + if (!value->IsNumber()) { + return {}; + } + return value->NumberValue(); } - return value->NumberValue(); -} -inline optional<double> toDouble(v8::Local<v8::Value> value) { - Nan::HandleScope scope; - if (!value->IsNumber()) { - return {}; + static optional<std::string> toString(const v8::Local<v8::Value>& value) { + Nan::HandleScope scope; + if (!value->IsString()) { + return {}; + } + return std::string(*Nan::Utf8String(value)); } - return value->NumberValue(); -} -inline optional<std::string> toString(v8::Local<v8::Value> value) { - Nan::HandleScope scope; - if (!value->IsString()) { - return {}; + static optional<Value> toValue(const v8::Local<v8::Value>& value) { + if (value->IsFalse()) { + return { false }; + } else if (value->IsTrue()) { + return { true }; + } else if (value->IsString()) { + return { std::string(*Nan::Utf8String(value)) }; + } else if (value->IsUint32()) { + return { std::uint64_t(value->Uint32Value()) }; + } else if (value->IsInt32()) { + return { std::int64_t(value->Int32Value()) }; + } else if (value->IsNumber()) { + return { value->NumberValue() }; + } else { + return {}; + } } - return std::string(*Nan::Utf8String(value)); -} -inline optional<Value> toValue(v8::Local<v8::Value> value) { - if (value->IsFalse()) { - return { false }; - } else if (value->IsTrue()) { - return { true }; - } else if (value->IsString()) { - return { std::string(*Nan::Utf8String(value)) }; - } else if (value->IsUint32()) { - return { std::uint64_t(value->Uint32Value()) }; - } else if (value->IsInt32()) { - return { std::int64_t(value->Int32Value()) }; - } else if (value->IsNumber()) { - return { value->NumberValue() }; - } else { - return {}; + static optional<GeoJSON> toGeoJSON(const v8::Local<v8::Value>& value, Error& error) { + try { + Nan::JSON JSON; + std::string string = *Nan::Utf8String(JSON.Stringify(value->ToObject()).ToLocalChecked()); + return parseGeoJSON(string, error); + } catch (const std::exception& ex) { + error = { ex.what() }; + return {}; + } } +}; + +template <class T, class...Args> +optional<T> convert(const v8::Local<v8::Value>& value, Error& error, Args&&...args) { + return convert<T>(Convertible(value), error, std::forward<Args>(args)...); } } // namespace conversion diff --git a/platform/node/src/node_geojson.hpp b/platform/node/src/node_geojson.hpp deleted file mode 100644 index 8a3927e2cf..0000000000 --- a/platform/node/src/node_geojson.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#include <mbgl/style/conversion/geojson.hpp> - -#include <string> -namespace mbgl { -namespace style { -namespace conversion { - -template <> -optional<GeoJSON> Converter<GeoJSON>::operator()(const v8::Local<v8::Value>& value, Error& error) const { - Nan::JSON JSON; - std::string string = *Nan::Utf8String(JSON.Stringify(value->ToObject()).ToLocalChecked()); - return convert<GeoJSON>(string, error); -} - -} // namespace conversion -} // namespace style -} // namespace mbgl diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp index f344461f31..6deccf05bf 100644 --- a/platform/node/src/node_map.cpp +++ b/platform/node/src/node_map.cpp @@ -2,7 +2,6 @@ #include "node_request.hpp" #include "node_feature.hpp" #include "node_conversion.hpp" -#include "node_geojson.hpp" #include <mbgl/util/exception.hpp> #include <mbgl/renderer/renderer.hpp> @@ -10,6 +9,15 @@ #include <mbgl/style/conversion/source.hpp> #include <mbgl/style/conversion/layer.hpp> #include <mbgl/style/conversion/filter.hpp> + +#include <mbgl/style/layers/background_layer.hpp> +#include <mbgl/style/layers/circle_layer.hpp> +#include <mbgl/style/layers/fill_layer.hpp> +#include <mbgl/style/layers/fill_extrusion_layer.hpp> +#include <mbgl/style/layers/line_layer.hpp> +#include <mbgl/style/layers/raster_layer.hpp> +#include <mbgl/style/layers/symbol_layer.hpp> + #include <mbgl/style/style.hpp> #include <mbgl/style/image.hpp> #include <mbgl/map/map_observer.hpp> @@ -741,7 +749,7 @@ void NodeMap::SetLayoutProperty(const Nan::FunctionCallbackInfo<v8::Value>& info return Nan::ThrowTypeError("Second argument must be a string"); } - mbgl::optional<Error> error = setLayoutProperty(*layer, *Nan::Utf8String(info[1]), info[2]); + mbgl::optional<Error> error = setLayoutProperty(*layer, *Nan::Utf8String(info[1]), Convertible(info[2])); if (error) { return Nan::ThrowTypeError(error->message.c_str()); } @@ -773,7 +781,7 @@ void NodeMap::SetPaintProperty(const Nan::FunctionCallbackInfo<v8::Value>& info) return Nan::ThrowTypeError("Second argument must be a string"); } - mbgl::optional<Error> error = setPaintProperty(*layer, *Nan::Utf8String(info[1]), info[2]); + mbgl::optional<Error> error = setPaintProperty(*layer, *Nan::Utf8String(info[1]), Convertible(info[2])); if (error) { return Nan::ThrowTypeError(error->message.c_str()); } diff --git a/platform/qt/qt.cmake b/platform/qt/qt.cmake index 2346d7d820..a7862c3936 100644 --- a/platform/qt/qt.cmake +++ b/platform/qt/qt.cmake @@ -60,6 +60,9 @@ set(MBGL_QT_FILESOURCE_FILES add_library(qmapboxgl SHARED platform/qt/include/qmapbox.hpp platform/qt/include/qmapboxgl.hpp + platform/qt/src/qt_conversion.hpp + platform/qt/src/qt_geojson.cpp + platform/qt/src/qt_geojson.hpp platform/qt/src/qmapbox.cpp platform/qt/src/qmapboxgl.cpp platform/qt/src/qmapboxgl_p.hpp diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp index 074ef280aa..740bb90202 100644 --- a/platform/qt/src/qmapboxgl.cpp +++ b/platform/qt/src/qmapboxgl.cpp @@ -14,7 +14,17 @@ #include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/layer.hpp> #include <mbgl/style/conversion/source.hpp> +#include <mbgl/style/conversion/filter.hpp> +#include <mbgl/style/conversion/geojson.hpp> +#include <mbgl/style/filter.hpp> #include <mbgl/style/layers/custom_layer.hpp> +#include <mbgl/style/layers/background_layer.hpp> +#include <mbgl/style/layers/circle_layer.hpp> +#include <mbgl/style/layers/fill_layer.hpp> +#include <mbgl/style/layers/fill_extrusion_layer.hpp> +#include <mbgl/style/layers/line_layer.hpp> +#include <mbgl/style/layers/raster_layer.hpp> +#include <mbgl/style/layers/symbol_layer.hpp> #include <mbgl/style/sources/geojson_source.hpp> #include <mbgl/style/transition_options.hpp> #include <mbgl/style/image.hpp> diff --git a/platform/qt/src/qt_conversion.hpp b/platform/qt/src/qt_conversion.hpp index 40d7e5b928..19b0cb54fc 100644 --- a/platform/qt/src/qt_conversion.hpp +++ b/platform/qt/src/qt_conversion.hpp @@ -1,120 +1,145 @@ #pragma once #include <mbgl/style/conversion.hpp> -#include <mbgl/util/feature.hpp> +#include <mbgl/style/conversion/geojson.hpp> #include <mbgl/util/optional.hpp> -#include <QMapbox> - -#include <QColor> #include <QVariant> +#include <QColor> +#include <QMapbox> +#include "qt_geojson.hpp" namespace mbgl { namespace style { namespace conversion { -inline bool isUndefined(const QVariant& value) { - return value.isNull() || !value.isValid(); -} +template <> +class ConversionTraits<QVariant> { +public: + static bool isUndefined(const QVariant& value) { + return value.isNull() || !value.isValid(); + } -inline bool isArray(const QVariant& value) { - return value.canConvert(QVariant::List); -} + static bool isArray(const QVariant& value) { + return value.canConvert(QVariant::List); + } -inline std::size_t arrayLength(const QVariant& value) { - return value.toList().size(); -} + static std::size_t arrayLength(const QVariant& value) { + return value.toList().size(); + } -inline QVariant arrayMember(const QVariant& value, std::size_t i) { - return value.toList()[i]; -} + static QVariant arrayMember(const QVariant& value, std::size_t i) { + return value.toList()[i]; + } -inline bool isObject(const QVariant& value) { - return value.canConvert(QVariant::Map) - || value.type() == QVariant::ByteArray -#if QT_VERSION >= 0x050000 - || QString(value.typeName()) == QStringLiteral("QMapbox::Feature"); -#else - || QString(value.typeName()) == QString("QMapbox::Feature"); -#endif -} + static bool isObject(const QVariant& value) { + return value.canConvert(QVariant::Map) + || value.type() == QVariant::ByteArray + #if QT_VERSION >= 0x050000 + || QString(value.typeName()) == QStringLiteral("QMapbox::Feature"); + #else + || QString(value.typeName()) == QString("QMapbox::Feature"); + #endif + } -inline optional<QVariant> objectMember(const QVariant& value, const char* key) { - auto map = value.toMap(); - auto iter = map.constFind(key); + static optional<QVariant> objectMember(const QVariant& value, const char* key) { + auto map = value.toMap(); + auto iter = map.constFind(key); - if (iter != map.constEnd()) { - return iter.value(); - } else { - return {}; + if (iter != map.constEnd()) { + return iter.value(); + } else { + return {}; + } } -} -using EachMemberFn = std::function<optional<Error>(const std::string&, const QVariant&)>; + template <class Fn> + static optional<Error> eachMember(const QVariant& value, Fn&& fn) { + auto map = value.toMap(); + auto iter = map.constBegin(); -optional<Error> eachMember(const QVariant& value, EachMemberFn&& fn) { - auto map = value.toMap(); - auto iter = map.constBegin(); + while (iter != map.constEnd()) { + optional<Error> result = fn(iter.key().toStdString(), QVariant(iter.value())); + if (result) { + return result; + } - while (iter != map.constEnd()) { - optional<Error> result = fn(iter.key().toStdString(), iter.value()); - if (result) { - return result; + ++iter; } - ++iter; + return {}; } - return {}; -} + static optional<bool> toBool(const QVariant& value) { + if (value.type() == QVariant::Bool) { + return value.toBool(); + } else { + return {}; + } + } -inline optional<bool> toBool(const QVariant& value) { - if (value.type() == QVariant::Bool) { - return value.toBool(); - } else { - return {}; + static optional<float> toNumber(const QVariant& value) { + if (value.type() == QVariant::Int || value.type() == QVariant::Double) { + return value.toFloat(); + } else { + return {}; + } } -} -inline optional<float> toNumber(const QVariant& value) { - if (value.type() == QVariant::Int || value.type() == QVariant::Double) { - return value.toFloat(); - } else { - return {}; + static optional<double> toDouble(const QVariant& value) { + if (value.type() == QVariant::Int || value.type() == QVariant::Double) { + return value.toDouble(); + } else { + return {}; + } } -} -inline optional<double> toDouble(const QVariant& value) { - if (value.type() == QVariant::Int || value.type() == QVariant::Double) { - return value.toDouble(); - } else { - return {}; + + static optional<std::string> toString(const QVariant& value) { + if (value.type() == QVariant::String) { + return value.toString().toStdString(); + } else if (value.type() == QVariant::Color) { + return value.value<QColor>().name().toStdString(); + } else { + return {}; + } } -} -inline optional<std::string> toString(const QVariant& value) { - if (value.type() == QVariant::String) { - return value.toString().toStdString(); - } else if (value.type() == QVariant::Color) { - return value.value<QColor>().name().toStdString(); - } else { - return {}; + static optional<Value> toValue(const QVariant& value) { + if (value.type() == QVariant::Bool) { + return { value.toBool() }; + } else if (value.type() == QVariant::String) { + return { value.toString().toStdString() }; + } else if (value.type() == QVariant::Color) { + return { value.value<QColor>().name().toStdString() }; + } else if (value.type() == QVariant::Int) { + return { int64_t(value.toInt()) }; + } else if (value.canConvert(QVariant::Double)) { + return { value.toDouble() }; + } else { + return {}; + } } -} -inline optional<Value> toValue(const QVariant& value) { - if (value.type() == QVariant::Bool) { - return { value.toBool() }; - } else if (value.type() == QVariant::String) { - return { value.toString().toStdString() }; - } else if (value.type() == QVariant::Color) { - return { value.value<QColor>().name().toStdString() }; - } else if (value.type() == QVariant::Int) { - return { int64_t(value.toInt()) }; - } else if (value.canConvert(QVariant::Double)) { - return { value.toDouble() }; - } else { - return {}; + static optional<GeoJSON> toGeoJSON(const QVariant& value, Error& error) { + #if QT_VERSION >= 0x050000 + if (value.typeName() == QStringLiteral("QMapbox::Feature")) { + #else + if (value.typeName() == QString("QMapbox::Feature")) { + #endif + return GeoJSON { asMapboxGLFeature(value.value<QMapbox::Feature>()) }; + } else if (value.type() != QVariant::ByteArray) { + error = { "JSON data must be in QByteArray" }; + return {}; + } + + QByteArray data = value.toByteArray(); + return parseGeoJSON(std::string(data.constData(), data.size()), error); } +}; + +template <class T, class...Args> +optional<T> convert(const QVariant& value, Error& error, Args&&...args) { + return convert<T>(Convertible(value), error, std::forward<Args>(args)...); } } // namespace conversion diff --git a/platform/qt/src/qt_geojson.cpp b/platform/qt/src/qt_geojson.cpp new file mode 100644 index 0000000000..80377de64d --- /dev/null +++ b/platform/qt/src/qt_geojson.cpp @@ -0,0 +1,166 @@ +#include "qt_geojson.hpp" +#include <mapbox/geojson.hpp> +#include <mbgl/util/geometry.hpp> +#include <mbgl/util/feature.hpp> + +namespace QMapbox { + +mbgl::Point<double> asMapboxGLPoint(const QMapbox::Coordinate &coordinate) { + return mbgl::Point<double> { coordinate.second, coordinate.first }; +} + +mbgl::MultiPoint<double> asMapboxGLMultiPoint(const QMapbox::Coordinates &multiPoint) { + mbgl::MultiPoint<double> mbglMultiPoint; + mbglMultiPoint.reserve(multiPoint.size()); + for (const auto &point: multiPoint) { + mbglMultiPoint.emplace_back(asMapboxGLPoint(point)); + } + return mbglMultiPoint; +}; + +mbgl::LineString<double> asMapboxGLLineString(const QMapbox::Coordinates &lineString) { + mbgl::LineString<double> mbglLineString; + mbglLineString.reserve(lineString.size()); + for (const auto &coordinate : lineString) { + mbglLineString.emplace_back(asMapboxGLPoint(coordinate)); + } + return mbglLineString; +}; + +mbgl::MultiLineString<double> asMapboxGLMultiLineString(const QMapbox::CoordinatesCollection &multiLineString) { + mbgl::MultiLineString<double> mbglMultiLineString; + mbglMultiLineString.reserve(multiLineString.size()); + for (const auto &lineString : multiLineString) { + mbglMultiLineString.emplace_back(std::forward<mbgl::LineString<double>>(asMapboxGLLineString(lineString))); + } + return mbglMultiLineString; +}; + +mbgl::Polygon<double> asMapboxGLPolygon(const QMapbox::CoordinatesCollection &polygon) { + mbgl::Polygon<double> mbglPolygon; + mbglPolygon.reserve(polygon.size()); + for (const auto &linearRing : polygon) { + mbgl::LinearRing<double> mbglLinearRing; + mbglLinearRing.reserve(linearRing.size()); + for (const auto &coordinate: linearRing) { + mbglLinearRing.emplace_back(asMapboxGLPoint(coordinate)); + } + mbglPolygon.emplace_back(std::move(mbglLinearRing)); + } + return mbglPolygon; +}; + +mbgl::MultiPolygon<double> asMapboxGLMultiPolygon(const QMapbox::CoordinatesCollections &multiPolygon) { + mbgl::MultiPolygon<double> mbglMultiPolygon; + mbglMultiPolygon.reserve(multiPolygon.size()); + for (const auto &polygon : multiPolygon) { + mbglMultiPolygon.emplace_back(std::forward<mbgl::Polygon<double>>(asMapboxGLPolygon(polygon))); + } + return mbglMultiPolygon; +}; + +mbgl::Value asMapboxGLPropertyValue(const QVariant &value) { + auto valueList = [](const QVariantList &list) { + std::vector<mbgl::Value> mbglList; + mbglList.reserve(list.size()); + for (const auto& listValue : list) { + mbglList.emplace_back(asMapboxGLPropertyValue(listValue)); + } + return mbglList; + }; + + auto valueMap = [](const QVariantMap &map) { + std::unordered_map<std::string, mbgl::Value> mbglMap; + mbglMap.reserve(map.size()); + auto it = map.constBegin(); + while (it != map.constEnd()) { + mbglMap.emplace(std::make_pair(it.key().toStdString(), asMapboxGLPropertyValue(it.value()))); + ++it; + } + return mbglMap; + }; + + switch (value.type()) { +#if QT_VERSION >= 0x050000 + case QMetaType::UnknownType: +#else + case QVariant::Invalid: +#endif + return mbgl::NullValue {}; + case QMetaType::Bool: + return { value.toBool() }; + case QMetaType::ULongLong: + return { uint64_t(value.toULongLong()) }; + case QMetaType::LongLong: + return { int64_t(value.toLongLong()) }; + case QMetaType::Double: + return { value.toDouble() }; + case QMetaType::QString: + return { value.toString().toStdString() }; + case QMetaType::QVariantList: + return valueList(value.toList()); + case QMetaType::QVariantMap: + return valueMap(value.toMap()); + default: + qWarning() << "Unsupported feature property value:" << value; + return {}; + } +} + +mbgl::FeatureIdentifier asMapboxGLFeatureIdentifier(const QVariant &id) { + switch (id.type()) { +#if QT_VERSION >= 0x050000 + case QMetaType::UnknownType: +#else + case QVariant::Invalid: +#endif + return {}; + case QMetaType::ULongLong: + return { uint64_t(id.toULongLong()) }; + case QMetaType::LongLong: + return { int64_t(id.toLongLong()) }; + case QMetaType::Double: + return { id.toDouble() }; + case QMetaType::QString: + return { id.toString().toStdString() }; + default: + qWarning() << "Unsupported feature identifier:" << id; + return {}; + } +} + +mbgl::Feature asMapboxGLFeature(const QMapbox::Feature &feature) { + mbgl::PropertyMap properties; + properties.reserve(feature.properties.size()); + auto it = feature.properties.constBegin(); + while (it != feature.properties.constEnd()) { + properties.emplace(std::make_pair(it.key().toStdString(), asMapboxGLPropertyValue(it.value()))); + } + + mbgl::FeatureIdentifier id = asMapboxGLFeatureIdentifier(feature.id); + + if (feature.type == QMapbox::Feature::PointType) { + const QMapbox::Coordinates &points = feature.geometry.first().first(); + if (points.size() == 1) { + return { asMapboxGLPoint(points.first()), std::move(properties), std::move(id) }; + } else { + return { asMapboxGLMultiPoint(points), std::move(properties), std::move(id) }; + } + } else if (feature.type == QMapbox::Feature::LineStringType) { + const QMapbox::CoordinatesCollection &lineStrings = feature.geometry.first(); + if (lineStrings.size() == 1) { + return { asMapboxGLLineString(lineStrings.first()), std::move(properties), std::move(id) }; + } else { + return { asMapboxGLMultiLineString(lineStrings), std::move(properties), std::move(id) }; + } + } else { // PolygonType + const QMapbox::CoordinatesCollections &polygons = feature.geometry; + if (polygons.size() == 1) { + return { asMapboxGLPolygon(polygons.first()), std::move(properties), std::move(id) }; + } else { + return { asMapboxGLMultiPolygon(polygons), std::move(properties), std::move(id) }; + } + } +}; + +} // namespace QMapbox diff --git a/platform/qt/src/qt_geojson.hpp b/platform/qt/src/qt_geojson.hpp index a6958b7edc..a9c10272ab 100644 --- a/platform/qt/src/qt_geojson.hpp +++ b/platform/qt/src/qt_geojson.hpp @@ -1,7 +1,8 @@ #pragma once #include <mapbox/geojson.hpp> -#include <mbgl/style/conversion/geojson.hpp> +#include <mbgl/util/geometry.hpp> +#include <mbgl/util/feature.hpp> #include <QMapbox> @@ -13,187 +14,14 @@ namespace QMapbox { -mbgl::Point<double> asMapboxGLPoint(const QMapbox::Coordinate &coordinate) { - return mbgl::Point<double> { coordinate.second, coordinate.first }; -} - -mbgl::MultiPoint<double> asMapboxGLMultiPoint(const QMapbox::Coordinates &multiPoint) { - mbgl::MultiPoint<double> mbglMultiPoint; - mbglMultiPoint.reserve(multiPoint.size()); - for (const auto &point: multiPoint) { - mbglMultiPoint.emplace_back(asMapboxGLPoint(point)); - } - return mbglMultiPoint; -}; - -mbgl::LineString<double> asMapboxGLLineString(const QMapbox::Coordinates &lineString) { - mbgl::LineString<double> mbglLineString; - mbglLineString.reserve(lineString.size()); - for (const auto &coordinate : lineString) { - mbglLineString.emplace_back(asMapboxGLPoint(coordinate)); - } - return mbglLineString; -}; - -mbgl::MultiLineString<double> asMapboxGLMultiLineString(const QMapbox::CoordinatesCollection &multiLineString) { - mbgl::MultiLineString<double> mbglMultiLineString; - mbglMultiLineString.reserve(multiLineString.size()); - for (const auto &lineString : multiLineString) { - mbglMultiLineString.emplace_back(std::forward<mbgl::LineString<double>>(asMapboxGLLineString(lineString))); - } - return mbglMultiLineString; -}; - -mbgl::Polygon<double> asMapboxGLPolygon(const QMapbox::CoordinatesCollection &polygon) { - mbgl::Polygon<double> mbglPolygon; - mbglPolygon.reserve(polygon.size()); - for (const auto &linearRing : polygon) { - mbgl::LinearRing<double> mbglLinearRing; - mbglLinearRing.reserve(linearRing.size()); - for (const auto &coordinate: linearRing) { - mbglLinearRing.emplace_back(asMapboxGLPoint(coordinate)); - } - mbglPolygon.emplace_back(std::move(mbglLinearRing)); - } - return mbglPolygon; -}; - -mbgl::MultiPolygon<double> asMapboxGLMultiPolygon(const QMapbox::CoordinatesCollections &multiPolygon) { - mbgl::MultiPolygon<double> mbglMultiPolygon; - mbglMultiPolygon.reserve(multiPolygon.size()); - for (const auto &polygon : multiPolygon) { - mbglMultiPolygon.emplace_back(std::forward<mbgl::Polygon<double>>(asMapboxGLPolygon(polygon))); - } - return mbglMultiPolygon; -}; - -mbgl::Value asMapboxGLPropertyValue(const QVariant &value) { - auto valueList = [](const QVariantList &list) { - std::vector<mbgl::Value> mbglList; - mbglList.reserve(list.size()); - for (const auto& listValue : list) { - mbglList.emplace_back(asMapboxGLPropertyValue(listValue)); - } - return mbglList; - }; - - auto valueMap = [](const QVariantMap &map) { - std::unordered_map<std::string, mbgl::Value> mbglMap; - mbglMap.reserve(map.size()); - auto it = map.constBegin(); - while (it != map.constEnd()) { - mbglMap.emplace(std::make_pair(it.key().toStdString(), asMapboxGLPropertyValue(it.value()))); - ++it; - } - return mbglMap; - }; - - switch (value.type()) { -#if QT_VERSION >= 0x050000 - case QMetaType::UnknownType: -#else - case QVariant::Invalid: -#endif - return mbgl::NullValue {}; - case QMetaType::Bool: - return { value.toBool() }; - case QMetaType::ULongLong: - return { uint64_t(value.toULongLong()) }; - case QMetaType::LongLong: - return { int64_t(value.toLongLong()) }; - case QMetaType::Double: - return { value.toDouble() }; - case QMetaType::QString: - return { value.toString().toStdString() }; - case QMetaType::QVariantList: - return valueList(value.toList()); - case QMetaType::QVariantMap: - return valueMap(value.toMap()); - default: - qWarning() << "Unsupported feature property value:" << value; - return {}; - } -} - -mbgl::FeatureIdentifier asMapboxGLFeatureIdentifier(const QVariant &id) { - switch (id.type()) { -#if QT_VERSION >= 0x050000 - case QMetaType::UnknownType: -#else - case QVariant::Invalid: -#endif - return {}; - case QMetaType::ULongLong: - return { uint64_t(id.toULongLong()) }; - case QMetaType::LongLong: - return { int64_t(id.toLongLong()) }; - case QMetaType::Double: - return { id.toDouble() }; - case QMetaType::QString: - return { id.toString().toStdString() }; - default: - qWarning() << "Unsupported feature identifier:" << id; - return {}; - } -} - -mbgl::Feature asMapboxGLFeature(const QMapbox::Feature &feature) { - mbgl::PropertyMap properties; - properties.reserve(feature.properties.size()); - auto it = feature.properties.constBegin(); - while (it != feature.properties.constEnd()) { - properties.emplace(std::make_pair(it.key().toStdString(), asMapboxGLPropertyValue(it.value()))); - } - - mbgl::FeatureIdentifier id = asMapboxGLFeatureIdentifier(feature.id); - - if (feature.type == QMapbox::Feature::PointType) { - const QMapbox::Coordinates &points = feature.geometry.first().first(); - if (points.size() == 1) { - return { asMapboxGLPoint(points.first()), std::move(properties), std::move(id) }; - } else { - return { asMapboxGLMultiPoint(points), std::move(properties), std::move(id) }; - } - } else if (feature.type == QMapbox::Feature::LineStringType) { - const QMapbox::CoordinatesCollection &lineStrings = feature.geometry.first(); - if (lineStrings.size() == 1) { - return { asMapboxGLLineString(lineStrings.first()), std::move(properties), std::move(id) }; - } else { - return { asMapboxGLMultiLineString(lineStrings), std::move(properties), std::move(id) }; - } - } else { // PolygonType - const QMapbox::CoordinatesCollections &polygons = feature.geometry; - if (polygons.size() == 1) { - return { asMapboxGLPolygon(polygons.first()), std::move(properties), std::move(id) }; - } else { - return { asMapboxGLMultiPolygon(polygons), std::move(properties), std::move(id) }; - } - } -}; +mbgl::Point<double> asMapboxGLPoint(const QMapbox::Coordinate &coordinate); +mbgl::MultiPoint<double> asMapboxGLMultiPoint(const QMapbox::Coordinates &multiPoint); +mbgl::LineString<double> asMapboxGLLineString(const QMapbox::Coordinates &lineString); +mbgl::MultiLineString<double> asMapboxGLMultiLineString(const QMapbox::CoordinatesCollection &multiLineString); +mbgl::Polygon<double> asMapboxGLPolygon(const QMapbox::CoordinatesCollection &polygon); +mbgl::MultiPolygon<double> asMapboxGLMultiPolygon(const QMapbox::CoordinatesCollections &multiPolygon); +mbgl::Value asMapboxGLPropertyValue(const QVariant &value); +mbgl::FeatureIdentifier asMapboxGLFeatureIdentifier(const QVariant &id); +mbgl::Feature asMapboxGLFeature(const QMapbox::Feature &feature); } // namespace QMapbox - -namespace mbgl { -namespace style { -namespace conversion { - -template <> -optional<GeoJSON> Converter<GeoJSON>::operator()(const QVariant& value, Error& error) const { -#if QT_VERSION >= 0x050000 - if (value.typeName() == QStringLiteral("QMapbox::Feature")) { -#else - if (value.typeName() == QString("QMapbox::Feature")) { -#endif - return GeoJSON { asMapboxGLFeature(value.value<QMapbox::Feature>()) }; - } else if (value.type() != QVariant::ByteArray) { - error = { "JSON data must be in QByteArray" }; - return {}; - } - - QByteArray data = value.toByteArray(); - return convert<GeoJSON>(std::string(data.constData(), data.size()), error); -} - -} // namespace conversion -} // namespace style -} // namespace mbgl |