Replace compile-time polymorphism with runtime polymorphism in the conversion system

This commit is contained in:
John Firebaugh
2017-09-26 14:14:44 -07:00
parent bb58ddaea8
commit 4b2e1cddb4
60 changed files with 2435 additions and 2175 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)...);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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)) {

View File

@@ -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

View File

@@ -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 {};

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)) {

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -3,6 +3,7 @@
#include <mbgl/util/range.hpp>
#include <mbgl/util/constants.hpp>
#include <tuple>
#include <vector>
#include <string>
#include <cstdint>

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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) };
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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 {

View File

@@ -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

View File

@@ -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

View File

@@ -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());
}

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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) => {

View 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

View 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

View 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

View File

@@ -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

View 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

View File

@@ -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

View 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

View 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

View 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

View 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

View 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

View File

@@ -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 {};

View 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

View 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

View 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

View File

@@ -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);

View File

@@ -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

View File

@@ -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