Replace compile-time polymorphism with runtime polymorphism in the conversion system
This commit is contained in:
@@ -19,6 +19,7 @@ target_link_libraries(mbgl-benchmark
|
||||
|
||||
target_add_mason_package(mbgl-benchmark PRIVATE boost)
|
||||
target_add_mason_package(mbgl-benchmark PRIVATE benchmark)
|
||||
target_add_mason_package(mbgl-benchmark PRIVATE geojson)
|
||||
target_add_mason_package(mbgl-benchmark PRIVATE rapidjson)
|
||||
target_add_mason_package(mbgl-benchmark PRIVATE protozero)
|
||||
target_add_mason_package(mbgl-benchmark PRIVATE vector-tile)
|
||||
|
||||
@@ -370,16 +370,26 @@ set(MBGL_CORE_FILES
|
||||
include/mbgl/style/conversion/geojson_options.hpp
|
||||
include/mbgl/style/conversion/layer.hpp
|
||||
include/mbgl/style/conversion/light.hpp
|
||||
include/mbgl/style/conversion/make_property_setters.hpp
|
||||
include/mbgl/style/conversion/position.hpp
|
||||
include/mbgl/style/conversion/property_setter.hpp
|
||||
include/mbgl/style/conversion/property_value.hpp
|
||||
include/mbgl/style/conversion/source.hpp
|
||||
include/mbgl/style/conversion/tileset.hpp
|
||||
include/mbgl/style/conversion/transition_options.hpp
|
||||
src/mbgl/style/conversion/constant.cpp
|
||||
src/mbgl/style/conversion/coordinate.cpp
|
||||
src/mbgl/style/conversion/filter.cpp
|
||||
src/mbgl/style/conversion/geojson.cpp
|
||||
src/mbgl/style/conversion/geojson_options.cpp
|
||||
src/mbgl/style/conversion/json.hpp
|
||||
src/mbgl/style/conversion/layer.cpp
|
||||
src/mbgl/style/conversion/light.cpp
|
||||
src/mbgl/style/conversion/make_property_setters.hpp
|
||||
src/mbgl/style/conversion/position.cpp
|
||||
src/mbgl/style/conversion/property_setter.hpp
|
||||
src/mbgl/style/conversion/source.cpp
|
||||
src/mbgl/style/conversion/stringify.hpp
|
||||
src/mbgl/style/conversion/tileset.cpp
|
||||
src/mbgl/style/conversion/transition_options.cpp
|
||||
|
||||
# style/function
|
||||
include/mbgl/style/function/camera_function.hpp
|
||||
|
||||
@@ -13,6 +13,7 @@ set_target_properties("mbgl-node" PROPERTIES CXX_STANDARD 14)
|
||||
target_sources(mbgl-node
|
||||
PRIVATE platform/node/src/node_logging.hpp
|
||||
PRIVATE platform/node/src/node_logging.cpp
|
||||
PRIVATE platform/node/src/node_conversion.hpp
|
||||
PRIVATE platform/node/src/node_map.hpp
|
||||
PRIVATE platform/node/src/node_map.cpp
|
||||
PRIVATE platform/node/src/node_request.hpp
|
||||
|
||||
@@ -51,7 +51,6 @@ set(MBGL_TEST_FILES
|
||||
test/sprite/sprite_parser.test.cpp
|
||||
|
||||
# src/mbgl/test
|
||||
test/src/mbgl/test/conversion_stubs.hpp
|
||||
test/src/mbgl/test/fake_file_source.hpp
|
||||
test/src/mbgl/test/fixture_log_observer.cpp
|
||||
test/src/mbgl/test/fixture_log_observer.hpp
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/util/optional.hpp>
|
||||
#include <mbgl/util/feature.hpp>
|
||||
#include <mbgl/util/geojson.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -9,9 +11,8 @@ namespace style {
|
||||
namespace conversion {
|
||||
|
||||
/*
|
||||
The `conversion` namespace defines conversions from a templated type `V` representing a JSON
|
||||
object conforming to the schema defined by the Mapbox Style Specification, to the various C++
|
||||
types that form the C++ model of that domain:
|
||||
The `conversion` namespace defines conversions from JSON structures conforming to the schema defined by
|
||||
the Mapbox Style Specification, to the various C++ types that form the C++ model of that domain:
|
||||
|
||||
* `std::unique_ptr<Source>`
|
||||
* `std::unique_ptr<Layer>`
|
||||
@@ -20,15 +21,31 @@ namespace conversion {
|
||||
|
||||
A single template function serves as the public interface:
|
||||
|
||||
template <class T, class V>
|
||||
optional<T> convert(const V& value, Error& error);
|
||||
template <class T>
|
||||
optional<T> convert(const Convertible& input, Error& error);
|
||||
|
||||
Where `T` is one of the above types. If the conversion fails, the result is empty, and the
|
||||
error parameter includes diagnostic text suitable for presentation to a library user. Otherwise,
|
||||
a filled optional is returned.
|
||||
|
||||
The implementation of `convert` requires that the following are legal expressions for a value `v`
|
||||
of type `const V&`:
|
||||
`Convertible` is a type that encapsulates a special form of polymorphism over various underlying types that
|
||||
can serve as input to the conversion algorithm. For instance, on macOS, we need to support
|
||||
conversion from both RapidJSON types, and a JSON structure represented with `NSArray`/`NSDictionary`/etc.
|
||||
On Qt, we need to support conversion from RapidJSON types and QVariant.
|
||||
|
||||
We don't want to use traditional forms of polymorphism to accomplish this:
|
||||
|
||||
* Compile time polymorphism using a template parameter for the actual value type leads to
|
||||
excessive code bloat and long compile times.
|
||||
* Runtime polymorphism using virtual methods requires extra heap allocation and ubiquitous
|
||||
use of std::unique_ptr, unsuitable for this performance-sensitive code.
|
||||
|
||||
Therefore, we're using a custom implementation of runtime polymorphism where we manually create and
|
||||
dispatch through a table of function pointers (vtable), while keeping the storage for any of the possible
|
||||
underlying types inline on the stack, using `std::aligned_storage`.
|
||||
|
||||
For a given underlying type T, an explicit specialization of `ConversionTraits<T>` must be provided. This
|
||||
specialization must provide the following static methods:
|
||||
|
||||
* `isUndefined(v)` -- returns a boolean indication whether `v` is undefined or a JSON null
|
||||
|
||||
@@ -48,21 +65,234 @@ namespace conversion {
|
||||
* `toNumber(v)` -- returns `optional<float>`, absence indicating `v` is not a JSON number
|
||||
* `toDouble(v)` -- returns `optional<double>`, absence indicating `v` is not a JSON number
|
||||
* `toString(v)` -- returns `optional<std::string>`, absence indicating `v` is not a JSON string
|
||||
* `toValue(v)` -- returns `optional<mbgl::Value>`, a variant type, for generic conversion,
|
||||
* `toValue(v)` -- returns `optional<Value>`, a variant type, for generic conversion,
|
||||
absence indicating `v` is not a boolean, number, or string. Numbers should be converted to
|
||||
unsigned integer, signed integer, or floating point, in descending preference.
|
||||
|
||||
The mbgl core implements these requirements for RapidJSON types, and the node bindings implement
|
||||
them for v8 types.
|
||||
In addition, the type T must be move-constructable. And finally, `Convertible::Storage`, a typedef for
|
||||
`std::aligned_storage_t`, must be large enough to satisfy the memory requirements for any of the
|
||||
possible underlying types. (A static assert will fail if this is not the case.)
|
||||
|
||||
`Convertible` itself is movable, but not copyable. A moved-from `Convertible` is in an invalid state;
|
||||
you must not do anything with it except let it go out of scope.
|
||||
*/
|
||||
|
||||
struct Error { std::string message; };
|
||||
|
||||
template <typename T>
|
||||
class ConversionTraits;
|
||||
|
||||
class Convertible {
|
||||
public:
|
||||
template <typename T>
|
||||
Convertible(T&& value) : vtable(vtableForType<std::decay_t<T>>()) {
|
||||
static_assert(sizeof(Storage) >= sizeof(std::decay_t<T>), "Storage must be large enough to hold value type");
|
||||
new (static_cast<void*>(&storage)) std::decay_t<T>(std::forward<T>(value));
|
||||
}
|
||||
|
||||
Convertible(Convertible&& v)
|
||||
: vtable(v.vtable)
|
||||
{
|
||||
if (vtable) {
|
||||
vtable->move(std::move(v.storage), this->storage);
|
||||
}
|
||||
}
|
||||
|
||||
~Convertible() {
|
||||
if (vtable) {
|
||||
vtable->destroy(storage);
|
||||
}
|
||||
}
|
||||
|
||||
Convertible& operator=(Convertible&& v) {
|
||||
if (vtable) {
|
||||
vtable->destroy(storage);
|
||||
}
|
||||
vtable = v.vtable;
|
||||
if (vtable) {
|
||||
vtable->move(std::move(v.storage), this->storage);
|
||||
}
|
||||
v.vtable = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Convertible() = delete;
|
||||
Convertible(const Convertible&) = delete;
|
||||
Convertible& operator=(const Convertible&) = delete;
|
||||
|
||||
friend inline bool isUndefined(const Convertible& v) {
|
||||
assert(v.vtable);
|
||||
return v.vtable->isUndefined(v.storage);
|
||||
}
|
||||
|
||||
friend inline bool isArray(const Convertible& v) {
|
||||
assert(v.vtable);
|
||||
return v.vtable->isArray(v.storage);
|
||||
}
|
||||
|
||||
friend inline std::size_t arrayLength(const Convertible& v) {
|
||||
assert(v.vtable);
|
||||
return v.vtable->arrayLength(v.storage);
|
||||
}
|
||||
|
||||
friend inline Convertible arrayMember(const Convertible& v, std::size_t i) {
|
||||
assert(v.vtable);
|
||||
return v.vtable->arrayMember(v.storage, i);
|
||||
}
|
||||
|
||||
friend inline bool isObject(const Convertible& v) {
|
||||
assert(v.vtable);
|
||||
return v.vtable->isObject(v.storage);
|
||||
}
|
||||
|
||||
friend inline optional<Convertible> objectMember(const Convertible& v, const char * name) {
|
||||
assert(v.vtable);
|
||||
return v.vtable->objectMember(v.storage, name);
|
||||
}
|
||||
|
||||
friend inline optional<Error> eachMember(const Convertible& v, const std::function<optional<Error> (const std::string&, const Convertible&)>& fn) {
|
||||
assert(v.vtable);
|
||||
return v.vtable->eachMember(v.storage, fn);
|
||||
}
|
||||
|
||||
friend inline optional<bool> toBool(const Convertible& v) {
|
||||
assert(v.vtable);
|
||||
return v.vtable->toBool(v.storage);
|
||||
}
|
||||
|
||||
friend inline optional<float> toNumber(const Convertible& v) {
|
||||
assert(v.vtable);
|
||||
return v.vtable->toNumber(v.storage);
|
||||
}
|
||||
|
||||
friend inline optional<double> toDouble(const Convertible& v) {
|
||||
assert(v.vtable);
|
||||
return v.vtable->toDouble(v.storage);
|
||||
}
|
||||
|
||||
friend inline optional<std::string> toString(const Convertible& v) {
|
||||
assert(v.vtable);
|
||||
return v.vtable->toString(v.storage);
|
||||
}
|
||||
|
||||
friend inline optional<Value> toValue(const Convertible& v) {
|
||||
assert(v.vtable);
|
||||
return v.vtable->toValue(v.storage);
|
||||
}
|
||||
|
||||
friend inline optional<GeoJSON> toGeoJSON(const Convertible& v, Error& error) {
|
||||
assert(v.vtable);
|
||||
return v.vtable->toGeoJSON(v.storage, error);
|
||||
}
|
||||
|
||||
private:
|
||||
#if __ANDROID__
|
||||
// Android: JSValue* or mbgl::android::Value
|
||||
using Storage = std::aligned_storage_t<32, 8>;
|
||||
#elif __QT__
|
||||
// Qt: JSValue* or QVariant
|
||||
using Storage = std::aligned_storage_t<32, 8>;
|
||||
#else
|
||||
// Node: JSValue* or v8::Local<v8::Value>
|
||||
// iOS/macOS: JSValue* or id
|
||||
using Storage = std::aligned_storage_t<8, 8>;
|
||||
#endif
|
||||
|
||||
struct VTable {
|
||||
void (*move) (Storage&& src, Storage& dest);
|
||||
void (*destroy) (Storage&);
|
||||
|
||||
bool (*isUndefined) (const Storage&);
|
||||
|
||||
bool (*isArray) (const Storage&);
|
||||
std::size_t (*arrayLength) (const Storage&);
|
||||
Convertible (*arrayMember) (const Storage&, std::size_t);
|
||||
|
||||
bool (*isObject) (const Storage&);
|
||||
optional<Convertible> (*objectMember) (const Storage&, const char *);
|
||||
optional<Error> (*eachMember) (const Storage&, const std::function<optional<Error> (const std::string&, const Convertible&)>&);
|
||||
|
||||
optional<bool> (*toBool) (const Storage&);
|
||||
optional<float> (*toNumber) (const Storage&);
|
||||
optional<double> (*toDouble) (const Storage&);
|
||||
optional<std::string> (*toString) (const Storage&);
|
||||
optional<Value> (*toValue) (const Storage&);
|
||||
|
||||
// https://github.com/mapbox/mapbox-gl-native/issues/5623
|
||||
optional<GeoJSON> (*toGeoJSON) (const Storage&, Error&);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
static VTable* vtableForType() {
|
||||
using Traits = ConversionTraits<T>;
|
||||
static VTable vtable = {
|
||||
[] (Storage&& src, Storage& dest) {
|
||||
auto srcValue = reinterpret_cast<T&&>(src);
|
||||
new (static_cast<void*>(&dest)) T(std::move(srcValue));
|
||||
srcValue.~T();
|
||||
},
|
||||
[] (Storage& s) {
|
||||
reinterpret_cast<T&>(s).~T();
|
||||
},
|
||||
[] (const Storage& s) {
|
||||
return Traits::isUndefined(reinterpret_cast<const T&>(s));
|
||||
},
|
||||
[] (const Storage& s) {
|
||||
return Traits::isArray(reinterpret_cast<const T&>(s));
|
||||
},
|
||||
[] (const Storage& s) {
|
||||
return Traits::arrayLength(reinterpret_cast<const T&>(s));
|
||||
},
|
||||
[] (const Storage& s, std::size_t i) {
|
||||
return Convertible(Traits::arrayMember(reinterpret_cast<const T&>(s), i));
|
||||
},
|
||||
[] (const Storage& s) {
|
||||
return Traits::isObject(reinterpret_cast<const T&>(s));
|
||||
},
|
||||
[] (const Storage& s, const char * key) {
|
||||
optional<T> member = Traits::objectMember(reinterpret_cast<const T&>(s), key);
|
||||
if (member) {
|
||||
return optional<Convertible>(Convertible(std::move(*member)));
|
||||
} else {
|
||||
return optional<Convertible>();
|
||||
}
|
||||
},
|
||||
[] (const Storage& s, const std::function<optional<Error> (const std::string&, const Convertible&)>& fn) {
|
||||
return Traits::eachMember(reinterpret_cast<const T&>(s), [&](const std::string& k, T&& v) {
|
||||
return fn(k, Convertible(std::move(v)));
|
||||
});
|
||||
},
|
||||
[] (const Storage& s) {
|
||||
return Traits::toBool(reinterpret_cast<const T&>(s));
|
||||
},
|
||||
[] (const Storage& s) {
|
||||
return Traits::toNumber(reinterpret_cast<const T&>(s));
|
||||
},
|
||||
[] (const Storage& s) {
|
||||
return Traits::toDouble(reinterpret_cast<const T&>(s));
|
||||
},
|
||||
[] (const Storage& s) {
|
||||
return Traits::toString(reinterpret_cast<const T&>(s));
|
||||
},
|
||||
[] (const Storage& s) {
|
||||
return Traits::toValue(reinterpret_cast<const T&>(s));
|
||||
},
|
||||
[] (const Storage& s, Error& err) {
|
||||
return Traits::toGeoJSON(reinterpret_cast<const T&>(s), err);
|
||||
}
|
||||
};
|
||||
return &vtable;
|
||||
}
|
||||
|
||||
VTable* vtable;
|
||||
Storage storage;
|
||||
};
|
||||
|
||||
template <class T, class Enable = void>
|
||||
struct Converter;
|
||||
|
||||
template <class T, class V, class...Args>
|
||||
optional<T> convert(const V& value, Error& error, Args&&...args) {
|
||||
template <class T, class...Args>
|
||||
optional<T> convert(const Convertible& value, Error& error, Args&&...args) {
|
||||
return Converter<T>()(value, error, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/style/conversion.hpp>
|
||||
#include <mbgl/util/optional.hpp>
|
||||
#include <mbgl/util/color.hpp>
|
||||
#include <mbgl/util/enum.hpp>
|
||||
#include <mbgl/util/string.hpp>
|
||||
@@ -16,47 +15,22 @@ namespace conversion {
|
||||
|
||||
template <>
|
||||
struct Converter<bool> {
|
||||
template <class V>
|
||||
optional<bool> operator()(const V& value, Error& error) const {
|
||||
optional<bool> converted = toBool(value);
|
||||
if (!converted) {
|
||||
error = { "value must be a boolean" };
|
||||
return {};
|
||||
}
|
||||
return *converted;
|
||||
}
|
||||
optional<bool> operator()(const Convertible& value, Error& error) const;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Converter<float> {
|
||||
template <class V>
|
||||
optional<float> operator()(const V& value, Error& error) const {
|
||||
optional<float> converted = toNumber(value);
|
||||
if (!converted) {
|
||||
error = { "value must be a number" };
|
||||
return {};
|
||||
}
|
||||
return *converted;
|
||||
}
|
||||
optional<float> operator()(const Convertible& value, Error& error) const;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Converter<std::string> {
|
||||
template <class V>
|
||||
optional<std::string> operator()(const V& value, Error& error) const {
|
||||
optional<std::string> converted = toString(value);
|
||||
if (!converted) {
|
||||
error = { "value must be a string" };
|
||||
return {};
|
||||
}
|
||||
return *converted;
|
||||
}
|
||||
optional<std::string> operator()(const Convertible& value, Error& error) const;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct Converter<T, typename std::enable_if_t<std::is_enum<T>::value>> {
|
||||
template <class V>
|
||||
optional<T> operator()(const V& value, Error& error) const {
|
||||
optional<T> operator()(const Convertible& value, Error& error) const {
|
||||
optional<std::string> string = toString(value);
|
||||
if (!string) {
|
||||
error = { "value must be a string" };
|
||||
@@ -75,28 +49,12 @@ struct Converter<T, typename std::enable_if_t<std::is_enum<T>::value>> {
|
||||
|
||||
template <>
|
||||
struct Converter<Color> {
|
||||
template <class V>
|
||||
optional<Color> operator()(const V& value, Error& error) const {
|
||||
optional<std::string> string = toString(value);
|
||||
if (!string) {
|
||||
error = { "value must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<Color> color = Color::parse(*string);
|
||||
if (!color) {
|
||||
error = { "value must be a valid color" };
|
||||
return {};
|
||||
}
|
||||
|
||||
return *color;
|
||||
}
|
||||
optional<Color> operator()(const Convertible& value, Error& error) const;
|
||||
};
|
||||
|
||||
template <size_t N>
|
||||
struct Converter<std::array<float, N>> {
|
||||
template <class V>
|
||||
optional<std::array<float, N>> operator()(const V& value, Error& error) const {
|
||||
optional<std::array<float, N>> operator()(const Convertible& value, Error& error) const {
|
||||
if (!isArray(value) || arrayLength(value) != N) {
|
||||
error = { "value must be an array of " + util::toString(N) + " numbers" };
|
||||
return {};
|
||||
@@ -117,52 +75,12 @@ struct Converter<std::array<float, N>> {
|
||||
|
||||
template <>
|
||||
struct Converter<std::vector<float>> {
|
||||
template <class V>
|
||||
optional<std::vector<float>> operator()(const V& value, Error& error) const {
|
||||
if (!isArray(value)) {
|
||||
error = { "value must be an array" };
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<float> result;
|
||||
result.reserve(arrayLength(value));
|
||||
|
||||
for (std::size_t i = 0; i < arrayLength(value); ++i) {
|
||||
optional<float> number = toNumber(arrayMember(value, i));
|
||||
if (!number) {
|
||||
error = { "value must be an array of numbers" };
|
||||
return {};
|
||||
}
|
||||
result.push_back(*number);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
optional<std::vector<float>> operator()(const Convertible& value, Error& error) const;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Converter<std::vector<std::string>> {
|
||||
template <class V>
|
||||
optional<std::vector<std::string>> operator()(const V& value, Error& error) const {
|
||||
if (!isArray(value)) {
|
||||
error = { "value must be an array" };
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<std::string> result;
|
||||
result.reserve(arrayLength(value));
|
||||
|
||||
for (std::size_t i = 0; i < arrayLength(value); ++i) {
|
||||
optional<std::string> string = toString(arrayMember(value, i));
|
||||
if (!string) {
|
||||
error = { "value must be an array of strings" };
|
||||
return {};
|
||||
}
|
||||
result.push_back(*string);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
optional<std::vector<std::string>> operator()(const Convertible& value, Error& error) const;
|
||||
};
|
||||
|
||||
} // namespace conversion
|
||||
|
||||
@@ -10,26 +10,7 @@ namespace conversion {
|
||||
template<>
|
||||
struct Converter<LatLng> {
|
||||
public:
|
||||
template <class V>
|
||||
optional<LatLng> operator() (const V& value, Error& error) const {
|
||||
if (!isArray(value) || arrayLength(value) < 2 ) {
|
||||
error = { "coordinate array must contain numeric longitude and latitude values" };
|
||||
return {};
|
||||
}
|
||||
//Style spec uses GeoJSON convention for specifying coordinates
|
||||
optional<double> latitude = toDouble(arrayMember(value, 1));
|
||||
optional<double> longitude = toDouble(arrayMember(value, 0));
|
||||
|
||||
if (!latitude || !longitude) {
|
||||
error = { "coordinate array must contain numeric longitude and latitude values" };
|
||||
return {};
|
||||
}
|
||||
if (*latitude < -90 || *latitude > 90 ){
|
||||
error = { "coordinate latitude must be between -90 and 90" };
|
||||
return {};
|
||||
}
|
||||
return LatLng(*latitude, *longitude);
|
||||
}
|
||||
optional<LatLng> operator() (const Convertible& value, Error& error) const;
|
||||
};
|
||||
|
||||
} // namespace conversion
|
||||
|
||||
@@ -11,8 +11,7 @@ namespace conversion {
|
||||
|
||||
template <class T>
|
||||
struct Converter<DataDrivenPropertyValue<T>> {
|
||||
template <class V>
|
||||
optional<DataDrivenPropertyValue<T>> operator()(const V& value, Error& error) const {
|
||||
optional<DataDrivenPropertyValue<T>> operator()(const Convertible& value, Error& error) const {
|
||||
if (isUndefined(value)) {
|
||||
return DataDrivenPropertyValue<T>();
|
||||
} else if (!isObject(value)) {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <mbgl/style/filter.hpp>
|
||||
#include <mbgl/style/conversion.hpp>
|
||||
#include <mbgl/util/geometry.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
@@ -11,247 +10,7 @@ namespace conversion {
|
||||
template <>
|
||||
struct Converter<Filter> {
|
||||
public:
|
||||
template <class V>
|
||||
optional<Filter> operator()(const V& value, Error& error) const {
|
||||
if (!isArray(value)) {
|
||||
error = { "filter expression must be an array" };
|
||||
return {};
|
||||
}
|
||||
|
||||
if (arrayLength(value) < 1) {
|
||||
error = { "filter expression must have at least 1 element" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<std::string> op = toString(arrayMember(value, 0));
|
||||
if (!op) {
|
||||
error = { "filter operator must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
if (*op == "==") {
|
||||
return convertEqualityFilter<EqualsFilter, TypeEqualsFilter, IdentifierEqualsFilter>(value, error);
|
||||
} else if (*op == "!=") {
|
||||
return convertEqualityFilter<NotEqualsFilter, TypeNotEqualsFilter, IdentifierNotEqualsFilter>(value, error);
|
||||
} else if (*op == ">") {
|
||||
return convertBinaryFilter<GreaterThanFilter>(value, error);
|
||||
} else if (*op == ">=") {
|
||||
return convertBinaryFilter<GreaterThanEqualsFilter>(value, error);
|
||||
} else if (*op == "<") {
|
||||
return convertBinaryFilter<LessThanFilter>(value, error);
|
||||
} else if (*op == "<=") {
|
||||
return convertBinaryFilter<LessThanEqualsFilter>(value, error);
|
||||
} else if (*op == "in") {
|
||||
return convertSetFilter<InFilter, TypeInFilter, IdentifierInFilter>(value, error);
|
||||
} else if (*op == "!in") {
|
||||
return convertSetFilter<NotInFilter, TypeNotInFilter, IdentifierNotInFilter>(value, error);
|
||||
} else if (*op == "all") {
|
||||
return convertCompoundFilter<AllFilter>(value, error);
|
||||
} else if (*op == "any") {
|
||||
return convertCompoundFilter<AnyFilter>(value, error);
|
||||
} else if (*op == "none") {
|
||||
return convertCompoundFilter<NoneFilter>(value, error);
|
||||
} else if (*op == "has") {
|
||||
return convertUnaryFilter<HasFilter, HasIdentifierFilter>(value, error);
|
||||
} else if (*op == "!has") {
|
||||
return convertUnaryFilter<NotHasFilter, NotHasIdentifierFilter>(value, error);
|
||||
}
|
||||
|
||||
error = { R"(filter operator must be one of "==", "!=", ">", ">=", "<", "<=", "in", "!in", "all", "any", "none", "has", or "!has")" };
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
optional<Value> normalizeValue(const optional<Value>& value, Error& error) const {
|
||||
if (!value) {
|
||||
error = { "filter expression value must be a boolean, number, or string" };
|
||||
return {};
|
||||
} else {
|
||||
return *value;
|
||||
}
|
||||
}
|
||||
|
||||
template <class V>
|
||||
optional<FeatureType> toFeatureType(const V& value, Error& error) const {
|
||||
optional<std::string> type = toString(value);
|
||||
if (!type) {
|
||||
error = { "value for $type filter must be a string" };
|
||||
return {};
|
||||
} else if (*type == "Point") {
|
||||
return FeatureType::Point;
|
||||
} else if (*type == "LineString") {
|
||||
return FeatureType::LineString;
|
||||
} else if (*type == "Polygon") {
|
||||
return FeatureType::Polygon;
|
||||
} else {
|
||||
error = { "value for $type filter must be Point, LineString, or Polygon" };
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
template <class V>
|
||||
optional<FeatureIdentifier> toFeatureIdentifier(const V& value, Error& error) const {
|
||||
optional<Value> identifier = toValue(value);
|
||||
if (!identifier) {
|
||||
error = { "filter expression value must be a boolean, number, or string" };
|
||||
return {};
|
||||
} else {
|
||||
return (*identifier).match(
|
||||
[] (uint64_t t) -> optional<FeatureIdentifier> { return { t }; },
|
||||
[] ( int64_t t) -> optional<FeatureIdentifier> { return { t }; },
|
||||
[] ( double t) -> optional<FeatureIdentifier> { return { t }; },
|
||||
[] (const std::string& t) -> optional<FeatureIdentifier> { return { t }; },
|
||||
[&] (const auto&) -> optional<FeatureIdentifier> {
|
||||
error = { "filter expression value must be a boolean, number, or string" };
|
||||
return {};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
template <class FilterType, class IdentifierFilterType, class V>
|
||||
optional<Filter> convertUnaryFilter(const V& value, Error& error) const {
|
||||
if (arrayLength(value) < 2) {
|
||||
error = { "filter expression must have 2 elements" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<std::string> key = toString(arrayMember(value, 1));
|
||||
if (!key) {
|
||||
error = { "filter expression key must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
if (*key == "$id") {
|
||||
return { IdentifierFilterType {} };
|
||||
} else {
|
||||
return { FilterType { *key } };
|
||||
}
|
||||
}
|
||||
|
||||
template <class FilterType, class TypeFilterType, class IdentifierFilterType, class V>
|
||||
optional<Filter> convertEqualityFilter(const V& value, Error& error) const {
|
||||
if (arrayLength(value) < 3) {
|
||||
error = { "filter expression must have 3 elements" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<std::string> key = toString(arrayMember(value, 1));
|
||||
if (!key) {
|
||||
error = { "filter expression key must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
if (*key == "$type") {
|
||||
optional<FeatureType> filterValue = toFeatureType(arrayMember(value, 2), error);
|
||||
if (!filterValue) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return { TypeFilterType { *filterValue } };
|
||||
|
||||
} else if (*key == "$id") {
|
||||
optional<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, 2), error);
|
||||
if (!filterValue) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return { IdentifierFilterType { *filterValue } };
|
||||
|
||||
} else {
|
||||
optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error);
|
||||
if (!filterValue) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return { FilterType { *key, *filterValue } };
|
||||
}
|
||||
}
|
||||
|
||||
template <class FilterType, class V>
|
||||
optional<Filter> convertBinaryFilter(const V& value, Error& error) const {
|
||||
if (arrayLength(value) < 3) {
|
||||
error = { "filter expression must have 3 elements" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<std::string> key = toString(arrayMember(value, 1));
|
||||
if (!key) {
|
||||
error = { "filter expression key must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error);
|
||||
if (!filterValue) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return { FilterType { *key, *filterValue } };
|
||||
}
|
||||
|
||||
template <class FilterType, class TypeFilterType, class IdentifierFilterType, class V>
|
||||
optional<Filter> convertSetFilter(const V& value, Error& error) const {
|
||||
if (arrayLength(value) < 2) {
|
||||
error = { "filter expression must at least 2 elements" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<std::string> key = toString(arrayMember(value, 1));
|
||||
if (!key) {
|
||||
error = { "filter expression key must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
if (*key == "$type") {
|
||||
std::vector<FeatureType> values;
|
||||
for (std::size_t i = 2; i < arrayLength(value); ++i) {
|
||||
optional<FeatureType> filterValue = toFeatureType(arrayMember(value, i), error);
|
||||
if (!filterValue) {
|
||||
return {};
|
||||
}
|
||||
values.push_back(*filterValue);
|
||||
}
|
||||
|
||||
return { TypeFilterType { std::move(values) } };
|
||||
|
||||
} else if (*key == "$id") {
|
||||
std::vector<FeatureIdentifier> values;
|
||||
for (std::size_t i = 2; i < arrayLength(value); ++i) {
|
||||
optional<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, i), error);
|
||||
if (!filterValue) {
|
||||
return {};
|
||||
}
|
||||
values.push_back(*filterValue);
|
||||
}
|
||||
|
||||
return { IdentifierFilterType { std::move(values) } };
|
||||
|
||||
} else {
|
||||
std::vector<Value> values;
|
||||
for (std::size_t i = 2; i < arrayLength(value); ++i) {
|
||||
optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, i)), error);
|
||||
if (!filterValue) {
|
||||
return {};
|
||||
}
|
||||
values.push_back(*filterValue);
|
||||
}
|
||||
|
||||
return { FilterType { *key, std::move(values) } };
|
||||
}
|
||||
}
|
||||
|
||||
template <class FilterType, class V>
|
||||
optional<Filter> convertCompoundFilter(const V& value, Error& error) const {
|
||||
std::vector<Filter> filters;
|
||||
for (std::size_t i = 1; i < arrayLength(value); ++i) {
|
||||
optional<Filter> element = operator()(arrayMember(value, i), error);
|
||||
if (!element) {
|
||||
return {};
|
||||
}
|
||||
filters.push_back(*element);
|
||||
}
|
||||
|
||||
return { FilterType { std::move(filters) } };
|
||||
}
|
||||
optional<Filter> operator()(const Convertible& value, Error& error) const;
|
||||
};
|
||||
|
||||
} // namespace conversion
|
||||
|
||||
@@ -11,8 +11,8 @@ namespace mbgl {
|
||||
namespace style {
|
||||
namespace conversion {
|
||||
|
||||
template <class D, class R, class V>
|
||||
optional<std::map<D, R>> convertStops(const V& value, Error& error) {
|
||||
template <class D, class R>
|
||||
optional<std::map<D, R>> convertStops(const Convertible& value, Error& error) {
|
||||
auto stopsValue = objectMember(value, "stops");
|
||||
if (!stopsValue) {
|
||||
error = { "function value must specify stops" };
|
||||
@@ -63,8 +63,7 @@ template <class T>
|
||||
struct Converter<ExponentialStops<T>> {
|
||||
static constexpr const char * type = "exponential";
|
||||
|
||||
template <class V>
|
||||
optional<ExponentialStops<T>> operator()(const V& value, Error& error) const {
|
||||
optional<ExponentialStops<T>> operator()(const Convertible& value, Error& error) const {
|
||||
auto stops = convertStops<float, T>(value, error);
|
||||
if (!stops) {
|
||||
return {};
|
||||
@@ -89,8 +88,7 @@ template <class T>
|
||||
struct Converter<IntervalStops<T>> {
|
||||
static constexpr const char * type = "interval";
|
||||
|
||||
template <class V>
|
||||
optional<IntervalStops<T>> operator()(const V& value, Error& error) const {
|
||||
optional<IntervalStops<T>> operator()(const Convertible& value, Error& error) const {
|
||||
auto stops = convertStops<float, T>(value, error);
|
||||
if (!stops) {
|
||||
return {};
|
||||
@@ -101,8 +99,7 @@ struct Converter<IntervalStops<T>> {
|
||||
|
||||
template <>
|
||||
struct Converter<CategoricalValue> {
|
||||
template <class V>
|
||||
optional<CategoricalValue> operator()(const V& value, Error& error) const {
|
||||
optional<CategoricalValue> operator()(const Convertible& value, Error& error) const {
|
||||
auto b = toBool(value);
|
||||
if (b) {
|
||||
return { *b };
|
||||
@@ -127,8 +124,7 @@ template <class T>
|
||||
struct Converter<CategoricalStops<T>> {
|
||||
static constexpr const char * type = "categorical";
|
||||
|
||||
template <class V>
|
||||
optional<CategoricalStops<T>> operator()(const V& value, Error& error) const {
|
||||
optional<CategoricalStops<T>> operator()(const Convertible& value, Error& error) const {
|
||||
auto stops = convertStops<CategoricalValue, T>(value, error);
|
||||
if (!stops) {
|
||||
return {};
|
||||
@@ -142,8 +138,7 @@ template <class T>
|
||||
struct Converter<IdentityStops<T>> {
|
||||
static constexpr const char * type = "identity";
|
||||
|
||||
template <class V>
|
||||
optional<IdentityStops<T>> operator()(const V&, Error&) const {
|
||||
optional<IdentityStops<T>> operator()(const Convertible&, Error&) const {
|
||||
return IdentityStops<T>();
|
||||
}
|
||||
};
|
||||
@@ -154,8 +149,7 @@ struct StopsConverter;
|
||||
template <class T, class... Ts>
|
||||
struct StopsConverter<T, variant<Ts...>> {
|
||||
public:
|
||||
template <class V>
|
||||
optional<variant<Ts...>> operator()(const V& value, Error& error) const {
|
||||
optional<variant<Ts...>> operator()(const Convertible& value, Error& error) const {
|
||||
std::string type = util::Interpolatable<T>::value ? "exponential" : "interval";
|
||||
|
||||
auto typeValue = objectMember(value, "type");
|
||||
@@ -193,8 +187,7 @@ public:
|
||||
|
||||
template <class T>
|
||||
struct Converter<CameraFunction<T>> {
|
||||
template <class V>
|
||||
optional<CameraFunction<T>> operator()(const V& value, Error& error) const {
|
||||
optional<CameraFunction<T>> operator()(const Convertible& value, Error& error) const {
|
||||
if (!isObject(value)) {
|
||||
error = { "function must be an object" };
|
||||
return {};
|
||||
@@ -209,8 +202,8 @@ struct Converter<CameraFunction<T>> {
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class V>
|
||||
optional<optional<T>> convertDefaultValue(const V& value, Error& error) {
|
||||
template <class T>
|
||||
optional<optional<T>> convertDefaultValue(const Convertible& value, Error& error) {
|
||||
auto defaultValueValue = objectMember(value, "default");
|
||||
if (!defaultValueValue) {
|
||||
return optional<T>();
|
||||
@@ -227,8 +220,7 @@ optional<optional<T>> convertDefaultValue(const V& value, Error& error) {
|
||||
|
||||
template <class T>
|
||||
struct Converter<SourceFunction<T>> {
|
||||
template <class V>
|
||||
optional<SourceFunction<T>> operator()(const V& value, Error& error) const {
|
||||
optional<SourceFunction<T>> operator()(const Convertible& value, Error& error) const {
|
||||
if (!isObject(value)) {
|
||||
error = { "function must be an object" };
|
||||
return {};
|
||||
@@ -267,8 +259,7 @@ struct CompositeValue : std::pair<float, S> {
|
||||
|
||||
template <class S>
|
||||
struct Converter<CompositeValue<S>> {
|
||||
template <class V>
|
||||
optional<CompositeValue<S>> operator()(const V& value, Error& error) const {
|
||||
optional<CompositeValue<S>> operator()(const Convertible& value, Error& error) const {
|
||||
if (!isObject(value)) {
|
||||
error = { "stop must be an object" };
|
||||
return {};
|
||||
@@ -304,8 +295,7 @@ template <class T>
|
||||
struct Converter<CompositeExponentialStops<T>> {
|
||||
static constexpr const char * type = "exponential";
|
||||
|
||||
template <class V>
|
||||
optional<CompositeExponentialStops<T>> operator()(const V& value, Error& error) const {
|
||||
optional<CompositeExponentialStops<T>> operator()(const Convertible& value, Error& error) const {
|
||||
auto stops = convertStops<CompositeValue<float>, T>(value, error);
|
||||
if (!stops) {
|
||||
return {};
|
||||
@@ -330,8 +320,7 @@ template <class T>
|
||||
struct Converter<CompositeIntervalStops<T>> {
|
||||
static constexpr const char * type = "interval";
|
||||
|
||||
template <class V>
|
||||
optional<CompositeIntervalStops<T>> operator()(const V& value, Error& error) const {
|
||||
optional<CompositeIntervalStops<T>> operator()(const Convertible& value, Error& error) const {
|
||||
auto stops = convertStops<CompositeValue<float>, T>(value, error);
|
||||
if (!stops) {
|
||||
return {};
|
||||
@@ -350,8 +339,7 @@ template <class T>
|
||||
struct Converter<CompositeCategoricalStops<T>> {
|
||||
static constexpr const char * type = "categorical";
|
||||
|
||||
template <class V>
|
||||
optional<CompositeCategoricalStops<T>> operator()(const V& value, Error& error) const {
|
||||
optional<CompositeCategoricalStops<T>> operator()(const Convertible& value, Error& error) const {
|
||||
auto stops = convertStops<CompositeValue<CategoricalValue>, T>(value, error);
|
||||
if (!stops) {
|
||||
return {};
|
||||
@@ -368,8 +356,7 @@ struct Converter<CompositeCategoricalStops<T>> {
|
||||
|
||||
template <class T>
|
||||
struct Converter<CompositeFunction<T>> {
|
||||
template <class V>
|
||||
optional<CompositeFunction<T>> operator()(const V& value, Error& error) const {
|
||||
optional<CompositeFunction<T>> operator()(const Convertible& value, Error& error) const {
|
||||
if (!isObject(value)) {
|
||||
error = { "function must be an object" };
|
||||
return {};
|
||||
|
||||
@@ -7,15 +7,13 @@ namespace mbgl {
|
||||
namespace style {
|
||||
namespace conversion {
|
||||
|
||||
// Workaround until https://github.com/mapbox/mapbox-gl-native/issues/5623 is done.
|
||||
optional<GeoJSON> parseGeoJSON(const std::string&, Error&);
|
||||
|
||||
template <>
|
||||
struct Converter<GeoJSON> {
|
||||
public:
|
||||
optional<GeoJSON> operator()(const std::string&, Error&) const;
|
||||
|
||||
// This is explicitly specialized in the .cpp file for JSValue. It may also be explicitly
|
||||
// specialized for SDK-specific types (e.g. mbgl::android::Value).
|
||||
template <class V>
|
||||
optional<GeoJSON> operator()(const V&, Error&) const;
|
||||
optional<GeoJSON> operator()(const Convertible&, Error&) const;
|
||||
};
|
||||
|
||||
} // namespace conversion
|
||||
|
||||
@@ -9,84 +9,7 @@ namespace conversion {
|
||||
|
||||
template <>
|
||||
struct Converter<GeoJSONOptions> {
|
||||
|
||||
template <class V>
|
||||
optional<GeoJSONOptions> operator()(const V& value, Error& error) const {
|
||||
GeoJSONOptions options;
|
||||
|
||||
const auto minzoomValue = objectMember(value, "minzoom");
|
||||
if (minzoomValue) {
|
||||
if (toNumber(*minzoomValue)) {
|
||||
options.minzoom = static_cast<uint8_t>(*toNumber(*minzoomValue));
|
||||
} else {
|
||||
error = { "GeoJSON source minzoom value must be a number" };
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto maxzoomValue = objectMember(value, "maxzoom");
|
||||
if (maxzoomValue) {
|
||||
if (toNumber(*maxzoomValue)) {
|
||||
options.maxzoom = static_cast<uint8_t>(*toNumber(*maxzoomValue));
|
||||
} else {
|
||||
error = { "GeoJSON source maxzoom value must be a number" };
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto bufferValue = objectMember(value, "buffer");
|
||||
if (bufferValue) {
|
||||
if (toNumber(*bufferValue)) {
|
||||
options.buffer = static_cast<uint16_t>(*toNumber(*bufferValue));
|
||||
} else {
|
||||
error = { "GeoJSON source buffer value must be a number" };
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto toleranceValue = objectMember(value, "tolerance");
|
||||
if (toleranceValue) {
|
||||
if (toNumber(*toleranceValue)) {
|
||||
options.tolerance = static_cast<double>(*toNumber(*toleranceValue));
|
||||
} else {
|
||||
error = { "GeoJSON source tolerance value must be a number" };
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto clusterValue = objectMember(value, "cluster");
|
||||
if (clusterValue) {
|
||||
if (toBool(*clusterValue)) {
|
||||
options.cluster = *toBool(*clusterValue);
|
||||
} else {
|
||||
error = { "GeoJSON source cluster value must be a boolean" };
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto clusterMaxZoomValue = objectMember(value, "clusterMaxZoom");
|
||||
if (clusterMaxZoomValue) {
|
||||
if (toNumber(*clusterMaxZoomValue)) {
|
||||
options.clusterMaxZoom = static_cast<uint8_t>(*toNumber(*clusterMaxZoomValue));
|
||||
} else {
|
||||
error = { "GeoJSON source clusterMaxZoom value must be a number" };
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto clusterRadiusValue = objectMember(value, "clusterRadius");
|
||||
if (clusterRadiusValue) {
|
||||
if (toNumber(*clusterRadiusValue)) {
|
||||
options.clusterRadius = static_cast<double>(*toNumber(*clusterRadiusValue));
|
||||
} else {
|
||||
error = { "GeoJSON source clusterRadius value must be a number" };
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return { options };
|
||||
}
|
||||
|
||||
optional<GeoJSONOptions> operator()(const Convertible& value, Error& error) const;
|
||||
};
|
||||
|
||||
} // namespace conversion
|
||||
|
||||
@@ -1,220 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/style/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/conversion.hpp>
|
||||
#include <mbgl/style/conversion/constant.hpp>
|
||||
#include <mbgl/style/conversion/filter.hpp>
|
||||
#include <mbgl/style/conversion/make_property_setters.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
namespace conversion {
|
||||
|
||||
template <class V>
|
||||
optional<Error> setLayoutProperty(Layer& layer, const std::string& name, const V& value) {
|
||||
static const auto setters = makeLayoutPropertySetters<V>();
|
||||
auto it = setters.find(name);
|
||||
if (it == setters.end()) {
|
||||
return Error { "property not found" };
|
||||
}
|
||||
return it->second(layer, value);
|
||||
}
|
||||
|
||||
template <class V>
|
||||
optional<Error> setPaintProperty(Layer& layer, const std::string& name, const V& value) {
|
||||
static const auto setters = makePaintPropertySetters<V>();
|
||||
auto it = setters.find(name);
|
||||
if (it == setters.end()) {
|
||||
return Error { "property not found" };
|
||||
}
|
||||
return it->second(layer, value);
|
||||
}
|
||||
|
||||
template <class V>
|
||||
optional<Error> setPaintProperties(Layer& layer, const V& value) {
|
||||
auto paintValue = objectMember(value, "paint");
|
||||
if (!paintValue) {
|
||||
return {};
|
||||
}
|
||||
return eachMember(*paintValue, [&] (const std::string& k, const V& v) {
|
||||
return setPaintProperty(layer, k, v);
|
||||
});
|
||||
}
|
||||
|
||||
template <>
|
||||
struct Converter<std::unique_ptr<Layer>> {
|
||||
public:
|
||||
template <class V>
|
||||
optional<std::unique_ptr<Layer>> operator()(const V& value, Error& error) const {
|
||||
if (!isObject(value)) {
|
||||
error = { "layer must be an object" };
|
||||
return {};
|
||||
}
|
||||
|
||||
auto idValue = objectMember(value, "id");
|
||||
if (!idValue) {
|
||||
error = { "layer must have an id" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<std::string> id = toString(*idValue);
|
||||
if (!id) {
|
||||
error = { "layer id must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
auto typeValue = objectMember(value, "type");
|
||||
if (!typeValue) {
|
||||
error = { "layer must have a type" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<std::string> type = toString(*typeValue);
|
||||
if (!type) {
|
||||
error = { "layer type must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<std::unique_ptr<Layer>> converted;
|
||||
|
||||
if (*type == "fill") {
|
||||
converted = convertVectorLayer<FillLayer>(*id, value, error);
|
||||
} else if (*type == "fill-extrusion") {
|
||||
converted = convertVectorLayer<FillExtrusionLayer>(*id, value, error);
|
||||
} else if (*type == "line") {
|
||||
converted = convertVectorLayer<LineLayer>(*id, value, error);
|
||||
} else if (*type == "circle") {
|
||||
converted = convertVectorLayer<CircleLayer>(*id, value, error);
|
||||
} else if (*type == "symbol") {
|
||||
converted = convertVectorLayer<SymbolLayer>(*id, value, error);
|
||||
} else if (*type == "raster") {
|
||||
converted = convertRasterLayer(*id, value, error);
|
||||
} else if (*type == "background") {
|
||||
converted = convertBackgroundLayer(*id, value, error);
|
||||
} else {
|
||||
error = { "invalid layer type" };
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!converted) {
|
||||
return converted;
|
||||
}
|
||||
|
||||
std::unique_ptr<Layer> layer = std::move(*converted);
|
||||
|
||||
auto minzoomValue = objectMember(value, "minzoom");
|
||||
if (minzoomValue) {
|
||||
optional<float> minzoom = toNumber(*minzoomValue);
|
||||
if (!minzoom) {
|
||||
error = { "minzoom must be numeric" };
|
||||
return {};
|
||||
}
|
||||
layer->setMinZoom(*minzoom);
|
||||
}
|
||||
|
||||
auto maxzoomValue = objectMember(value, "maxzoom");
|
||||
if (maxzoomValue) {
|
||||
optional<float> maxzoom = toNumber(*maxzoomValue);
|
||||
if (!maxzoom) {
|
||||
error = { "maxzoom must be numeric" };
|
||||
return {};
|
||||
}
|
||||
layer->setMaxZoom(*maxzoom);
|
||||
}
|
||||
|
||||
auto layoutValue = objectMember(value, "layout");
|
||||
if (layoutValue) {
|
||||
if (!isObject(*layoutValue)) {
|
||||
error = { "layout must be an object" };
|
||||
return {};
|
||||
}
|
||||
optional<Error> error_ = eachMember(*layoutValue, [&] (const std::string& k, const V& v) {
|
||||
return setLayoutProperty(*layer, k, v);
|
||||
});
|
||||
if (error_) {
|
||||
error = *error_;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
optional<Error> error_ = setPaintProperties(*layer, value);
|
||||
if (error_) {
|
||||
error = *error_;
|
||||
return {};
|
||||
}
|
||||
|
||||
return std::move(layer);
|
||||
}
|
||||
|
||||
private:
|
||||
template <class LayerType, class V>
|
||||
optional<std::unique_ptr<Layer>> convertVectorLayer(const std::string& id, const V& value, Error& error) const {
|
||||
auto sourceValue = objectMember(value, "source");
|
||||
if (!sourceValue) {
|
||||
error = { "layer must have a source" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<std::string> source = toString(*sourceValue);
|
||||
if (!source) {
|
||||
error = { "layer source must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<LayerType> layer = std::make_unique<LayerType>(id, *source);
|
||||
|
||||
auto sourceLayerValue = objectMember(value, "source-layer");
|
||||
if (sourceLayerValue) {
|
||||
optional<std::string> sourceLayer = toString(*sourceLayerValue);
|
||||
if (!sourceLayer) {
|
||||
error = { "layer source-layer must be a string" };
|
||||
return {};
|
||||
}
|
||||
layer->setSourceLayer(*sourceLayer);
|
||||
}
|
||||
|
||||
auto filterValue = objectMember(value, "filter");
|
||||
if (filterValue) {
|
||||
optional<Filter> filter = convert<Filter>(*filterValue, error);
|
||||
if (!filter) {
|
||||
return {};
|
||||
}
|
||||
layer->setFilter(*filter);
|
||||
}
|
||||
|
||||
return { std::move(layer) };
|
||||
}
|
||||
|
||||
template <class V>
|
||||
optional<std::unique_ptr<Layer>> convertRasterLayer(const std::string& id, const V& value, Error& error) const {
|
||||
auto sourceValue = objectMember(value, "source");
|
||||
if (!sourceValue) {
|
||||
error = { "layer must have a source" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<std::string> source = toString(*sourceValue);
|
||||
if (!source) {
|
||||
error = { "layer source must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
return { std::make_unique<RasterLayer>(id, *source) };
|
||||
}
|
||||
|
||||
template <class V>
|
||||
optional<std::unique_ptr<Layer>> convertBackgroundLayer(const std::string& id, const V&, Error&) const {
|
||||
return { std::make_unique<BackgroundLayer>(id) };
|
||||
}
|
||||
optional<std::unique_ptr<Layer>> operator()(const Convertible& value, Error& error) const;
|
||||
};
|
||||
|
||||
optional<Error> setLayoutProperty(Layer& layer, const std::string& name, const Convertible& value);
|
||||
optional<Error> setPaintProperty(Layer& layer, const std::string& name, const Convertible& value);
|
||||
optional<Error> setPaintProperties(Layer& layer, const Convertible& value);
|
||||
|
||||
} // namespace conversion
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
#include <mbgl/style/light.hpp>
|
||||
#include <mbgl/style/conversion.hpp>
|
||||
#include <mbgl/style/conversion/position.hpp>
|
||||
#include <mbgl/style/conversion/property_value.hpp>
|
||||
#include <mbgl/style/conversion/transition_options.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
@@ -13,108 +10,7 @@ namespace conversion {
|
||||
template <>
|
||||
struct Converter<Light> {
|
||||
public:
|
||||
template <class V>
|
||||
optional<Light> operator()(const V& value, Error& error) const {
|
||||
if (!isObject(value)) {
|
||||
error = { "light must be an object" };
|
||||
return {};
|
||||
}
|
||||
|
||||
Light light;
|
||||
|
||||
const auto anchor = objectMember(value, "anchor");
|
||||
if (anchor) {
|
||||
optional<PropertyValue<LightAnchorType>> convertedAnchor =
|
||||
convert<PropertyValue<LightAnchorType>>(*anchor, error);
|
||||
|
||||
if (convertedAnchor) {
|
||||
light.setAnchor(*convertedAnchor);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto anchorTransition = objectMember(value, "anchor-transition");
|
||||
if (anchorTransition) {
|
||||
optional<TransitionOptions> transition =
|
||||
convert<TransitionOptions>(*anchorTransition, error);
|
||||
if (transition) {
|
||||
light.setAnchorTransition(*transition);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto color = objectMember(value, "color");
|
||||
if (color) {
|
||||
optional<PropertyValue<Color>> convertedColor =
|
||||
convert<PropertyValue<Color>>(*color, error);
|
||||
|
||||
if (convertedColor) {
|
||||
light.setColor(*convertedColor);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto colorTransition = objectMember(value, "color-transition");
|
||||
if (colorTransition) {
|
||||
optional<TransitionOptions> transition =
|
||||
convert<TransitionOptions>(*colorTransition, error);
|
||||
if (transition) {
|
||||
light.setColorTransition(*transition);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto position = objectMember(value, "position");
|
||||
if (position) {
|
||||
optional<PropertyValue<Position>> convertedPosition =
|
||||
convert<PropertyValue<Position>>(*position, error);
|
||||
|
||||
if (convertedPosition) {
|
||||
light.setPosition(*convertedPosition);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto positionTransition = objectMember(value, "position-transition");
|
||||
if (positionTransition) {
|
||||
optional<TransitionOptions> transition =
|
||||
convert<TransitionOptions>(*positionTransition, error);
|
||||
if (transition) {
|
||||
light.setPositionTransition(*transition);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto intensity = objectMember(value, "intensity");
|
||||
if (intensity) {
|
||||
optional<PropertyValue<float>> convertedIntensity =
|
||||
convert<PropertyValue<float>>(*intensity, error);
|
||||
|
||||
if (convertedIntensity) {
|
||||
light.setIntensity(*convertedIntensity);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto intensityTransition = objectMember(value, "intensity-transition");
|
||||
if (intensityTransition) {
|
||||
optional<TransitionOptions> transition =
|
||||
convert<TransitionOptions>(*intensityTransition, error);
|
||||
if (transition) {
|
||||
light.setIntensityTransition(*transition);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return { std::move(light) };
|
||||
};
|
||||
optional<Light> operator()(const Convertible& value, Error& error) const;
|
||||
};
|
||||
|
||||
} // namespace conversion
|
||||
|
||||
@@ -1,211 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// This file is generated. Edit make_property_setters.hpp.ejs, then run `make style-code`.
|
||||
|
||||
#include <mbgl/style/conversion/property_setter.hpp>
|
||||
|
||||
#include <mbgl/style/layers/fill_layer.hpp>
|
||||
#include <mbgl/style/layers/line_layer.hpp>
|
||||
#include <mbgl/style/layers/symbol_layer.hpp>
|
||||
#include <mbgl/style/layers/circle_layer.hpp>
|
||||
#include <mbgl/style/layers/fill_extrusion_layer.hpp>
|
||||
#include <mbgl/style/layers/raster_layer.hpp>
|
||||
#include <mbgl/style/layers/background_layer.hpp>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
namespace conversion {
|
||||
|
||||
template <class V>
|
||||
auto makeLayoutPropertySetters() {
|
||||
std::unordered_map<std::string, PropertySetter<V>> result;
|
||||
|
||||
result["visibility"] = &setVisibility<V>;
|
||||
|
||||
|
||||
result["line-cap"] = &setProperty<V, LineLayer, PropertyValue<LineCapType>, &LineLayer::setLineCap>;
|
||||
result["line-join"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<LineJoinType>, &LineLayer::setLineJoin>;
|
||||
result["line-miter-limit"] = &setProperty<V, LineLayer, PropertyValue<float>, &LineLayer::setLineMiterLimit>;
|
||||
result["line-round-limit"] = &setProperty<V, LineLayer, PropertyValue<float>, &LineLayer::setLineRoundLimit>;
|
||||
|
||||
result["symbol-placement"] = &setProperty<V, SymbolLayer, PropertyValue<SymbolPlacementType>, &SymbolLayer::setSymbolPlacement>;
|
||||
result["symbol-spacing"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setSymbolSpacing>;
|
||||
result["symbol-avoid-edges"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setSymbolAvoidEdges>;
|
||||
result["icon-allow-overlap"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconAllowOverlap>;
|
||||
result["icon-ignore-placement"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconIgnorePlacement>;
|
||||
result["icon-optional"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconOptional>;
|
||||
result["icon-rotation-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setIconRotationAlignment>;
|
||||
result["icon-size"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconSize>;
|
||||
result["icon-text-fit"] = &setProperty<V, SymbolLayer, PropertyValue<IconTextFitType>, &SymbolLayer::setIconTextFit>;
|
||||
result["icon-text-fit-padding"] = &setProperty<V, SymbolLayer, PropertyValue<std::array<float, 4>>, &SymbolLayer::setIconTextFitPadding>;
|
||||
result["icon-image"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setIconImage>;
|
||||
result["icon-rotate"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconRotate>;
|
||||
result["icon-padding"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setIconPadding>;
|
||||
result["icon-keep-upright"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconKeepUpright>;
|
||||
result["icon-offset"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::array<float, 2>>, &SymbolLayer::setIconOffset>;
|
||||
result["icon-anchor"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<SymbolAnchorType>, &SymbolLayer::setIconAnchor>;
|
||||
result["icon-pitch-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setIconPitchAlignment>;
|
||||
result["text-pitch-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextPitchAlignment>;
|
||||
result["text-rotation-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextRotationAlignment>;
|
||||
result["text-field"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setTextField>;
|
||||
result["text-font"] = &setProperty<V, SymbolLayer, PropertyValue<std::vector<std::string>>, &SymbolLayer::setTextFont>;
|
||||
result["text-size"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextSize>;
|
||||
result["text-max-width"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextMaxWidth>;
|
||||
result["text-line-height"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextLineHeight>;
|
||||
result["text-letter-spacing"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextLetterSpacing>;
|
||||
result["text-justify"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<TextJustifyType>, &SymbolLayer::setTextJustify>;
|
||||
result["text-anchor"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<SymbolAnchorType>, &SymbolLayer::setTextAnchor>;
|
||||
result["text-max-angle"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextMaxAngle>;
|
||||
result["text-rotate"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextRotate>;
|
||||
result["text-padding"] = &setProperty<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextPadding>;
|
||||
result["text-keep-upright"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextKeepUpright>;
|
||||
result["text-transform"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<TextTransformType>, &SymbolLayer::setTextTransform>;
|
||||
result["text-offset"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::array<float, 2>>, &SymbolLayer::setTextOffset>;
|
||||
result["text-allow-overlap"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextAllowOverlap>;
|
||||
result["text-ignore-placement"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextIgnorePlacement>;
|
||||
result["text-optional"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextOptional>;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class V>
|
||||
auto makePaintPropertySetters() {
|
||||
std::unordered_map<std::string, PropertySetter<V>> result;
|
||||
|
||||
result["fill-antialias"] = &setProperty<V, FillLayer, PropertyValue<bool>, &FillLayer::setFillAntialias>;
|
||||
result["fill-antialias-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillAntialiasTransition>;
|
||||
result["fill-opacity"] = &setProperty<V, FillLayer, DataDrivenPropertyValue<float>, &FillLayer::setFillOpacity>;
|
||||
result["fill-opacity-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillOpacityTransition>;
|
||||
result["fill-color"] = &setProperty<V, FillLayer, DataDrivenPropertyValue<Color>, &FillLayer::setFillColor>;
|
||||
result["fill-color-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillColorTransition>;
|
||||
result["fill-outline-color"] = &setProperty<V, FillLayer, DataDrivenPropertyValue<Color>, &FillLayer::setFillOutlineColor>;
|
||||
result["fill-outline-color-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillOutlineColorTransition>;
|
||||
result["fill-translate"] = &setProperty<V, FillLayer, PropertyValue<std::array<float, 2>>, &FillLayer::setFillTranslate>;
|
||||
result["fill-translate-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillTranslateTransition>;
|
||||
result["fill-translate-anchor"] = &setProperty<V, FillLayer, PropertyValue<TranslateAnchorType>, &FillLayer::setFillTranslateAnchor>;
|
||||
result["fill-translate-anchor-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillTranslateAnchorTransition>;
|
||||
result["fill-pattern"] = &setProperty<V, FillLayer, PropertyValue<std::string>, &FillLayer::setFillPattern>;
|
||||
result["fill-pattern-transition"] = &setTransition<V, FillLayer, &FillLayer::setFillPatternTransition>;
|
||||
|
||||
result["line-opacity"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineOpacity>;
|
||||
result["line-opacity-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineOpacityTransition>;
|
||||
result["line-color"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<Color>, &LineLayer::setLineColor>;
|
||||
result["line-color-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineColorTransition>;
|
||||
result["line-translate"] = &setProperty<V, LineLayer, PropertyValue<std::array<float, 2>>, &LineLayer::setLineTranslate>;
|
||||
result["line-translate-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineTranslateTransition>;
|
||||
result["line-translate-anchor"] = &setProperty<V, LineLayer, PropertyValue<TranslateAnchorType>, &LineLayer::setLineTranslateAnchor>;
|
||||
result["line-translate-anchor-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineTranslateAnchorTransition>;
|
||||
result["line-width"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineWidth>;
|
||||
result["line-width-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineWidthTransition>;
|
||||
result["line-gap-width"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineGapWidth>;
|
||||
result["line-gap-width-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineGapWidthTransition>;
|
||||
result["line-offset"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineOffset>;
|
||||
result["line-offset-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineOffsetTransition>;
|
||||
result["line-blur"] = &setProperty<V, LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineBlur>;
|
||||
result["line-blur-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineBlurTransition>;
|
||||
result["line-dasharray"] = &setProperty<V, LineLayer, PropertyValue<std::vector<float>>, &LineLayer::setLineDasharray>;
|
||||
result["line-dasharray-transition"] = &setTransition<V, LineLayer, &LineLayer::setLineDasharrayTransition>;
|
||||
result["line-pattern"] = &setProperty<V, LineLayer, PropertyValue<std::string>, &LineLayer::setLinePattern>;
|
||||
result["line-pattern-transition"] = &setTransition<V, LineLayer, &LineLayer::setLinePatternTransition>;
|
||||
|
||||
result["icon-opacity"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconOpacity>;
|
||||
result["icon-opacity-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconOpacityTransition>;
|
||||
result["icon-color"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setIconColor>;
|
||||
result["icon-color-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconColorTransition>;
|
||||
result["icon-halo-color"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setIconHaloColor>;
|
||||
result["icon-halo-color-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconHaloColorTransition>;
|
||||
result["icon-halo-width"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconHaloWidth>;
|
||||
result["icon-halo-width-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconHaloWidthTransition>;
|
||||
result["icon-halo-blur"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconHaloBlur>;
|
||||
result["icon-halo-blur-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconHaloBlurTransition>;
|
||||
result["icon-translate"] = &setProperty<V, SymbolLayer, PropertyValue<std::array<float, 2>>, &SymbolLayer::setIconTranslate>;
|
||||
result["icon-translate-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconTranslateTransition>;
|
||||
result["icon-translate-anchor"] = &setProperty<V, SymbolLayer, PropertyValue<TranslateAnchorType>, &SymbolLayer::setIconTranslateAnchor>;
|
||||
result["icon-translate-anchor-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setIconTranslateAnchorTransition>;
|
||||
result["text-opacity"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextOpacity>;
|
||||
result["text-opacity-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextOpacityTransition>;
|
||||
result["text-color"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setTextColor>;
|
||||
result["text-color-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextColorTransition>;
|
||||
result["text-halo-color"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setTextHaloColor>;
|
||||
result["text-halo-color-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextHaloColorTransition>;
|
||||
result["text-halo-width"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextHaloWidth>;
|
||||
result["text-halo-width-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextHaloWidthTransition>;
|
||||
result["text-halo-blur"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextHaloBlur>;
|
||||
result["text-halo-blur-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextHaloBlurTransition>;
|
||||
result["text-translate"] = &setProperty<V, SymbolLayer, PropertyValue<std::array<float, 2>>, &SymbolLayer::setTextTranslate>;
|
||||
result["text-translate-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextTranslateTransition>;
|
||||
result["text-translate-anchor"] = &setProperty<V, SymbolLayer, PropertyValue<TranslateAnchorType>, &SymbolLayer::setTextTranslateAnchor>;
|
||||
result["text-translate-anchor-transition"] = &setTransition<V, SymbolLayer, &SymbolLayer::setTextTranslateAnchorTransition>;
|
||||
|
||||
result["circle-radius"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleRadius>;
|
||||
result["circle-radius-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleRadiusTransition>;
|
||||
result["circle-color"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<Color>, &CircleLayer::setCircleColor>;
|
||||
result["circle-color-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleColorTransition>;
|
||||
result["circle-blur"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleBlur>;
|
||||
result["circle-blur-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleBlurTransition>;
|
||||
result["circle-opacity"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleOpacity>;
|
||||
result["circle-opacity-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleOpacityTransition>;
|
||||
result["circle-translate"] = &setProperty<V, CircleLayer, PropertyValue<std::array<float, 2>>, &CircleLayer::setCircleTranslate>;
|
||||
result["circle-translate-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleTranslateTransition>;
|
||||
result["circle-translate-anchor"] = &setProperty<V, CircleLayer, PropertyValue<TranslateAnchorType>, &CircleLayer::setCircleTranslateAnchor>;
|
||||
result["circle-translate-anchor-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleTranslateAnchorTransition>;
|
||||
result["circle-pitch-scale"] = &setProperty<V, CircleLayer, PropertyValue<CirclePitchScaleType>, &CircleLayer::setCirclePitchScale>;
|
||||
result["circle-pitch-scale-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCirclePitchScaleTransition>;
|
||||
result["circle-pitch-alignment"] = &setProperty<V, CircleLayer, PropertyValue<AlignmentType>, &CircleLayer::setCirclePitchAlignment>;
|
||||
result["circle-pitch-alignment-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCirclePitchAlignmentTransition>;
|
||||
result["circle-stroke-width"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleStrokeWidth>;
|
||||
result["circle-stroke-width-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleStrokeWidthTransition>;
|
||||
result["circle-stroke-color"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<Color>, &CircleLayer::setCircleStrokeColor>;
|
||||
result["circle-stroke-color-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleStrokeColorTransition>;
|
||||
result["circle-stroke-opacity"] = &setProperty<V, CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleStrokeOpacity>;
|
||||
result["circle-stroke-opacity-transition"] = &setTransition<V, CircleLayer, &CircleLayer::setCircleStrokeOpacityTransition>;
|
||||
|
||||
result["fill-extrusion-opacity"] = &setProperty<V, FillExtrusionLayer, PropertyValue<float>, &FillExtrusionLayer::setFillExtrusionOpacity>;
|
||||
result["fill-extrusion-opacity-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionOpacityTransition>;
|
||||
result["fill-extrusion-color"] = &setProperty<V, FillExtrusionLayer, DataDrivenPropertyValue<Color>, &FillExtrusionLayer::setFillExtrusionColor>;
|
||||
result["fill-extrusion-color-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionColorTransition>;
|
||||
result["fill-extrusion-translate"] = &setProperty<V, FillExtrusionLayer, PropertyValue<std::array<float, 2>>, &FillExtrusionLayer::setFillExtrusionTranslate>;
|
||||
result["fill-extrusion-translate-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionTranslateTransition>;
|
||||
result["fill-extrusion-translate-anchor"] = &setProperty<V, FillExtrusionLayer, PropertyValue<TranslateAnchorType>, &FillExtrusionLayer::setFillExtrusionTranslateAnchor>;
|
||||
result["fill-extrusion-translate-anchor-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionTranslateAnchorTransition>;
|
||||
result["fill-extrusion-pattern"] = &setProperty<V, FillExtrusionLayer, PropertyValue<std::string>, &FillExtrusionLayer::setFillExtrusionPattern>;
|
||||
result["fill-extrusion-pattern-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionPatternTransition>;
|
||||
result["fill-extrusion-height"] = &setProperty<V, FillExtrusionLayer, DataDrivenPropertyValue<float>, &FillExtrusionLayer::setFillExtrusionHeight>;
|
||||
result["fill-extrusion-height-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionHeightTransition>;
|
||||
result["fill-extrusion-base"] = &setProperty<V, FillExtrusionLayer, DataDrivenPropertyValue<float>, &FillExtrusionLayer::setFillExtrusionBase>;
|
||||
result["fill-extrusion-base-transition"] = &setTransition<V, FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionBaseTransition>;
|
||||
|
||||
result["raster-opacity"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterOpacity>;
|
||||
result["raster-opacity-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterOpacityTransition>;
|
||||
result["raster-hue-rotate"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterHueRotate>;
|
||||
result["raster-hue-rotate-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterHueRotateTransition>;
|
||||
result["raster-brightness-min"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterBrightnessMin>;
|
||||
result["raster-brightness-min-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterBrightnessMinTransition>;
|
||||
result["raster-brightness-max"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterBrightnessMax>;
|
||||
result["raster-brightness-max-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterBrightnessMaxTransition>;
|
||||
result["raster-saturation"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterSaturation>;
|
||||
result["raster-saturation-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterSaturationTransition>;
|
||||
result["raster-contrast"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterContrast>;
|
||||
result["raster-contrast-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterContrastTransition>;
|
||||
result["raster-fade-duration"] = &setProperty<V, RasterLayer, PropertyValue<float>, &RasterLayer::setRasterFadeDuration>;
|
||||
result["raster-fade-duration-transition"] = &setTransition<V, RasterLayer, &RasterLayer::setRasterFadeDurationTransition>;
|
||||
|
||||
result["background-color"] = &setProperty<V, BackgroundLayer, PropertyValue<Color>, &BackgroundLayer::setBackgroundColor>;
|
||||
result["background-color-transition"] = &setTransition<V, BackgroundLayer, &BackgroundLayer::setBackgroundColorTransition>;
|
||||
result["background-pattern"] = &setProperty<V, BackgroundLayer, PropertyValue<std::string>, &BackgroundLayer::setBackgroundPattern>;
|
||||
result["background-pattern-transition"] = &setTransition<V, BackgroundLayer, &BackgroundLayer::setBackgroundPatternTransition>;
|
||||
result["background-opacity"] = &setProperty<V, BackgroundLayer, PropertyValue<float>, &BackgroundLayer::setBackgroundOpacity>;
|
||||
result["background-opacity-transition"] = &setTransition<V, BackgroundLayer, &BackgroundLayer::setBackgroundOpacityTransition>;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace conversion
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
@@ -1,48 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// This file is generated. Edit make_property_setters.hpp.ejs, then run `make style-code`.
|
||||
|
||||
#include <mbgl/style/conversion/property_setter.hpp>
|
||||
|
||||
<% for (const layer of locals.layers) { -%>
|
||||
#include <mbgl/style/layers/<%- layer.type.replace('-', '_') %>_layer.hpp>
|
||||
<% } -%>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
namespace conversion {
|
||||
|
||||
template <class V>
|
||||
auto makeLayoutPropertySetters() {
|
||||
std::unordered_map<std::string, PropertySetter<V>> result;
|
||||
|
||||
result["visibility"] = &setVisibility<V>;
|
||||
|
||||
<% for (const layer of locals.layers) { -%>
|
||||
<% for (const property of layer.layoutProperties) { -%>
|
||||
result["<%- property.name %>"] = &setProperty<V, <%- camelize(layer.type) %>Layer, <%- propertyValueType(property) %>, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>>;
|
||||
<% } -%>
|
||||
|
||||
<% } -%>
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class V>
|
||||
auto makePaintPropertySetters() {
|
||||
std::unordered_map<std::string, PropertySetter<V>> result;
|
||||
|
||||
<% for (const layer of locals.layers) { -%>
|
||||
<% for (const property of layer.paintProperties) { -%>
|
||||
result["<%- property.name %>"] = &setProperty<V, <%- camelize(layer.type) %>Layer, <%- propertyValueType(property) %>, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>>;
|
||||
result["<%- property.name %>-transition"] = &setTransition<V, <%- camelize(layer.type) %>Layer, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>Transition>;
|
||||
<% } -%>
|
||||
|
||||
<% } -%>
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace conversion
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
#include <mbgl/style/conversion.hpp>
|
||||
#include <mbgl/style/position.hpp>
|
||||
#include <mbgl/util/optional.hpp>
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
@@ -12,16 +9,7 @@ namespace conversion {
|
||||
|
||||
template <>
|
||||
struct Converter<Position> {
|
||||
template <class V>
|
||||
optional<Position> operator()(const V& value, Error& error) const {
|
||||
optional<std::array<float, 3>> spherical = convert<std::array<float, 3>>(value, error);
|
||||
|
||||
if (!spherical) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return Position(*spherical);
|
||||
}
|
||||
optional<Position> operator()(const Convertible& value, Error& error) const;
|
||||
};
|
||||
|
||||
} // namespace conversion
|
||||
|
||||
@@ -11,8 +11,7 @@ namespace conversion {
|
||||
|
||||
template <class T>
|
||||
struct Converter<PropertyValue<T>> {
|
||||
template <class V>
|
||||
optional<PropertyValue<T>> operator()(const V& value, Error& error) const {
|
||||
optional<PropertyValue<T>> operator()(const Convertible& value, Error& error) const {
|
||||
if (isUndefined(value)) {
|
||||
return PropertyValue<T>();
|
||||
} else if (isObject(value)) {
|
||||
|
||||
@@ -1,16 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/style/conversion.hpp>
|
||||
#include <mbgl/style/conversion/coordinate.hpp>
|
||||
#include <mbgl/style/conversion/geojson.hpp>
|
||||
#include <mbgl/style/conversion/geojson_options.hpp>
|
||||
#include <mbgl/style/conversion/tileset.hpp>
|
||||
#include <mbgl/style/source.hpp>
|
||||
#include <mbgl/style/sources/geojson_source.hpp>
|
||||
#include <mbgl/style/sources/raster_source.hpp>
|
||||
#include <mbgl/style/sources/vector_source.hpp>
|
||||
#include <mbgl/style/sources/image_source.hpp>
|
||||
#include <mbgl/util/geo.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
@@ -19,170 +12,7 @@ namespace conversion {
|
||||
template <>
|
||||
struct Converter<std::unique_ptr<Source>> {
|
||||
public:
|
||||
|
||||
template <class V>
|
||||
optional<std::unique_ptr<Source>> operator()(const V& value, Error& error, const std::string& id) const {
|
||||
if (!isObject(value)) {
|
||||
error = { "source must be an object" };
|
||||
return {};
|
||||
}
|
||||
|
||||
auto typeValue = objectMember(value, "type");
|
||||
if (!typeValue) {
|
||||
error = { "source must have a type" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<std::string> type = toString(*typeValue);
|
||||
if (!type) {
|
||||
error = { "source type must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
if (*type == "raster") {
|
||||
return convertRasterSource(id, value, error);
|
||||
} else if (*type == "vector") {
|
||||
return convertVectorSource(id, value, error);
|
||||
} else if (*type == "geojson") {
|
||||
return convertGeoJSONSource(id, value, error);
|
||||
} else if (*type == "image") {
|
||||
return convertImageSource(id, value, error);
|
||||
} else {
|
||||
error = { "invalid source type" };
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// A tile source can either specify a URL to TileJSON, or inline TileJSON.
|
||||
template <class V>
|
||||
optional<variant<std::string, Tileset>> convertURLOrTileset(const V& value, Error& error) const {
|
||||
auto urlVal = objectMember(value, "url");
|
||||
if (!urlVal) {
|
||||
optional<Tileset> tileset = convert<Tileset>(value, error);
|
||||
if (!tileset) {
|
||||
return {};
|
||||
}
|
||||
return { *tileset };
|
||||
}
|
||||
|
||||
optional<std::string> url = toString(*urlVal);
|
||||
if (!url) {
|
||||
error = { "source url must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
return { *url };
|
||||
}
|
||||
|
||||
template <class V>
|
||||
optional<std::unique_ptr<Source>> convertRasterSource(const std::string& id,
|
||||
const V& value,
|
||||
Error& error) const {
|
||||
optional<variant<std::string, Tileset>> urlOrTileset = convertURLOrTileset(value, error);
|
||||
if (!urlOrTileset) {
|
||||
return {};
|
||||
}
|
||||
|
||||
uint16_t tileSize = util::tileSize;
|
||||
auto tileSizeValue = objectMember(value, "tileSize");
|
||||
if (tileSizeValue) {
|
||||
optional<float> size = toNumber(*tileSizeValue);
|
||||
if (!size || *size < 0 || *size > std::numeric_limits<uint16_t>::max()) {
|
||||
error = { "invalid tileSize" };
|
||||
return {};
|
||||
}
|
||||
tileSize = *size;
|
||||
}
|
||||
|
||||
return { std::make_unique<RasterSource>(id, std::move(*urlOrTileset), tileSize) };
|
||||
}
|
||||
|
||||
template <class V>
|
||||
optional<std::unique_ptr<Source>> convertVectorSource(const std::string& id,
|
||||
const V& value,
|
||||
Error& error) const {
|
||||
optional<variant<std::string, Tileset>> urlOrTileset = convertURLOrTileset(value, error);
|
||||
if (!urlOrTileset) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return { std::make_unique<VectorSource>(id, std::move(*urlOrTileset)) };
|
||||
}
|
||||
|
||||
template <class V>
|
||||
optional<std::unique_ptr<Source>> convertGeoJSONSource(const std::string& id,
|
||||
const V& value,
|
||||
Error& error) const {
|
||||
auto dataValue = objectMember(value, "data");
|
||||
if (!dataValue) {
|
||||
error = { "GeoJSON source must have a data value" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<GeoJSONOptions> options = convert<GeoJSONOptions>(value, error);
|
||||
if (!options) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto result = std::make_unique<GeoJSONSource>(id, *options);
|
||||
|
||||
if (isObject(*dataValue)) {
|
||||
optional<GeoJSON> geoJSON = convert<GeoJSON>(*dataValue, error);
|
||||
if (!geoJSON) {
|
||||
return {};
|
||||
}
|
||||
result->setGeoJSON(std::move(*geoJSON));
|
||||
} else if (toString(*dataValue)) {
|
||||
result->setURL(*toString(*dataValue));
|
||||
} else {
|
||||
error = { "GeoJSON data must be a URL or an object" };
|
||||
return {};
|
||||
}
|
||||
|
||||
return { std::move(result) };
|
||||
}
|
||||
|
||||
template <class V>
|
||||
optional<std::unique_ptr<Source>> convertImageSource(const std::string& id,
|
||||
const V& value,
|
||||
Error& error) const {
|
||||
auto urlValue = objectMember(value, "url");
|
||||
if (!urlValue) {
|
||||
error = { "Image source must have a url value" };
|
||||
return {};
|
||||
}
|
||||
|
||||
auto urlString = toString(*urlValue);
|
||||
if (!urlString) {
|
||||
error = { "Image url must be a URL string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
auto coordinatesValue = objectMember(value, "coordinates");
|
||||
if (!coordinatesValue) {
|
||||
error = { "Image source must have a coordinates values" };
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!isArray(*coordinatesValue) || arrayLength(*coordinatesValue) != 4) {
|
||||
error = { "Image coordinates must be an array of four longitude latitude pairs" };
|
||||
return {};
|
||||
}
|
||||
|
||||
std::array<LatLng, 4> coordinates;
|
||||
for (std::size_t i=0; i < 4; i++) {
|
||||
auto latLng = conversion::convert<LatLng>(arrayMember(*coordinatesValue,i), error);
|
||||
if (!latLng) {
|
||||
return {};
|
||||
}
|
||||
coordinates[i] = *latLng;
|
||||
}
|
||||
auto result = std::make_unique<ImageSource>(id, coordinates);
|
||||
result->setURL(*urlString);
|
||||
|
||||
return { std::move(result) };
|
||||
}
|
||||
optional<std::unique_ptr<Source>> operator()(const Convertible& value, Error& error, const std::string& id) const;
|
||||
};
|
||||
|
||||
} // namespace conversion
|
||||
|
||||
@@ -10,70 +10,7 @@ namespace conversion {
|
||||
template <>
|
||||
struct Converter<Tileset> {
|
||||
public:
|
||||
template <class V>
|
||||
optional<Tileset> operator()(const V& value, Error& error) const {
|
||||
Tileset result;
|
||||
|
||||
auto tiles = objectMember(value, "tiles");
|
||||
if (!tiles) {
|
||||
error = { "source must have tiles" };
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!isArray(*tiles)) {
|
||||
error = { "source tiles must be an array" };
|
||||
return {};
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < arrayLength(*tiles); i++) {
|
||||
optional<std::string> urlTemplate = toString(arrayMember(*tiles, i));
|
||||
if (!urlTemplate) {
|
||||
error = { "source tiles member must be a string" };
|
||||
return {};
|
||||
}
|
||||
result.tiles.push_back(std::move(*urlTemplate));
|
||||
}
|
||||
|
||||
auto schemeValue = objectMember(value, "scheme");
|
||||
if (schemeValue) {
|
||||
optional<std::string> scheme = toString(*schemeValue);
|
||||
if (scheme && *scheme == "tms") {
|
||||
result.scheme = Tileset::Scheme::TMS;
|
||||
}
|
||||
}
|
||||
|
||||
auto minzoomValue = objectMember(value, "minzoom");
|
||||
if (minzoomValue) {
|
||||
optional<float> minzoom = toNumber(*minzoomValue);
|
||||
if (!minzoom || *minzoom < 0 || *minzoom > std::numeric_limits<uint8_t>::max()) {
|
||||
error = { "invalid minzoom" };
|
||||
return {};
|
||||
}
|
||||
result.zoomRange.min = *minzoom;
|
||||
}
|
||||
|
||||
auto maxzoomValue = objectMember(value, "maxzoom");
|
||||
if (maxzoomValue) {
|
||||
optional<float> maxzoom = toNumber(*maxzoomValue);
|
||||
if (!maxzoom || *maxzoom < 0 || *maxzoom > std::numeric_limits<uint8_t>::max()) {
|
||||
error = { "invalid maxzoom" };
|
||||
return {};
|
||||
}
|
||||
result.zoomRange.max = *maxzoom;
|
||||
}
|
||||
|
||||
auto attributionValue = objectMember(value, "attribution");
|
||||
if (attributionValue) {
|
||||
optional<std::string> attribution = toString(*attributionValue);
|
||||
if (!attribution) {
|
||||
error = { "source attribution must be a string" };
|
||||
return {};
|
||||
}
|
||||
result.attribution = std::move(*attribution);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
optional<Tileset> operator()(const Convertible& value, Error& error) const;
|
||||
};
|
||||
|
||||
} // namespace conversion
|
||||
|
||||
@@ -10,37 +10,7 @@ namespace conversion {
|
||||
template <>
|
||||
struct Converter<TransitionOptions> {
|
||||
public:
|
||||
template <class V>
|
||||
optional<TransitionOptions> operator()(const V& value, Error& error) const {
|
||||
if (!isObject(value)) {
|
||||
error = { "transition must be an object" };
|
||||
return {};
|
||||
}
|
||||
|
||||
TransitionOptions result;
|
||||
|
||||
auto duration = objectMember(value, "duration");
|
||||
if (duration) {
|
||||
auto number = toNumber(*duration);
|
||||
if (!number) {
|
||||
error = { "duration must be a number" };
|
||||
return {};
|
||||
}
|
||||
result.duration = { std::chrono::milliseconds(int64_t(*number)) };
|
||||
}
|
||||
|
||||
auto delay = objectMember(value, "delay");
|
||||
if (delay) {
|
||||
auto number = toNumber(*delay);
|
||||
if (!number) {
|
||||
error = { "delay must be a number" };
|
||||
return {};
|
||||
}
|
||||
result.delay = { std::chrono::milliseconds(int64_t(*number)) };
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
optional<TransitionOptions> operator()(const Convertible& value, Error& error) const;
|
||||
};
|
||||
|
||||
} // namespace conversion
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <mbgl/util/range.hpp>
|
||||
#include <mbgl/util/constants.hpp>
|
||||
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {};
|
||||
}
|
||||
|
||||
inline optional<bool> toBool(const mbgl::android::Value& value) {
|
||||
if (value.isBool()) {
|
||||
return value.toBool();
|
||||
} else {
|
||||
return {};
|
||||
static optional<bool> toBool(const mbgl::android::Value& value) {
|
||||
if (value.isBool()) {
|
||||
return value.toBool();
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline optional<float> toNumber(const mbgl::android::Value& value) {
|
||||
if (value.isNumber()) {
|
||||
auto num = value.toFloat();
|
||||
return num;
|
||||
} else {
|
||||
return {};
|
||||
static optional<float> toNumber(const mbgl::android::Value& value) {
|
||||
if (value.isNumber()) {
|
||||
auto num = value.toFloat();
|
||||
return num;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline optional<double> toDouble(const mbgl::android::Value& value) {
|
||||
if (value.isNumber()) {
|
||||
return value.toDouble();
|
||||
} else {
|
||||
return {};
|
||||
static optional<double> toDouble(const mbgl::android::Value& value) {
|
||||
if (value.isNumber()) {
|
||||
return value.toDouble();
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline optional<std::string> toString(const mbgl::android::Value& value) {
|
||||
if (value.isString()) {
|
||||
return value.toString();
|
||||
} else {
|
||||
return {};
|
||||
static optional<std::string> toString(const mbgl::android::Value& value) {
|
||||
if (value.isString()) {
|
||||
return value.toString();
|
||||
} 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<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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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) };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
NSObject * _Nullable operator*() {
|
||||
NSCAssert(this, @"Expected non-null value.");
|
||||
return value;
|
||||
}
|
||||
private:
|
||||
NSObject * _Nullable value;
|
||||
Holder(const id v) : value(v) {}
|
||||
const id value;
|
||||
};
|
||||
|
||||
inline bool isUndefined(const id value) {
|
||||
return !value || value == [NSNull null];
|
||||
}
|
||||
|
||||
inline bool isArray(const id value) {
|
||||
return [value isKindOfClass:[NSArray class]];
|
||||
}
|
||||
|
||||
inline bool isObject(const id value) {
|
||||
return [value isKindOfClass:[NSDictionary class]];
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
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 };
|
||||
template <>
|
||||
class ConversionTraits<Holder> {
|
||||
public:
|
||||
static bool isUndefined(const Holder& holder) {
|
||||
const id value = holder.value;
|
||||
return !value || value == [NSNull null];
|
||||
}
|
||||
}
|
||||
|
||||
// Not implemented (unneeded for MGLStyleFunction conversion):
|
||||
// optional<Error> eachMember(const NSObject*, Fn&&)
|
||||
static bool isArray(const Holder& holder) {
|
||||
const id value = holder.value;
|
||||
return [value isKindOfClass:[NSArray class]];
|
||||
}
|
||||
|
||||
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 bool isObject(const Holder& holder) {
|
||||
const id value = holder.value;
|
||||
return [value isKindOfClass:[NSDictionary class]];
|
||||
}
|
||||
|
||||
inline optional<bool> toBool(const id value) {
|
||||
if (_isBool(value)) {
|
||||
return ((NSNumber *)value).boolValue;
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
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]};
|
||||
}
|
||||
|
||||
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 {};
|
||||
}
|
||||
}
|
||||
|
||||
// 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 optional<float> toNumber(const id value) {
|
||||
if (_isNumber(value)) {
|
||||
return ((NSNumber *)value).floatValue;
|
||||
} else {
|
||||
static optional<bool> toBool(const Holder& holder) {
|
||||
const id value = holder.value;
|
||||
if (_isBool(value)) {
|
||||
return ((NSNumber *)value).boolValue;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
static optional<float> toNumber(const Holder& holder) {
|
||||
const id value = holder.value;
|
||||
if (_isNumber(value)) {
|
||||
return ((NSNumber *)value).floatValue;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
static optional<double> toDouble(const Holder& holder) {
|
||||
const id value = holder.value;
|
||||
if (_isNumber(value)) {
|
||||
return ((NSNumber *)value).doubleValue;
|
||||
} 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 {};
|
||||
}
|
||||
}
|
||||
|
||||
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 {};
|
||||
}
|
||||
}
|
||||
|
||||
static optional<GeoJSON> toGeoJSON(const Holder& holder, Error& error) {
|
||||
error = { "toGeoJSON not implemented" };
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
inline optional<double> toDouble(const id value) {
|
||||
if (_isNumber(value)) {
|
||||
return ((NSNumber *)value).doubleValue;
|
||||
} 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<std::string> toString(const id value) {
|
||||
if (_isString(value)) {
|
||||
return std::string(static_cast<const char *>([value UTF8String]));
|
||||
} else {
|
||||
return {};
|
||||
static bool _isNumber(const id value) {
|
||||
return [value isKindOfClass:[NSNumber class]] && !_isBool(value);
|
||||
}
|
||||
}
|
||||
|
||||
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 _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
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()) {
|
||||
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())};
|
||||
}
|
||||
|
||||
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 {};
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
static optional<float> toNumber(const v8::Local<v8::Value>& value) {
|
||||
Nan::HandleScope scope;
|
||||
if (!value->IsNumber()) {
|
||||
return {};
|
||||
}
|
||||
return value->NumberValue();
|
||||
}
|
||||
|
||||
static optional<double> toDouble(const v8::Local<v8::Value>& value) {
|
||||
Nan::HandleScope scope;
|
||||
if (!value->IsNumber()) {
|
||||
return {};
|
||||
}
|
||||
return value->NumberValue();
|
||||
}
|
||||
|
||||
static optional<std::string> toString(const v8::Local<v8::Value>& value) {
|
||||
Nan::HandleScope scope;
|
||||
if (!value->IsString()) {
|
||||
return {};
|
||||
}
|
||||
return std::string(*Nan::Utf8String(value));
|
||||
}
|
||||
|
||||
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 {};
|
||||
}
|
||||
|
||||
inline optional<bool> toBool(v8::Local<v8::Value> value) {
|
||||
Nan::HandleScope scope;
|
||||
if (!value->IsBoolean()) {
|
||||
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 {};
|
||||
}
|
||||
}
|
||||
return value->BooleanValue();
|
||||
}
|
||||
};
|
||||
|
||||
inline optional<float> toNumber(v8::Local<v8::Value> value) {
|
||||
Nan::HandleScope scope;
|
||||
if (!value->IsNumber()) {
|
||||
return {};
|
||||
}
|
||||
return value->NumberValue();
|
||||
}
|
||||
|
||||
inline optional<double> toDouble(v8::Local<v8::Value> value) {
|
||||
Nan::HandleScope scope;
|
||||
if (!value->IsNumber()) {
|
||||
return {};
|
||||
}
|
||||
return value->NumberValue();
|
||||
}
|
||||
|
||||
inline optional<std::string> toString(v8::Local<v8::Value> value) {
|
||||
Nan::HandleScope scope;
|
||||
if (!value->IsString()) {
|
||||
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 {};
|
||||
}
|
||||
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
|
||||
|
||||
@@ -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
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
inline bool isArray(const QVariant& value) {
|
||||
return value.canConvert(QVariant::List);
|
||||
}
|
||||
|
||||
inline 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];
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
inline 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 {};
|
||||
template <>
|
||||
class ConversionTraits<QVariant> {
|
||||
public:
|
||||
static bool isUndefined(const QVariant& value) {
|
||||
return value.isNull() || !value.isValid();
|
||||
}
|
||||
}
|
||||
|
||||
using EachMemberFn = std::function<optional<Error>(const std::string&, const QVariant&)>;
|
||||
static bool isArray(const QVariant& value) {
|
||||
return value.canConvert(QVariant::List);
|
||||
}
|
||||
|
||||
optional<Error> eachMember(const QVariant& value, EachMemberFn&& fn) {
|
||||
auto map = value.toMap();
|
||||
auto iter = map.constBegin();
|
||||
static std::size_t arrayLength(const QVariant& value) {
|
||||
return value.toList().size();
|
||||
}
|
||||
|
||||
while (iter != map.constEnd()) {
|
||||
optional<Error> result = fn(iter.key().toStdString(), iter.value());
|
||||
if (result) {
|
||||
return result;
|
||||
static QVariant arrayMember(const QVariant& value, std::size_t i) {
|
||||
return value.toList()[i];
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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 {};
|
||||
}
|
||||
}
|
||||
|
||||
template <class Fn>
|
||||
static optional<Error> eachMember(const QVariant& value, Fn&& 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;
|
||||
}
|
||||
|
||||
++iter;
|
||||
}
|
||||
|
||||
++iter;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
inline optional<bool> toBool(const QVariant& value) {
|
||||
if (value.type() == QVariant::Bool) {
|
||||
return value.toBool();
|
||||
} 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<bool> toBool(const QVariant& value) {
|
||||
if (value.type() == QVariant::Bool) {
|
||||
return value.toBool();
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
inline optional<double> toDouble(const QVariant& value) {
|
||||
if (value.type() == QVariant::Int || value.type() == QVariant::Double) {
|
||||
return value.toDouble();
|
||||
} 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<float> toNumber(const QVariant& value) {
|
||||
if (value.type() == QVariant::Int || value.type() == QVariant::Double) {
|
||||
return value.toFloat();
|
||||
} 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<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 {};
|
||||
}
|
||||
}
|
||||
|
||||
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 {};
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
166
platform/qt/src/qt_geojson.cpp
Normal file
166
platform/qt/src/qt_geojson.cpp
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -186,8 +186,8 @@ for (const layer of layers) {
|
||||
writeIfModified(`src/mbgl/style/layers/${layerFileName}_layer_properties.cpp`, propertiesCpp(layer));
|
||||
}
|
||||
|
||||
const propertySettersHpp = ejs.compile(fs.readFileSync('include/mbgl/style/conversion/make_property_setters.hpp.ejs', 'utf8'), {strict: true});
|
||||
writeIfModified('include/mbgl/style/conversion/make_property_setters.hpp', propertySettersHpp({layers: layers}));
|
||||
const propertySettersHpp = ejs.compile(fs.readFileSync('src/mbgl/style/conversion/make_property_setters.hpp.ejs', 'utf8'), {strict: true});
|
||||
writeIfModified('src/mbgl/style/conversion/make_property_setters.hpp', propertySettersHpp({layers: layers}));
|
||||
|
||||
// Light
|
||||
const lightProperties = Object.keys(spec[`light`]).reduce((memo, name) => {
|
||||
|
||||
94
src/mbgl/style/conversion/constant.cpp
Normal file
94
src/mbgl/style/conversion/constant.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
#include <mbgl/style/conversion/constant.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
namespace conversion {
|
||||
|
||||
optional<bool> Converter<bool>::operator()(const Convertible& value, Error& error) const {
|
||||
optional<bool> converted = toBool(value);
|
||||
if (!converted) {
|
||||
error = { "value must be a boolean" };
|
||||
return {};
|
||||
}
|
||||
return *converted;
|
||||
}
|
||||
|
||||
optional<float> Converter<float>::operator()(const Convertible& value, Error& error) const {
|
||||
optional<float> converted = toNumber(value);
|
||||
if (!converted) {
|
||||
error = { "value must be a number" };
|
||||
return {};
|
||||
}
|
||||
return *converted;
|
||||
}
|
||||
|
||||
optional<std::string> Converter<std::string>::operator()(const Convertible& value, Error& error) const {
|
||||
optional<std::string> converted = toString(value);
|
||||
if (!converted) {
|
||||
error = { "value must be a string" };
|
||||
return {};
|
||||
}
|
||||
return *converted;
|
||||
}
|
||||
|
||||
optional<Color> Converter<Color>::operator()(const Convertible& value, Error& error) const {
|
||||
optional<std::string> string = toString(value);
|
||||
if (!string) {
|
||||
error = { "value must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<Color> color = Color::parse(*string);
|
||||
if (!color) {
|
||||
error = { "value must be a valid color" };
|
||||
return {};
|
||||
}
|
||||
|
||||
return *color;
|
||||
}
|
||||
|
||||
optional<std::vector<float>> Converter<std::vector<float>>::operator()(const Convertible& value, Error& error) const {
|
||||
if (!isArray(value)) {
|
||||
error = { "value must be an array" };
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<float> result;
|
||||
result.reserve(arrayLength(value));
|
||||
|
||||
for (std::size_t i = 0; i < arrayLength(value); ++i) {
|
||||
optional<float> number = toNumber(arrayMember(value, i));
|
||||
if (!number) {
|
||||
error = { "value must be an array of numbers" };
|
||||
return {};
|
||||
}
|
||||
result.push_back(*number);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
optional<std::vector<std::string>> Converter<std::vector<std::string>>::operator()(const Convertible& value, Error& error) const {
|
||||
if (!isArray(value)) {
|
||||
error = { "value must be an array" };
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<std::string> result;
|
||||
result.reserve(arrayLength(value));
|
||||
|
||||
for (std::size_t i = 0; i < arrayLength(value); ++i) {
|
||||
optional<std::string> string = toString(arrayMember(value, i));
|
||||
if (!string) {
|
||||
error = { "value must be an array of strings" };
|
||||
return {};
|
||||
}
|
||||
result.push_back(*string);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace conversion
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
29
src/mbgl/style/conversion/coordinate.cpp
Normal file
29
src/mbgl/style/conversion/coordinate.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#include <mbgl/style/conversion/coordinate.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
namespace conversion {
|
||||
|
||||
optional<LatLng> Converter<LatLng>::operator() (const Convertible& value, Error& error) const {
|
||||
if (!isArray(value) || arrayLength(value) < 2 ) {
|
||||
error = { "coordinate array must contain numeric longitude and latitude values" };
|
||||
return {};
|
||||
}
|
||||
//Style spec uses GeoJSON convention for specifying coordinates
|
||||
optional<double> latitude = toDouble(arrayMember(value, 1));
|
||||
optional<double> longitude = toDouble(arrayMember(value, 0));
|
||||
|
||||
if (!latitude || !longitude) {
|
||||
error = { "coordinate array must contain numeric longitude and latitude values" };
|
||||
return {};
|
||||
}
|
||||
if (*latitude < -90 || *latitude > 90 ){
|
||||
error = { "coordinate latitude must be between -90 and 90" };
|
||||
return {};
|
||||
}
|
||||
return LatLng(*latitude, *longitude);
|
||||
}
|
||||
|
||||
} // namespace conversion
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
248
src/mbgl/style/conversion/filter.cpp
Normal file
248
src/mbgl/style/conversion/filter.cpp
Normal file
@@ -0,0 +1,248 @@
|
||||
#include <mbgl/style/conversion/filter.hpp>
|
||||
#include <mbgl/util/geometry.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
namespace conversion {
|
||||
|
||||
static optional<Value> normalizeValue(const optional<Value>& value, Error& error) {
|
||||
if (!value) {
|
||||
error = { "filter expression value must be a boolean, number, or string" };
|
||||
return {};
|
||||
} else {
|
||||
return *value;
|
||||
}
|
||||
}
|
||||
|
||||
static optional<FeatureType> toFeatureType(const Convertible& value, Error& error) {
|
||||
optional<std::string> type = toString(value);
|
||||
if (!type) {
|
||||
error = { "value for $type filter must be a string" };
|
||||
return {};
|
||||
} else if (*type == "Point") {
|
||||
return FeatureType::Point;
|
||||
} else if (*type == "LineString") {
|
||||
return FeatureType::LineString;
|
||||
} else if (*type == "Polygon") {
|
||||
return FeatureType::Polygon;
|
||||
} else {
|
||||
error = { "value for $type filter must be Point, LineString, or Polygon" };
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
static optional<FeatureIdentifier> toFeatureIdentifier(const Convertible& value, Error& error) {
|
||||
optional<Value> identifier = toValue(value);
|
||||
if (!identifier) {
|
||||
error = { "filter expression value must be a boolean, number, or string" };
|
||||
return {};
|
||||
} else {
|
||||
return (*identifier).match(
|
||||
[] (uint64_t t) -> optional<FeatureIdentifier> { return { t }; },
|
||||
[] ( int64_t t) -> optional<FeatureIdentifier> { return { t }; },
|
||||
[] ( double t) -> optional<FeatureIdentifier> { return { t }; },
|
||||
[] (const std::string& t) -> optional<FeatureIdentifier> { return { t }; },
|
||||
[&] (const auto&) -> optional<FeatureIdentifier> {
|
||||
error = { "filter expression value must be a boolean, number, or string" };
|
||||
return {};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
template <class FilterType, class IdentifierFilterType>
|
||||
optional<Filter> convertUnaryFilter(const Convertible& value, Error& error) {
|
||||
if (arrayLength(value) < 2) {
|
||||
error = { "filter expression must have 2 elements" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<std::string> key = toString(arrayMember(value, 1));
|
||||
if (!key) {
|
||||
error = { "filter expression key must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
if (*key == "$id") {
|
||||
return { IdentifierFilterType {} };
|
||||
} else {
|
||||
return { FilterType { *key } };
|
||||
}
|
||||
}
|
||||
|
||||
template <class FilterType, class TypeFilterType, class IdentifierFilterType>
|
||||
optional<Filter> convertEqualityFilter(const Convertible& value, Error& error) {
|
||||
if (arrayLength(value) < 3) {
|
||||
error = { "filter expression must have 3 elements" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<std::string> key = toString(arrayMember(value, 1));
|
||||
if (!key) {
|
||||
error = { "filter expression key must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
if (*key == "$type") {
|
||||
optional<FeatureType> filterValue = toFeatureType(arrayMember(value, 2), error);
|
||||
if (!filterValue) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return { TypeFilterType { *filterValue } };
|
||||
|
||||
} else if (*key == "$id") {
|
||||
optional<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, 2), error);
|
||||
if (!filterValue) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return { IdentifierFilterType { *filterValue } };
|
||||
|
||||
} else {
|
||||
optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error);
|
||||
if (!filterValue) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return { FilterType { *key, *filterValue } };
|
||||
}
|
||||
}
|
||||
|
||||
template <class FilterType>
|
||||
optional<Filter> convertBinaryFilter(const Convertible& value, Error& error) {
|
||||
if (arrayLength(value) < 3) {
|
||||
error = { "filter expression must have 3 elements" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<std::string> key = toString(arrayMember(value, 1));
|
||||
if (!key) {
|
||||
error = { "filter expression key must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error);
|
||||
if (!filterValue) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return { FilterType { *key, *filterValue } };
|
||||
}
|
||||
|
||||
template <class FilterType, class TypeFilterType, class IdentifierFilterType>
|
||||
optional<Filter> convertSetFilter(const Convertible& value, Error& error) {
|
||||
if (arrayLength(value) < 2) {
|
||||
error = { "filter expression must at least 2 elements" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<std::string> key = toString(arrayMember(value, 1));
|
||||
if (!key) {
|
||||
error = { "filter expression key must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
if (*key == "$type") {
|
||||
std::vector<FeatureType> values;
|
||||
for (std::size_t i = 2; i < arrayLength(value); ++i) {
|
||||
optional<FeatureType> filterValue = toFeatureType(arrayMember(value, i), error);
|
||||
if (!filterValue) {
|
||||
return {};
|
||||
}
|
||||
values.push_back(*filterValue);
|
||||
}
|
||||
|
||||
return { TypeFilterType { std::move(values) } };
|
||||
|
||||
} else if (*key == "$id") {
|
||||
std::vector<FeatureIdentifier> values;
|
||||
for (std::size_t i = 2; i < arrayLength(value); ++i) {
|
||||
optional<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, i), error);
|
||||
if (!filterValue) {
|
||||
return {};
|
||||
}
|
||||
values.push_back(*filterValue);
|
||||
}
|
||||
|
||||
return { IdentifierFilterType { std::move(values) } };
|
||||
|
||||
} else {
|
||||
std::vector<Value> values;
|
||||
for (std::size_t i = 2; i < arrayLength(value); ++i) {
|
||||
optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, i)), error);
|
||||
if (!filterValue) {
|
||||
return {};
|
||||
}
|
||||
values.push_back(*filterValue);
|
||||
}
|
||||
|
||||
return { FilterType { *key, std::move(values) } };
|
||||
}
|
||||
}
|
||||
|
||||
template <class FilterType>
|
||||
optional<Filter> convertCompoundFilter(const Convertible& value, Error& error) {
|
||||
std::vector<Filter> filters;
|
||||
for (std::size_t i = 1; i < arrayLength(value); ++i) {
|
||||
optional<Filter> element = convert<Filter>(arrayMember(value, i), error);
|
||||
if (!element) {
|
||||
return {};
|
||||
}
|
||||
filters.push_back(*element);
|
||||
}
|
||||
|
||||
return { FilterType { std::move(filters) } };
|
||||
}
|
||||
|
||||
optional<Filter> Converter<Filter>::operator()(const Convertible& value, Error& error) const {
|
||||
if (!isArray(value)) {
|
||||
error = { "filter expression must be an array" };
|
||||
return {};
|
||||
}
|
||||
|
||||
if (arrayLength(value) < 1) {
|
||||
error = { "filter expression must have at least 1 element" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<std::string> op = toString(arrayMember(value, 0));
|
||||
if (!op) {
|
||||
error = { "filter operator must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
if (*op == "==") {
|
||||
return convertEqualityFilter<EqualsFilter, TypeEqualsFilter, IdentifierEqualsFilter>(value, error);
|
||||
} else if (*op == "!=") {
|
||||
return convertEqualityFilter<NotEqualsFilter, TypeNotEqualsFilter, IdentifierNotEqualsFilter>(value, error);
|
||||
} else if (*op == ">") {
|
||||
return convertBinaryFilter<GreaterThanFilter>(value, error);
|
||||
} else if (*op == ">=") {
|
||||
return convertBinaryFilter<GreaterThanEqualsFilter>(value, error);
|
||||
} else if (*op == "<") {
|
||||
return convertBinaryFilter<LessThanFilter>(value, error);
|
||||
} else if (*op == "<=") {
|
||||
return convertBinaryFilter<LessThanEqualsFilter>(value, error);
|
||||
} else if (*op == "in") {
|
||||
return convertSetFilter<InFilter, TypeInFilter, IdentifierInFilter>(value, error);
|
||||
} else if (*op == "!in") {
|
||||
return convertSetFilter<NotInFilter, TypeNotInFilter, IdentifierNotInFilter>(value, error);
|
||||
} else if (*op == "all") {
|
||||
return convertCompoundFilter<AllFilter>(value, error);
|
||||
} else if (*op == "any") {
|
||||
return convertCompoundFilter<AnyFilter>(value, error);
|
||||
} else if (*op == "none") {
|
||||
return convertCompoundFilter<NoneFilter>(value, error);
|
||||
} else if (*op == "has") {
|
||||
return convertUnaryFilter<HasFilter, HasIdentifierFilter>(value, error);
|
||||
} else if (*op == "!has") {
|
||||
return convertUnaryFilter<NotHasFilter, NotHasIdentifierFilter>(value, error);
|
||||
}
|
||||
|
||||
error = { R"(filter operator must be one of "==", "!=", ">", ">=", "<", "<=", "in", "!in", "all", "any", "none", "has", or "!has")" };
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace conversion
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
@@ -1,26 +1,16 @@
|
||||
#include <mbgl/style/conversion/geojson.hpp>
|
||||
#include <mbgl/style/conversion/json.hpp>
|
||||
#include <mbgl/util/rapidjson.hpp>
|
||||
|
||||
#include <mapbox/geojson.hpp>
|
||||
#include <mapbox/geojson/rapidjson.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
namespace conversion {
|
||||
|
||||
optional<GeoJSON> Converter<GeoJSON>::operator()(const std::string& value, Error& error) const {
|
||||
return convertJSON<GeoJSON>(value, error);
|
||||
optional<GeoJSON> Converter<GeoJSON>::operator()(const Convertible& value, Error& error) const {
|
||||
return toGeoJSON(value, error);
|
||||
}
|
||||
|
||||
template <>
|
||||
optional<GeoJSON> Converter<GeoJSON>::operator()(const JSValue& value, Error& error) const {
|
||||
try {
|
||||
return mapbox::geojson::convert(value);
|
||||
} catch (const std::exception& ex) {
|
||||
error = { ex.what() };
|
||||
return {};
|
||||
}
|
||||
optional<GeoJSON> parseGeoJSON(const std::string& value, Error& error) {
|
||||
return convertJSON<GeoJSON>(value, error);
|
||||
}
|
||||
|
||||
} // namespace conversion
|
||||
|
||||
85
src/mbgl/style/conversion/geojson_options.cpp
Normal file
85
src/mbgl/style/conversion/geojson_options.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
#include <mbgl/style/conversion/geojson_options.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
namespace conversion {
|
||||
|
||||
optional<GeoJSONOptions> Converter<GeoJSONOptions>::operator()(const Convertible& value, Error& error) const {
|
||||
GeoJSONOptions options;
|
||||
|
||||
const auto minzoomValue = objectMember(value, "minzoom");
|
||||
if (minzoomValue) {
|
||||
if (toNumber(*minzoomValue)) {
|
||||
options.minzoom = static_cast<uint8_t>(*toNumber(*minzoomValue));
|
||||
} else {
|
||||
error = { "GeoJSON source minzoom value must be a number" };
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto maxzoomValue = objectMember(value, "maxzoom");
|
||||
if (maxzoomValue) {
|
||||
if (toNumber(*maxzoomValue)) {
|
||||
options.maxzoom = static_cast<uint8_t>(*toNumber(*maxzoomValue));
|
||||
} else {
|
||||
error = { "GeoJSON source maxzoom value must be a number" };
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto bufferValue = objectMember(value, "buffer");
|
||||
if (bufferValue) {
|
||||
if (toNumber(*bufferValue)) {
|
||||
options.buffer = static_cast<uint16_t>(*toNumber(*bufferValue));
|
||||
} else {
|
||||
error = { "GeoJSON source buffer value must be a number" };
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto toleranceValue = objectMember(value, "tolerance");
|
||||
if (toleranceValue) {
|
||||
if (toNumber(*toleranceValue)) {
|
||||
options.tolerance = static_cast<double>(*toNumber(*toleranceValue));
|
||||
} else {
|
||||
error = { "GeoJSON source tolerance value must be a number" };
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto clusterValue = objectMember(value, "cluster");
|
||||
if (clusterValue) {
|
||||
if (toBool(*clusterValue)) {
|
||||
options.cluster = *toBool(*clusterValue);
|
||||
} else {
|
||||
error = { "GeoJSON source cluster value must be a boolean" };
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto clusterMaxZoomValue = objectMember(value, "clusterMaxZoom");
|
||||
if (clusterMaxZoomValue) {
|
||||
if (toNumber(*clusterMaxZoomValue)) {
|
||||
options.clusterMaxZoom = static_cast<uint8_t>(*toNumber(*clusterMaxZoomValue));
|
||||
} else {
|
||||
error = { "GeoJSON source clusterMaxZoom value must be a number" };
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto clusterRadiusValue = objectMember(value, "clusterRadius");
|
||||
if (clusterRadiusValue) {
|
||||
if (toNumber(*clusterRadiusValue)) {
|
||||
options.clusterRadius = static_cast<double>(*toNumber(*clusterRadiusValue));
|
||||
} else {
|
||||
error = { "GeoJSON source clusterRadius value must be a number" };
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return { options };
|
||||
}
|
||||
|
||||
} // namespace conversion
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
@@ -20,7 +20,7 @@ optional<T> convertJSON(const std::string& json, Error& error, Args&&...args) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return convert<T, JSValue>(document, error, std::forward<Args>(args)...);
|
||||
return convert<T>(document, error, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
} // namespace conversion
|
||||
|
||||
206
src/mbgl/style/conversion/layer.cpp
Normal file
206
src/mbgl/style/conversion/layer.cpp
Normal file
@@ -0,0 +1,206 @@
|
||||
#include <mbgl/style/conversion/layer.hpp>
|
||||
#include <mbgl/style/conversion/constant.hpp>
|
||||
#include <mbgl/style/conversion/filter.hpp>
|
||||
#include <mbgl/style/conversion/make_property_setters.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>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
namespace conversion {
|
||||
|
||||
optional<Error> setLayoutProperty(Layer& layer, const std::string& name, const Convertible& value) {
|
||||
static const auto setters = makeLayoutPropertySetters();
|
||||
auto it = setters.find(name);
|
||||
if (it == setters.end()) {
|
||||
return Error { "property not found" };
|
||||
}
|
||||
return it->second(layer, value);
|
||||
}
|
||||
|
||||
optional<Error> setPaintProperty(Layer& layer, const std::string& name, const Convertible& value) {
|
||||
static const auto setters = makePaintPropertySetters();
|
||||
auto it = setters.find(name);
|
||||
if (it == setters.end()) {
|
||||
return Error { "property not found" };
|
||||
}
|
||||
return it->second(layer, value);
|
||||
}
|
||||
|
||||
optional<Error> setPaintProperties(Layer& layer, const Convertible& value) {
|
||||
auto paintValue = objectMember(value, "paint");
|
||||
if (!paintValue) {
|
||||
return {};
|
||||
}
|
||||
return eachMember(*paintValue, [&] (const std::string& k, const Convertible& v) {
|
||||
return setPaintProperty(layer, k, v);
|
||||
});
|
||||
}
|
||||
|
||||
template <class LayerType>
|
||||
optional<std::unique_ptr<Layer>> convertVectorLayer(const std::string& id, const Convertible& value, Error& error) {
|
||||
auto sourceValue = objectMember(value, "source");
|
||||
if (!sourceValue) {
|
||||
error = { "layer must have a source" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<std::string> source = toString(*sourceValue);
|
||||
if (!source) {
|
||||
error = { "layer source must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<LayerType> layer = std::make_unique<LayerType>(id, *source);
|
||||
|
||||
auto sourceLayerValue = objectMember(value, "source-layer");
|
||||
if (sourceLayerValue) {
|
||||
optional<std::string> sourceLayer = toString(*sourceLayerValue);
|
||||
if (!sourceLayer) {
|
||||
error = { "layer source-layer must be a string" };
|
||||
return {};
|
||||
}
|
||||
layer->setSourceLayer(*sourceLayer);
|
||||
}
|
||||
|
||||
auto filterValue = objectMember(value, "filter");
|
||||
if (filterValue) {
|
||||
optional<Filter> filter = convert<Filter>(*filterValue, error);
|
||||
if (!filter) {
|
||||
return {};
|
||||
}
|
||||
layer->setFilter(*filter);
|
||||
}
|
||||
|
||||
return { std::move(layer) };
|
||||
}
|
||||
|
||||
static optional<std::unique_ptr<Layer>> convertRasterLayer(const std::string& id, const Convertible& value, Error& error) {
|
||||
auto sourceValue = objectMember(value, "source");
|
||||
if (!sourceValue) {
|
||||
error = { "layer must have a source" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<std::string> source = toString(*sourceValue);
|
||||
if (!source) {
|
||||
error = { "layer source must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
return { std::make_unique<RasterLayer>(id, *source) };
|
||||
}
|
||||
|
||||
static optional<std::unique_ptr<Layer>> convertBackgroundLayer(const std::string& id, const Convertible&, Error&) {
|
||||
return { std::make_unique<BackgroundLayer>(id) };
|
||||
}
|
||||
|
||||
optional<std::unique_ptr<Layer>> Converter<std::unique_ptr<Layer>>::operator()(const Convertible& value, Error& error) const {
|
||||
if (!isObject(value)) {
|
||||
error = { "layer must be an object" };
|
||||
return {};
|
||||
}
|
||||
|
||||
auto idValue = objectMember(value, "id");
|
||||
if (!idValue) {
|
||||
error = { "layer must have an id" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<std::string> id = toString(*idValue);
|
||||
if (!id) {
|
||||
error = { "layer id must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
auto typeValue = objectMember(value, "type");
|
||||
if (!typeValue) {
|
||||
error = { "layer must have a type" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<std::string> type = toString(*typeValue);
|
||||
if (!type) {
|
||||
error = { "layer type must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<std::unique_ptr<Layer>> converted;
|
||||
|
||||
if (*type == "fill") {
|
||||
converted = convertVectorLayer<FillLayer>(*id, value, error);
|
||||
} else if (*type == "fill-extrusion") {
|
||||
converted = convertVectorLayer<FillExtrusionLayer>(*id, value, error);
|
||||
} else if (*type == "line") {
|
||||
converted = convertVectorLayer<LineLayer>(*id, value, error);
|
||||
} else if (*type == "circle") {
|
||||
converted = convertVectorLayer<CircleLayer>(*id, value, error);
|
||||
} else if (*type == "symbol") {
|
||||
converted = convertVectorLayer<SymbolLayer>(*id, value, error);
|
||||
} else if (*type == "raster") {
|
||||
converted = convertRasterLayer(*id, value, error);
|
||||
} else if (*type == "background") {
|
||||
converted = convertBackgroundLayer(*id, value, error);
|
||||
} else {
|
||||
error = { "invalid layer type" };
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!converted) {
|
||||
return converted;
|
||||
}
|
||||
|
||||
std::unique_ptr<Layer> layer = std::move(*converted);
|
||||
|
||||
auto minzoomValue = objectMember(value, "minzoom");
|
||||
if (minzoomValue) {
|
||||
optional<float> minzoom = toNumber(*minzoomValue);
|
||||
if (!minzoom) {
|
||||
error = { "minzoom must be numeric" };
|
||||
return {};
|
||||
}
|
||||
layer->setMinZoom(*minzoom);
|
||||
}
|
||||
|
||||
auto maxzoomValue = objectMember(value, "maxzoom");
|
||||
if (maxzoomValue) {
|
||||
optional<float> maxzoom = toNumber(*maxzoomValue);
|
||||
if (!maxzoom) {
|
||||
error = { "maxzoom must be numeric" };
|
||||
return {};
|
||||
}
|
||||
layer->setMaxZoom(*maxzoom);
|
||||
}
|
||||
|
||||
auto layoutValue = objectMember(value, "layout");
|
||||
if (layoutValue) {
|
||||
if (!isObject(*layoutValue)) {
|
||||
error = { "layout must be an object" };
|
||||
return {};
|
||||
}
|
||||
optional<Error> error_ = eachMember(*layoutValue, [&] (const std::string& k, const Convertible& v) {
|
||||
return setLayoutProperty(*layer, k, v);
|
||||
});
|
||||
if (error_) {
|
||||
error = *error_;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
optional<Error> error_ = setPaintProperties(*layer, value);
|
||||
if (error_) {
|
||||
error = *error_;
|
||||
return {};
|
||||
}
|
||||
|
||||
return std::move(layer);
|
||||
}
|
||||
|
||||
} // namespace conversion
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
115
src/mbgl/style/conversion/light.cpp
Normal file
115
src/mbgl/style/conversion/light.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
#include <mbgl/style/conversion/light.hpp>
|
||||
#include <mbgl/style/conversion/position.hpp>
|
||||
#include <mbgl/style/conversion/property_value.hpp>
|
||||
#include <mbgl/style/conversion/transition_options.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
namespace conversion {
|
||||
|
||||
optional<Light> Converter<Light>::operator()(const Convertible& value, Error& error) const {
|
||||
if (!isObject(value)) {
|
||||
error = { "light must be an object" };
|
||||
return {};
|
||||
}
|
||||
|
||||
Light light;
|
||||
|
||||
const auto anchor = objectMember(value, "anchor");
|
||||
if (anchor) {
|
||||
optional<PropertyValue<LightAnchorType>> convertedAnchor =
|
||||
convert<PropertyValue<LightAnchorType>>(*anchor, error);
|
||||
|
||||
if (convertedAnchor) {
|
||||
light.setAnchor(*convertedAnchor);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto anchorTransition = objectMember(value, "anchor-transition");
|
||||
if (anchorTransition) {
|
||||
optional<TransitionOptions> transition =
|
||||
convert<TransitionOptions>(*anchorTransition, error);
|
||||
if (transition) {
|
||||
light.setAnchorTransition(*transition);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto color = objectMember(value, "color");
|
||||
if (color) {
|
||||
optional<PropertyValue<Color>> convertedColor =
|
||||
convert<PropertyValue<Color>>(*color, error);
|
||||
|
||||
if (convertedColor) {
|
||||
light.setColor(*convertedColor);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto colorTransition = objectMember(value, "color-transition");
|
||||
if (colorTransition) {
|
||||
optional<TransitionOptions> transition =
|
||||
convert<TransitionOptions>(*colorTransition, error);
|
||||
if (transition) {
|
||||
light.setColorTransition(*transition);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto position = objectMember(value, "position");
|
||||
if (position) {
|
||||
optional<PropertyValue<Position>> convertedPosition =
|
||||
convert<PropertyValue<Position>>(*position, error);
|
||||
|
||||
if (convertedPosition) {
|
||||
light.setPosition(*convertedPosition);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto positionTransition = objectMember(value, "position-transition");
|
||||
if (positionTransition) {
|
||||
optional<TransitionOptions> transition =
|
||||
convert<TransitionOptions>(*positionTransition, error);
|
||||
if (transition) {
|
||||
light.setPositionTransition(*transition);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto intensity = objectMember(value, "intensity");
|
||||
if (intensity) {
|
||||
optional<PropertyValue<float>> convertedIntensity =
|
||||
convert<PropertyValue<float>>(*intensity, error);
|
||||
|
||||
if (convertedIntensity) {
|
||||
light.setIntensity(*convertedIntensity);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto intensityTransition = objectMember(value, "intensity-transition");
|
||||
if (intensityTransition) {
|
||||
optional<TransitionOptions> transition =
|
||||
convert<TransitionOptions>(*intensityTransition, error);
|
||||
if (transition) {
|
||||
light.setIntensityTransition(*transition);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return { std::move(light) };
|
||||
}
|
||||
|
||||
} // namespace conversion
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
209
src/mbgl/style/conversion/make_property_setters.hpp
Normal file
209
src/mbgl/style/conversion/make_property_setters.hpp
Normal file
@@ -0,0 +1,209 @@
|
||||
#pragma once
|
||||
|
||||
// This file is generated. Edit make_property_setters.hpp.ejs, then run `make style-code`.
|
||||
|
||||
#include <mbgl/style/conversion/property_setter.hpp>
|
||||
|
||||
#include <mbgl/style/layers/fill_layer.hpp>
|
||||
#include <mbgl/style/layers/line_layer.hpp>
|
||||
#include <mbgl/style/layers/symbol_layer.hpp>
|
||||
#include <mbgl/style/layers/circle_layer.hpp>
|
||||
#include <mbgl/style/layers/fill_extrusion_layer.hpp>
|
||||
#include <mbgl/style/layers/raster_layer.hpp>
|
||||
#include <mbgl/style/layers/background_layer.hpp>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
namespace conversion {
|
||||
|
||||
inline auto makeLayoutPropertySetters() {
|
||||
std::unordered_map<std::string, PropertySetter> result;
|
||||
|
||||
result["visibility"] = &setVisibility;
|
||||
|
||||
|
||||
result["line-cap"] = &setProperty<LineLayer, PropertyValue<LineCapType>, &LineLayer::setLineCap>;
|
||||
result["line-join"] = &setProperty<LineLayer, DataDrivenPropertyValue<LineJoinType>, &LineLayer::setLineJoin>;
|
||||
result["line-miter-limit"] = &setProperty<LineLayer, PropertyValue<float>, &LineLayer::setLineMiterLimit>;
|
||||
result["line-round-limit"] = &setProperty<LineLayer, PropertyValue<float>, &LineLayer::setLineRoundLimit>;
|
||||
|
||||
result["symbol-placement"] = &setProperty<SymbolLayer, PropertyValue<SymbolPlacementType>, &SymbolLayer::setSymbolPlacement>;
|
||||
result["symbol-spacing"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setSymbolSpacing>;
|
||||
result["symbol-avoid-edges"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setSymbolAvoidEdges>;
|
||||
result["icon-allow-overlap"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconAllowOverlap>;
|
||||
result["icon-ignore-placement"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconIgnorePlacement>;
|
||||
result["icon-optional"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconOptional>;
|
||||
result["icon-rotation-alignment"] = &setProperty<SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setIconRotationAlignment>;
|
||||
result["icon-size"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconSize>;
|
||||
result["icon-text-fit"] = &setProperty<SymbolLayer, PropertyValue<IconTextFitType>, &SymbolLayer::setIconTextFit>;
|
||||
result["icon-text-fit-padding"] = &setProperty<SymbolLayer, PropertyValue<std::array<float, 4>>, &SymbolLayer::setIconTextFitPadding>;
|
||||
result["icon-image"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setIconImage>;
|
||||
result["icon-rotate"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconRotate>;
|
||||
result["icon-padding"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setIconPadding>;
|
||||
result["icon-keep-upright"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconKeepUpright>;
|
||||
result["icon-offset"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<std::array<float, 2>>, &SymbolLayer::setIconOffset>;
|
||||
result["icon-anchor"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<SymbolAnchorType>, &SymbolLayer::setIconAnchor>;
|
||||
result["icon-pitch-alignment"] = &setProperty<SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setIconPitchAlignment>;
|
||||
result["text-pitch-alignment"] = &setProperty<SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextPitchAlignment>;
|
||||
result["text-rotation-alignment"] = &setProperty<SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextRotationAlignment>;
|
||||
result["text-field"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setTextField>;
|
||||
result["text-font"] = &setProperty<SymbolLayer, PropertyValue<std::vector<std::string>>, &SymbolLayer::setTextFont>;
|
||||
result["text-size"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextSize>;
|
||||
result["text-max-width"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextMaxWidth>;
|
||||
result["text-line-height"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextLineHeight>;
|
||||
result["text-letter-spacing"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextLetterSpacing>;
|
||||
result["text-justify"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<TextJustifyType>, &SymbolLayer::setTextJustify>;
|
||||
result["text-anchor"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<SymbolAnchorType>, &SymbolLayer::setTextAnchor>;
|
||||
result["text-max-angle"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextMaxAngle>;
|
||||
result["text-rotate"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextRotate>;
|
||||
result["text-padding"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextPadding>;
|
||||
result["text-keep-upright"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextKeepUpright>;
|
||||
result["text-transform"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<TextTransformType>, &SymbolLayer::setTextTransform>;
|
||||
result["text-offset"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<std::array<float, 2>>, &SymbolLayer::setTextOffset>;
|
||||
result["text-allow-overlap"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextAllowOverlap>;
|
||||
result["text-ignore-placement"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextIgnorePlacement>;
|
||||
result["text-optional"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setTextOptional>;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline auto makePaintPropertySetters() {
|
||||
std::unordered_map<std::string, PropertySetter> result;
|
||||
|
||||
result["fill-antialias"] = &setProperty<FillLayer, PropertyValue<bool>, &FillLayer::setFillAntialias>;
|
||||
result["fill-antialias-transition"] = &setTransition<FillLayer, &FillLayer::setFillAntialiasTransition>;
|
||||
result["fill-opacity"] = &setProperty<FillLayer, DataDrivenPropertyValue<float>, &FillLayer::setFillOpacity>;
|
||||
result["fill-opacity-transition"] = &setTransition<FillLayer, &FillLayer::setFillOpacityTransition>;
|
||||
result["fill-color"] = &setProperty<FillLayer, DataDrivenPropertyValue<Color>, &FillLayer::setFillColor>;
|
||||
result["fill-color-transition"] = &setTransition<FillLayer, &FillLayer::setFillColorTransition>;
|
||||
result["fill-outline-color"] = &setProperty<FillLayer, DataDrivenPropertyValue<Color>, &FillLayer::setFillOutlineColor>;
|
||||
result["fill-outline-color-transition"] = &setTransition<FillLayer, &FillLayer::setFillOutlineColorTransition>;
|
||||
result["fill-translate"] = &setProperty<FillLayer, PropertyValue<std::array<float, 2>>, &FillLayer::setFillTranslate>;
|
||||
result["fill-translate-transition"] = &setTransition<FillLayer, &FillLayer::setFillTranslateTransition>;
|
||||
result["fill-translate-anchor"] = &setProperty<FillLayer, PropertyValue<TranslateAnchorType>, &FillLayer::setFillTranslateAnchor>;
|
||||
result["fill-translate-anchor-transition"] = &setTransition<FillLayer, &FillLayer::setFillTranslateAnchorTransition>;
|
||||
result["fill-pattern"] = &setProperty<FillLayer, PropertyValue<std::string>, &FillLayer::setFillPattern>;
|
||||
result["fill-pattern-transition"] = &setTransition<FillLayer, &FillLayer::setFillPatternTransition>;
|
||||
|
||||
result["line-opacity"] = &setProperty<LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineOpacity>;
|
||||
result["line-opacity-transition"] = &setTransition<LineLayer, &LineLayer::setLineOpacityTransition>;
|
||||
result["line-color"] = &setProperty<LineLayer, DataDrivenPropertyValue<Color>, &LineLayer::setLineColor>;
|
||||
result["line-color-transition"] = &setTransition<LineLayer, &LineLayer::setLineColorTransition>;
|
||||
result["line-translate"] = &setProperty<LineLayer, PropertyValue<std::array<float, 2>>, &LineLayer::setLineTranslate>;
|
||||
result["line-translate-transition"] = &setTransition<LineLayer, &LineLayer::setLineTranslateTransition>;
|
||||
result["line-translate-anchor"] = &setProperty<LineLayer, PropertyValue<TranslateAnchorType>, &LineLayer::setLineTranslateAnchor>;
|
||||
result["line-translate-anchor-transition"] = &setTransition<LineLayer, &LineLayer::setLineTranslateAnchorTransition>;
|
||||
result["line-width"] = &setProperty<LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineWidth>;
|
||||
result["line-width-transition"] = &setTransition<LineLayer, &LineLayer::setLineWidthTransition>;
|
||||
result["line-gap-width"] = &setProperty<LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineGapWidth>;
|
||||
result["line-gap-width-transition"] = &setTransition<LineLayer, &LineLayer::setLineGapWidthTransition>;
|
||||
result["line-offset"] = &setProperty<LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineOffset>;
|
||||
result["line-offset-transition"] = &setTransition<LineLayer, &LineLayer::setLineOffsetTransition>;
|
||||
result["line-blur"] = &setProperty<LineLayer, DataDrivenPropertyValue<float>, &LineLayer::setLineBlur>;
|
||||
result["line-blur-transition"] = &setTransition<LineLayer, &LineLayer::setLineBlurTransition>;
|
||||
result["line-dasharray"] = &setProperty<LineLayer, PropertyValue<std::vector<float>>, &LineLayer::setLineDasharray>;
|
||||
result["line-dasharray-transition"] = &setTransition<LineLayer, &LineLayer::setLineDasharrayTransition>;
|
||||
result["line-pattern"] = &setProperty<LineLayer, PropertyValue<std::string>, &LineLayer::setLinePattern>;
|
||||
result["line-pattern-transition"] = &setTransition<LineLayer, &LineLayer::setLinePatternTransition>;
|
||||
|
||||
result["icon-opacity"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconOpacity>;
|
||||
result["icon-opacity-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconOpacityTransition>;
|
||||
result["icon-color"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setIconColor>;
|
||||
result["icon-color-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconColorTransition>;
|
||||
result["icon-halo-color"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setIconHaloColor>;
|
||||
result["icon-halo-color-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconHaloColorTransition>;
|
||||
result["icon-halo-width"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconHaloWidth>;
|
||||
result["icon-halo-width-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconHaloWidthTransition>;
|
||||
result["icon-halo-blur"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconHaloBlur>;
|
||||
result["icon-halo-blur-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconHaloBlurTransition>;
|
||||
result["icon-translate"] = &setProperty<SymbolLayer, PropertyValue<std::array<float, 2>>, &SymbolLayer::setIconTranslate>;
|
||||
result["icon-translate-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconTranslateTransition>;
|
||||
result["icon-translate-anchor"] = &setProperty<SymbolLayer, PropertyValue<TranslateAnchorType>, &SymbolLayer::setIconTranslateAnchor>;
|
||||
result["icon-translate-anchor-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setIconTranslateAnchorTransition>;
|
||||
result["text-opacity"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextOpacity>;
|
||||
result["text-opacity-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setTextOpacityTransition>;
|
||||
result["text-color"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setTextColor>;
|
||||
result["text-color-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setTextColorTransition>;
|
||||
result["text-halo-color"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<Color>, &SymbolLayer::setTextHaloColor>;
|
||||
result["text-halo-color-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setTextHaloColorTransition>;
|
||||
result["text-halo-width"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextHaloWidth>;
|
||||
result["text-halo-width-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setTextHaloWidthTransition>;
|
||||
result["text-halo-blur"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextHaloBlur>;
|
||||
result["text-halo-blur-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setTextHaloBlurTransition>;
|
||||
result["text-translate"] = &setProperty<SymbolLayer, PropertyValue<std::array<float, 2>>, &SymbolLayer::setTextTranslate>;
|
||||
result["text-translate-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setTextTranslateTransition>;
|
||||
result["text-translate-anchor"] = &setProperty<SymbolLayer, PropertyValue<TranslateAnchorType>, &SymbolLayer::setTextTranslateAnchor>;
|
||||
result["text-translate-anchor-transition"] = &setTransition<SymbolLayer, &SymbolLayer::setTextTranslateAnchorTransition>;
|
||||
|
||||
result["circle-radius"] = &setProperty<CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleRadius>;
|
||||
result["circle-radius-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleRadiusTransition>;
|
||||
result["circle-color"] = &setProperty<CircleLayer, DataDrivenPropertyValue<Color>, &CircleLayer::setCircleColor>;
|
||||
result["circle-color-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleColorTransition>;
|
||||
result["circle-blur"] = &setProperty<CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleBlur>;
|
||||
result["circle-blur-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleBlurTransition>;
|
||||
result["circle-opacity"] = &setProperty<CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleOpacity>;
|
||||
result["circle-opacity-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleOpacityTransition>;
|
||||
result["circle-translate"] = &setProperty<CircleLayer, PropertyValue<std::array<float, 2>>, &CircleLayer::setCircleTranslate>;
|
||||
result["circle-translate-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleTranslateTransition>;
|
||||
result["circle-translate-anchor"] = &setProperty<CircleLayer, PropertyValue<TranslateAnchorType>, &CircleLayer::setCircleTranslateAnchor>;
|
||||
result["circle-translate-anchor-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleTranslateAnchorTransition>;
|
||||
result["circle-pitch-scale"] = &setProperty<CircleLayer, PropertyValue<CirclePitchScaleType>, &CircleLayer::setCirclePitchScale>;
|
||||
result["circle-pitch-scale-transition"] = &setTransition<CircleLayer, &CircleLayer::setCirclePitchScaleTransition>;
|
||||
result["circle-pitch-alignment"] = &setProperty<CircleLayer, PropertyValue<AlignmentType>, &CircleLayer::setCirclePitchAlignment>;
|
||||
result["circle-pitch-alignment-transition"] = &setTransition<CircleLayer, &CircleLayer::setCirclePitchAlignmentTransition>;
|
||||
result["circle-stroke-width"] = &setProperty<CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleStrokeWidth>;
|
||||
result["circle-stroke-width-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleStrokeWidthTransition>;
|
||||
result["circle-stroke-color"] = &setProperty<CircleLayer, DataDrivenPropertyValue<Color>, &CircleLayer::setCircleStrokeColor>;
|
||||
result["circle-stroke-color-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleStrokeColorTransition>;
|
||||
result["circle-stroke-opacity"] = &setProperty<CircleLayer, DataDrivenPropertyValue<float>, &CircleLayer::setCircleStrokeOpacity>;
|
||||
result["circle-stroke-opacity-transition"] = &setTransition<CircleLayer, &CircleLayer::setCircleStrokeOpacityTransition>;
|
||||
|
||||
result["fill-extrusion-opacity"] = &setProperty<FillExtrusionLayer, PropertyValue<float>, &FillExtrusionLayer::setFillExtrusionOpacity>;
|
||||
result["fill-extrusion-opacity-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionOpacityTransition>;
|
||||
result["fill-extrusion-color"] = &setProperty<FillExtrusionLayer, DataDrivenPropertyValue<Color>, &FillExtrusionLayer::setFillExtrusionColor>;
|
||||
result["fill-extrusion-color-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionColorTransition>;
|
||||
result["fill-extrusion-translate"] = &setProperty<FillExtrusionLayer, PropertyValue<std::array<float, 2>>, &FillExtrusionLayer::setFillExtrusionTranslate>;
|
||||
result["fill-extrusion-translate-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionTranslateTransition>;
|
||||
result["fill-extrusion-translate-anchor"] = &setProperty<FillExtrusionLayer, PropertyValue<TranslateAnchorType>, &FillExtrusionLayer::setFillExtrusionTranslateAnchor>;
|
||||
result["fill-extrusion-translate-anchor-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionTranslateAnchorTransition>;
|
||||
result["fill-extrusion-pattern"] = &setProperty<FillExtrusionLayer, PropertyValue<std::string>, &FillExtrusionLayer::setFillExtrusionPattern>;
|
||||
result["fill-extrusion-pattern-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionPatternTransition>;
|
||||
result["fill-extrusion-height"] = &setProperty<FillExtrusionLayer, DataDrivenPropertyValue<float>, &FillExtrusionLayer::setFillExtrusionHeight>;
|
||||
result["fill-extrusion-height-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionHeightTransition>;
|
||||
result["fill-extrusion-base"] = &setProperty<FillExtrusionLayer, DataDrivenPropertyValue<float>, &FillExtrusionLayer::setFillExtrusionBase>;
|
||||
result["fill-extrusion-base-transition"] = &setTransition<FillExtrusionLayer, &FillExtrusionLayer::setFillExtrusionBaseTransition>;
|
||||
|
||||
result["raster-opacity"] = &setProperty<RasterLayer, PropertyValue<float>, &RasterLayer::setRasterOpacity>;
|
||||
result["raster-opacity-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterOpacityTransition>;
|
||||
result["raster-hue-rotate"] = &setProperty<RasterLayer, PropertyValue<float>, &RasterLayer::setRasterHueRotate>;
|
||||
result["raster-hue-rotate-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterHueRotateTransition>;
|
||||
result["raster-brightness-min"] = &setProperty<RasterLayer, PropertyValue<float>, &RasterLayer::setRasterBrightnessMin>;
|
||||
result["raster-brightness-min-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterBrightnessMinTransition>;
|
||||
result["raster-brightness-max"] = &setProperty<RasterLayer, PropertyValue<float>, &RasterLayer::setRasterBrightnessMax>;
|
||||
result["raster-brightness-max-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterBrightnessMaxTransition>;
|
||||
result["raster-saturation"] = &setProperty<RasterLayer, PropertyValue<float>, &RasterLayer::setRasterSaturation>;
|
||||
result["raster-saturation-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterSaturationTransition>;
|
||||
result["raster-contrast"] = &setProperty<RasterLayer, PropertyValue<float>, &RasterLayer::setRasterContrast>;
|
||||
result["raster-contrast-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterContrastTransition>;
|
||||
result["raster-fade-duration"] = &setProperty<RasterLayer, PropertyValue<float>, &RasterLayer::setRasterFadeDuration>;
|
||||
result["raster-fade-duration-transition"] = &setTransition<RasterLayer, &RasterLayer::setRasterFadeDurationTransition>;
|
||||
|
||||
result["background-color"] = &setProperty<BackgroundLayer, PropertyValue<Color>, &BackgroundLayer::setBackgroundColor>;
|
||||
result["background-color-transition"] = &setTransition<BackgroundLayer, &BackgroundLayer::setBackgroundColorTransition>;
|
||||
result["background-pattern"] = &setProperty<BackgroundLayer, PropertyValue<std::string>, &BackgroundLayer::setBackgroundPattern>;
|
||||
result["background-pattern-transition"] = &setTransition<BackgroundLayer, &BackgroundLayer::setBackgroundPatternTransition>;
|
||||
result["background-opacity"] = &setProperty<BackgroundLayer, PropertyValue<float>, &BackgroundLayer::setBackgroundOpacity>;
|
||||
result["background-opacity-transition"] = &setTransition<BackgroundLayer, &BackgroundLayer::setBackgroundOpacityTransition>;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace conversion
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
46
src/mbgl/style/conversion/make_property_setters.hpp.ejs
Normal file
46
src/mbgl/style/conversion/make_property_setters.hpp.ejs
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
// This file is generated. Edit make_property_setters.hpp.ejs, then run `make style-code`.
|
||||
|
||||
#include <mbgl/style/conversion/property_setter.hpp>
|
||||
|
||||
<% for (const layer of locals.layers) { -%>
|
||||
#include <mbgl/style/layers/<%- layer.type.replace('-', '_') %>_layer.hpp>
|
||||
<% } -%>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
namespace conversion {
|
||||
|
||||
inline auto makeLayoutPropertySetters() {
|
||||
std::unordered_map<std::string, PropertySetter> result;
|
||||
|
||||
result["visibility"] = &setVisibility;
|
||||
|
||||
<% for (const layer of locals.layers) { -%>
|
||||
<% for (const property of layer.layoutProperties) { -%>
|
||||
result["<%- property.name %>"] = &setProperty<<%- camelize(layer.type) %>Layer, <%- propertyValueType(property) %>, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>>;
|
||||
<% } -%>
|
||||
|
||||
<% } -%>
|
||||
return result;
|
||||
}
|
||||
|
||||
inline auto makePaintPropertySetters() {
|
||||
std::unordered_map<std::string, PropertySetter> result;
|
||||
|
||||
<% for (const layer of locals.layers) { -%>
|
||||
<% for (const property of layer.paintProperties) { -%>
|
||||
result["<%- property.name %>"] = &setProperty<<%- camelize(layer.type) %>Layer, <%- propertyValueType(property) %>, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>>;
|
||||
result["<%- property.name %>-transition"] = &setTransition<<%- camelize(layer.type) %>Layer, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>Transition>;
|
||||
<% } -%>
|
||||
|
||||
<% } -%>
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace conversion
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
22
src/mbgl/style/conversion/position.cpp
Normal file
22
src/mbgl/style/conversion/position.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include <mbgl/style/conversion/position.hpp>
|
||||
#include <mbgl/style/conversion/constant.hpp>
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
namespace conversion {
|
||||
|
||||
optional<Position> Converter<Position>::operator()(const Convertible& value, Error& error) const {
|
||||
optional<std::array<float, 3>> spherical = convert<std::array<float, 3>>(value, error);
|
||||
|
||||
if (!spherical) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return Position(*spherical);
|
||||
}
|
||||
|
||||
} // namespace conversion
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
@@ -13,11 +13,10 @@ namespace mbgl {
|
||||
namespace style {
|
||||
namespace conversion {
|
||||
|
||||
template <class V>
|
||||
using PropertySetter = optional<Error> (*) (Layer&, const V&);
|
||||
using PropertySetter = optional<Error> (*) (Layer&, const Convertible&);
|
||||
|
||||
template <class V, class L, class PropertyValue, void (L::*setter)(PropertyValue)>
|
||||
optional<Error> setProperty(Layer& layer, const V& value) {
|
||||
template <class L, class PropertyValue, void (L::*setter)(PropertyValue)>
|
||||
optional<Error> setProperty(Layer& layer, const Convertible& value) {
|
||||
auto* typedLayer = layer.as<L>();
|
||||
if (!typedLayer) {
|
||||
return Error { "layer doesn't support this property" };
|
||||
@@ -33,8 +32,8 @@ optional<Error> setProperty(Layer& layer, const V& value) {
|
||||
return {};
|
||||
}
|
||||
|
||||
template <class V, class L, void (L::*setter)(const TransitionOptions&)>
|
||||
optional<Error> setTransition(Layer& layer, const V& value) {
|
||||
template <class L, void (L::*setter)(const TransitionOptions&)>
|
||||
optional<Error> setTransition(Layer& layer, const Convertible& value) {
|
||||
auto* typedLayer = layer.as<L>();
|
||||
if (!typedLayer) {
|
||||
return Error { "layer doesn't support this property" };
|
||||
@@ -50,8 +49,7 @@ optional<Error> setTransition(Layer& layer, const V& value) {
|
||||
return {};
|
||||
}
|
||||
|
||||
template <class V>
|
||||
optional<Error> setVisibility(Layer& layer, const V& value) {
|
||||
inline optional<Error> setVisibility(Layer& layer, const Convertible& value) {
|
||||
if (isUndefined(value)) {
|
||||
layer.setVisibility(VisibilityType::Visible);
|
||||
return {};
|
||||
175
src/mbgl/style/conversion/source.cpp
Normal file
175
src/mbgl/style/conversion/source.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
#include <mbgl/style/conversion/source.hpp>
|
||||
#include <mbgl/style/conversion/coordinate.hpp>
|
||||
#include <mbgl/style/conversion/geojson.hpp>
|
||||
#include <mbgl/style/conversion/geojson_options.hpp>
|
||||
#include <mbgl/style/conversion/tileset.hpp>
|
||||
#include <mbgl/style/sources/geojson_source.hpp>
|
||||
#include <mbgl/style/sources/raster_source.hpp>
|
||||
#include <mbgl/style/sources/vector_source.hpp>
|
||||
#include <mbgl/style/sources/image_source.hpp>
|
||||
#include <mbgl/util/geo.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
namespace conversion {
|
||||
|
||||
// A tile source can either specify a URL to TileJSON, or inline TileJSON.
|
||||
static optional<variant<std::string, Tileset>> convertURLOrTileset(const Convertible& value, Error& error) {
|
||||
auto urlVal = objectMember(value, "url");
|
||||
if (!urlVal) {
|
||||
optional<Tileset> tileset = convert<Tileset>(value, error);
|
||||
if (!tileset) {
|
||||
return {};
|
||||
}
|
||||
return { *tileset };
|
||||
}
|
||||
|
||||
optional<std::string> url = toString(*urlVal);
|
||||
if (!url) {
|
||||
error = { "source url must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
return { *url };
|
||||
}
|
||||
|
||||
static optional<std::unique_ptr<Source>> convertRasterSource(const std::string& id,
|
||||
const Convertible& value,
|
||||
Error& error) {
|
||||
optional<variant<std::string, Tileset>> urlOrTileset = convertURLOrTileset(value, error);
|
||||
if (!urlOrTileset) {
|
||||
return {};
|
||||
}
|
||||
|
||||
uint16_t tileSize = util::tileSize;
|
||||
auto tileSizeValue = objectMember(value, "tileSize");
|
||||
if (tileSizeValue) {
|
||||
optional<float> size = toNumber(*tileSizeValue);
|
||||
if (!size || *size < 0 || *size > std::numeric_limits<uint16_t>::max()) {
|
||||
error = { "invalid tileSize" };
|
||||
return {};
|
||||
}
|
||||
tileSize = *size;
|
||||
}
|
||||
|
||||
return { std::make_unique<RasterSource>(id, std::move(*urlOrTileset), tileSize) };
|
||||
}
|
||||
|
||||
static optional<std::unique_ptr<Source>> convertVectorSource(const std::string& id,
|
||||
const Convertible& value,
|
||||
Error& error) {
|
||||
optional<variant<std::string, Tileset>> urlOrTileset = convertURLOrTileset(value, error);
|
||||
if (!urlOrTileset) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return { std::make_unique<VectorSource>(id, std::move(*urlOrTileset)) };
|
||||
}
|
||||
|
||||
static optional<std::unique_ptr<Source>> convertGeoJSONSource(const std::string& id,
|
||||
const Convertible& value,
|
||||
Error& error) {
|
||||
auto dataValue = objectMember(value, "data");
|
||||
if (!dataValue) {
|
||||
error = { "GeoJSON source must have a data value" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<GeoJSONOptions> options = convert<GeoJSONOptions>(value, error);
|
||||
if (!options) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto result = std::make_unique<GeoJSONSource>(id, *options);
|
||||
|
||||
if (isObject(*dataValue)) {
|
||||
optional<GeoJSON> geoJSON = convert<GeoJSON>(*dataValue, error);
|
||||
if (!geoJSON) {
|
||||
return {};
|
||||
}
|
||||
result->setGeoJSON(std::move(*geoJSON));
|
||||
} else if (toString(*dataValue)) {
|
||||
result->setURL(*toString(*dataValue));
|
||||
} else {
|
||||
error = { "GeoJSON data must be a URL or an object" };
|
||||
return {};
|
||||
}
|
||||
|
||||
return { std::move(result) };
|
||||
}
|
||||
|
||||
static optional<std::unique_ptr<Source>> convertImageSource(const std::string& id,
|
||||
const Convertible& value,
|
||||
Error& error) {
|
||||
auto urlValue = objectMember(value, "url");
|
||||
if (!urlValue) {
|
||||
error = { "Image source must have a url value" };
|
||||
return {};
|
||||
}
|
||||
|
||||
auto urlString = toString(*urlValue);
|
||||
if (!urlString) {
|
||||
error = { "Image url must be a URL string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
auto coordinatesValue = objectMember(value, "coordinates");
|
||||
if (!coordinatesValue) {
|
||||
error = { "Image source must have a coordinates values" };
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!isArray(*coordinatesValue) || arrayLength(*coordinatesValue) != 4) {
|
||||
error = { "Image coordinates must be an array of four longitude latitude pairs" };
|
||||
return {};
|
||||
}
|
||||
|
||||
std::array<LatLng, 4> coordinates;
|
||||
for (std::size_t i=0; i < 4; i++) {
|
||||
auto latLng = conversion::convert<LatLng>(arrayMember(*coordinatesValue,i), error);
|
||||
if (!latLng) {
|
||||
return {};
|
||||
}
|
||||
coordinates[i] = *latLng;
|
||||
}
|
||||
auto result = std::make_unique<ImageSource>(id, coordinates);
|
||||
result->setURL(*urlString);
|
||||
|
||||
return { std::move(result) };
|
||||
}
|
||||
|
||||
optional<std::unique_ptr<Source>> Converter<std::unique_ptr<Source>>::operator()(const Convertible& value, Error& error, const std::string& id) const {
|
||||
if (!isObject(value)) {
|
||||
error = { "source must be an object" };
|
||||
return {};
|
||||
}
|
||||
|
||||
auto typeValue = objectMember(value, "type");
|
||||
if (!typeValue) {
|
||||
error = { "source must have a type" };
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<std::string> type = toString(*typeValue);
|
||||
if (!type) {
|
||||
error = { "source type must be a string" };
|
||||
return {};
|
||||
}
|
||||
|
||||
if (*type == "raster") {
|
||||
return convertRasterSource(id, value, error);
|
||||
} else if (*type == "vector") {
|
||||
return convertVectorSource(id, value, error);
|
||||
} else if (*type == "geojson") {
|
||||
return convertGeoJSONSource(id, value, error);
|
||||
} else if (*type == "image") {
|
||||
return convertImageSource(id, value, error);
|
||||
} else {
|
||||
error = { "invalid source type" };
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace conversion
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
73
src/mbgl/style/conversion/tileset.cpp
Normal file
73
src/mbgl/style/conversion/tileset.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#include <mbgl/style/conversion/tileset.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
namespace conversion {
|
||||
|
||||
optional<Tileset> Converter<Tileset>::operator()(const Convertible& value, Error& error) const {
|
||||
Tileset result;
|
||||
|
||||
auto tiles = objectMember(value, "tiles");
|
||||
if (!tiles) {
|
||||
error = { "source must have tiles" };
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!isArray(*tiles)) {
|
||||
error = { "source tiles must be an array" };
|
||||
return {};
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < arrayLength(*tiles); i++) {
|
||||
optional<std::string> urlTemplate = toString(arrayMember(*tiles, i));
|
||||
if (!urlTemplate) {
|
||||
error = { "source tiles member must be a string" };
|
||||
return {};
|
||||
}
|
||||
result.tiles.push_back(std::move(*urlTemplate));
|
||||
}
|
||||
|
||||
auto schemeValue = objectMember(value, "scheme");
|
||||
if (schemeValue) {
|
||||
optional<std::string> scheme = toString(*schemeValue);
|
||||
if (scheme && *scheme == "tms") {
|
||||
result.scheme = Tileset::Scheme::TMS;
|
||||
}
|
||||
}
|
||||
|
||||
auto minzoomValue = objectMember(value, "minzoom");
|
||||
if (minzoomValue) {
|
||||
optional<float> minzoom = toNumber(*minzoomValue);
|
||||
if (!minzoom || *minzoom < 0 || *minzoom > std::numeric_limits<uint8_t>::max()) {
|
||||
error = { "invalid minzoom" };
|
||||
return {};
|
||||
}
|
||||
result.zoomRange.min = *minzoom;
|
||||
}
|
||||
|
||||
auto maxzoomValue = objectMember(value, "maxzoom");
|
||||
if (maxzoomValue) {
|
||||
optional<float> maxzoom = toNumber(*maxzoomValue);
|
||||
if (!maxzoom || *maxzoom < 0 || *maxzoom > std::numeric_limits<uint8_t>::max()) {
|
||||
error = { "invalid maxzoom" };
|
||||
return {};
|
||||
}
|
||||
result.zoomRange.max = *maxzoom;
|
||||
}
|
||||
|
||||
auto attributionValue = objectMember(value, "attribution");
|
||||
if (attributionValue) {
|
||||
optional<std::string> attribution = toString(*attributionValue);
|
||||
if (!attribution) {
|
||||
error = { "source attribution must be a string" };
|
||||
return {};
|
||||
}
|
||||
result.attribution = std::move(*attribution);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace conversion
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
40
src/mbgl/style/conversion/transition_options.cpp
Normal file
40
src/mbgl/style/conversion/transition_options.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include <mbgl/style/conversion/transition_options.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
namespace conversion {
|
||||
|
||||
optional<TransitionOptions> Converter<TransitionOptions>::operator()(const Convertible& value, Error& error) const {
|
||||
if (!isObject(value)) {
|
||||
error = { "transition must be an object" };
|
||||
return {};
|
||||
}
|
||||
|
||||
TransitionOptions result;
|
||||
|
||||
auto duration = objectMember(value, "duration");
|
||||
if (duration) {
|
||||
auto number = toNumber(*duration);
|
||||
if (!number) {
|
||||
error = { "duration must be a number" };
|
||||
return {};
|
||||
}
|
||||
result.duration = { std::chrono::milliseconds(int64_t(*number)) };
|
||||
}
|
||||
|
||||
auto delay = objectMember(value, "delay");
|
||||
if (delay) {
|
||||
auto number = toNumber(*delay);
|
||||
if (!number) {
|
||||
error = { "delay must be a number" };
|
||||
return {};
|
||||
}
|
||||
result.delay = { std::chrono::milliseconds(int64_t(*number)) };
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace conversion
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
@@ -1,11 +1,13 @@
|
||||
#include <mbgl/style/parser.hpp>
|
||||
#include <mbgl/style/layer_impl.hpp>
|
||||
#include <mbgl/style/layers/symbol_layer.hpp>
|
||||
#include <mbgl/style/rapidjson_conversion.hpp>
|
||||
#include <mbgl/style/conversion.hpp>
|
||||
#include <mbgl/style/conversion/coordinate.hpp>
|
||||
#include <mbgl/style/conversion/source.hpp>
|
||||
#include <mbgl/style/conversion/layer.hpp>
|
||||
#include <mbgl/style/conversion/light.hpp>
|
||||
#include <mbgl/style/conversion/transition_options.hpp>
|
||||
|
||||
#include <mbgl/util/logging.hpp>
|
||||
#include <mbgl/util/string.hpp>
|
||||
@@ -149,7 +151,7 @@ void Parser::parseSources(const JSValue& value) {
|
||||
}
|
||||
|
||||
for (const auto& property : value.GetObject()) {
|
||||
std::string id = *conversion::toString(property.name);
|
||||
std::string id { property.name.GetString(), property.name.GetStringLength() };
|
||||
|
||||
conversion::Error error;
|
||||
optional<std::unique_ptr<Source>> source =
|
||||
@@ -256,7 +258,7 @@ void Parser::parseLayer(const std::string& id, const JSValue& value, std::unique
|
||||
}
|
||||
|
||||
layer = reference->cloneRef(id);
|
||||
conversion::setPaintProperties(*layer, value);
|
||||
conversion::setPaintProperties(*layer, conversion::Convertible(&value));
|
||||
} else {
|
||||
conversion::Error error;
|
||||
optional<std::unique_ptr<Layer>> converted = conversion::convert<std::unique_ptr<Layer>>(value, error);
|
||||
|
||||
@@ -1,103 +1,125 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/util/rapidjson.hpp>
|
||||
#include <mbgl/util/feature.hpp>
|
||||
#include <mbgl/style/conversion.hpp>
|
||||
|
||||
#include <mapbox/geojson.hpp>
|
||||
#include <mapbox/geojson/rapidjson.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
namespace conversion {
|
||||
|
||||
inline bool isUndefined(const JSValue& value) {
|
||||
return value.IsNull();
|
||||
}
|
||||
|
||||
inline bool isArray(const JSValue& value) {
|
||||
return value.IsArray();
|
||||
}
|
||||
|
||||
inline std::size_t arrayLength(const JSValue& value) {
|
||||
return value.Size();
|
||||
}
|
||||
|
||||
inline const JSValue& arrayMember(const JSValue& value, std::size_t i) {
|
||||
return value[rapidjson::SizeType(i)];
|
||||
}
|
||||
|
||||
inline bool isObject(const JSValue& value) {
|
||||
return value.IsObject();
|
||||
}
|
||||
|
||||
inline const JSValue* objectMember(const JSValue& value, const char * name) {
|
||||
if (!value.HasMember(name)) {
|
||||
return nullptr;
|
||||
template <>
|
||||
class ConversionTraits<const JSValue*> {
|
||||
public:
|
||||
static bool isUndefined(const JSValue* value) {
|
||||
return value->IsNull();
|
||||
}
|
||||
return &value[name];
|
||||
}
|
||||
|
||||
template <class Fn>
|
||||
optional<Error> eachMember(const JSValue& value, Fn&& fn) {
|
||||
assert(value.IsObject());
|
||||
for (const auto& property : value.GetObject()) {
|
||||
optional<Error> result =
|
||||
fn({ property.name.GetString(), property.name.GetStringLength() }, property.value);
|
||||
if (result) {
|
||||
return result;
|
||||
static bool isArray(const JSValue* value) {
|
||||
return value->IsArray();
|
||||
}
|
||||
|
||||
static std::size_t arrayLength(const JSValue* value) {
|
||||
return value->Size();
|
||||
}
|
||||
|
||||
static const JSValue* arrayMember(const JSValue* value, std::size_t i) {
|
||||
return &(*value)[rapidjson::SizeType(i)];
|
||||
}
|
||||
|
||||
static bool isObject(const JSValue* value) {
|
||||
return value->IsObject();
|
||||
}
|
||||
|
||||
static optional<const JSValue*> objectMember(const JSValue* value, const char * name) {
|
||||
if (!value->HasMember(name)) {
|
||||
return optional<const JSValue*>();
|
||||
}
|
||||
const JSValue* const& member = &(*value)[name];
|
||||
return {member};
|
||||
}
|
||||
|
||||
template <class Fn>
|
||||
static optional<Error> eachMember(const JSValue* value, Fn&& fn) {
|
||||
assert(value->IsObject());
|
||||
for (const auto& property : value->GetObject()) {
|
||||
optional<Error> result =
|
||||
fn({ property.name.GetString(), property.name.GetStringLength() }, &property.value);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
static optional<bool> toBool(const JSValue* value) {
|
||||
if (!value->IsBool()) {
|
||||
return {};
|
||||
}
|
||||
return value->GetBool();
|
||||
}
|
||||
|
||||
static optional<float> toNumber(const JSValue* value) {
|
||||
if (!value->IsNumber()) {
|
||||
return {};
|
||||
}
|
||||
return value->GetDouble();
|
||||
}
|
||||
|
||||
static optional<double> toDouble(const JSValue* value) {
|
||||
if (!value->IsNumber()) {
|
||||
return {};
|
||||
}
|
||||
return value->GetDouble();
|
||||
}
|
||||
|
||||
static optional<std::string> toString(const JSValue* value) {
|
||||
if (!value->IsString()) {
|
||||
return {};
|
||||
}
|
||||
return {{ value->GetString(), value->GetStringLength() }};
|
||||
}
|
||||
|
||||
static optional<Value> toValue(const JSValue* value) {
|
||||
switch (value->GetType()) {
|
||||
case rapidjson::kNullType:
|
||||
case rapidjson::kFalseType:
|
||||
return { false };
|
||||
|
||||
case rapidjson::kTrueType:
|
||||
return { true };
|
||||
|
||||
case rapidjson::kStringType:
|
||||
return { std::string { value->GetString(), value->GetStringLength() } };
|
||||
|
||||
case rapidjson::kNumberType:
|
||||
if (value->IsUint64()) return { value->GetUint64() };
|
||||
if (value->IsInt64()) return { value->GetInt64() };
|
||||
return { value->GetDouble() };
|
||||
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
inline optional<bool> toBool(const JSValue& value) {
|
||||
if (!value.IsBool()) {
|
||||
return {};
|
||||
}
|
||||
return value.GetBool();
|
||||
}
|
||||
|
||||
inline optional<float> toNumber(const JSValue& value) {
|
||||
if (!value.IsNumber()) {
|
||||
return {};
|
||||
}
|
||||
return value.GetDouble();
|
||||
}
|
||||
|
||||
inline optional<double> toDouble(const JSValue& value) {
|
||||
if (!value.IsNumber()) {
|
||||
return {};
|
||||
}
|
||||
return value.GetDouble();
|
||||
}
|
||||
|
||||
inline optional<std::string> toString(const JSValue& value) {
|
||||
if (!value.IsString()) {
|
||||
return {};
|
||||
}
|
||||
return {{ value.GetString(), value.GetStringLength() }};
|
||||
}
|
||||
|
||||
inline optional<Value> toValue(const JSValue& value) {
|
||||
switch (value.GetType()) {
|
||||
case rapidjson::kNullType:
|
||||
case rapidjson::kFalseType:
|
||||
return { false };
|
||||
|
||||
case rapidjson::kTrueType:
|
||||
return { true };
|
||||
|
||||
case rapidjson::kStringType:
|
||||
return { std::string { value.GetString(), value.GetStringLength() } };
|
||||
|
||||
case rapidjson::kNumberType:
|
||||
if (value.IsUint64()) return { value.GetUint64() };
|
||||
if (value.IsInt64()) return { value.GetInt64() };
|
||||
return { value.GetDouble() };
|
||||
|
||||
default:
|
||||
static optional<GeoJSON> toGeoJSON(const JSValue* value, Error& error) {
|
||||
try {
|
||||
return mapbox::geojson::convert(*value);
|
||||
} catch (const std::exception& ex) {
|
||||
error = { ex.what() };
|
||||
return {};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class...Args>
|
||||
optional<T> convert(const JSValue& value, Error& error, Args&&...args) {
|
||||
return convert<T>(Convertible(&value), error, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
} // namespace conversion
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/style/conversion.hpp>
|
||||
#include <mbgl/util/feature.hpp>
|
||||
#include <mbgl/util/optional.hpp>
|
||||
#include <mbgl/util/variant.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
namespace conversion {
|
||||
|
||||
class Value;
|
||||
using ValueMap = std::unordered_map<std::string, Value>;
|
||||
using ValueVector = std::vector<Value>;
|
||||
class Value : public mbgl::variant<std::string,
|
||||
float,
|
||||
double,
|
||||
bool,
|
||||
mapbox::util::recursive_wrapper<ValueMap>,
|
||||
mapbox::util::recursive_wrapper<ValueVector>> {
|
||||
using variant::variant;
|
||||
};
|
||||
|
||||
inline bool isUndefined(const Value&) {
|
||||
// Variant is always intialized
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool isArray(const Value& value) {
|
||||
return value.is<mapbox::util::recursive_wrapper<ValueVector>>();
|
||||
}
|
||||
|
||||
inline std::size_t arrayLength(const Value& value) {
|
||||
return value.get<mapbox::util::recursive_wrapper<ValueVector>>().get().size();
|
||||
}
|
||||
|
||||
inline Value arrayMember(const Value& value, std::size_t i) {
|
||||
return value.get<mapbox::util::recursive_wrapper<ValueVector>>().get()[i];
|
||||
}
|
||||
|
||||
inline bool isObject(const Value& value) {
|
||||
return value.is<mapbox::util::recursive_wrapper<ValueMap>>();
|
||||
}
|
||||
|
||||
inline optional<Value> objectMember(const Value& value, const char* key) {
|
||||
auto map = value.get<ValueMap>();
|
||||
auto iter = map.find(key);
|
||||
|
||||
if (iter != map.end()) {
|
||||
return iter->second;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
using EachMemberFn = std::function<optional<Error>(const std::string&, const Value&)>;
|
||||
|
||||
optional<Error> eachMember(const Value& value, EachMemberFn&& fn) {
|
||||
auto map = value.get<ValueMap>();
|
||||
auto iter = map.begin();
|
||||
|
||||
while (iter != map.end()) {
|
||||
optional<Error> result = fn(iter->first, iter->second);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
++iter;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
inline optional<bool> toBool(const Value& value) {
|
||||
if (value.is<bool>()) {
|
||||
return value.get<bool>();
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
inline optional<float> toNumber(const Value& value) {
|
||||
if (value.is<float>()) {
|
||||
return value.get<float>();
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
inline optional<double> toDouble(const Value& value) {
|
||||
if (value.is<double>()) {
|
||||
return value.get<double>();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
inline optional<std::string> toString(const Value& value) {
|
||||
if (value.is<std::string>()) {
|
||||
return value.get<std::string>();
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
inline optional<mbgl::Value> toValue(const Value& value) {
|
||||
if (value.is<bool>()) {
|
||||
return { value.get<bool>() };
|
||||
} else if (value.is<std::string>()) {
|
||||
return { value.get<std::string>() };
|
||||
} else if (value.is<float>()) {
|
||||
return { double(value.get<float>()) };
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace conversion
|
||||
} // namespace style
|
||||
} // namespace mbgl
|
||||
Reference in New Issue
Block a user