summaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
authorJohn Firebaugh <john.firebaugh@gmail.com>2016-06-10 21:17:58 -0700
committerJohn Firebaugh <john.firebaugh@gmail.com>2016-06-13 17:22:05 -0700
commit3f5ea169f5d617455b21467fbd12a812c6aa2972 (patch)
tree63163d4ba45f7a88b6c7bb6cc681d9eda16ec868 /platform
parent6cc2f8a3a52d0c375458c7da4fe51e8160e38a88 (diff)
downloadqtlocation-mapboxgl-3f5ea169f5d617455b21467fbd12a812c6aa2972.tar.gz
[node] Partial implementation of runtime styling API for node bindings
Diffstat (limited to 'platform')
-rw-r--r--platform/node/src/node_map.cpp76
-rw-r--r--platform/node/src/node_map.hpp3
-rw-r--r--platform/node/src/node_style.hpp126
-rw-r--r--platform/node/src/node_style_properties.hpp120
-rw-r--r--platform/node/src/node_style_properties.hpp.ejs35
-rw-r--r--platform/node/test/suite_implementation.js39
6 files changed, 391 insertions, 8 deletions
diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp
index e423a274c4..e629bb2149 100644
--- a/platform/node/src/node_map.cpp
+++ b/platform/node/src/node_map.cpp
@@ -1,9 +1,12 @@
#include "node_map.hpp"
#include "node_request.hpp"
#include "node_feature.hpp"
+#include "node_style_properties.hpp"
#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/util/exception.hpp>
+#include <mbgl/style/layer.hpp>
+#include <mbgl/style/layers/fill_layer.hpp>
#include <unistd.h>
@@ -48,8 +51,13 @@ NAN_MODULE_INIT(NodeMap::Init) {
tpl->SetClassName(Nan::New("Map").ToLocalChecked());
Nan::SetPrototypeMethod(tpl, "load", Load);
+ Nan::SetPrototypeMethod(tpl, "loaded", Loaded);
Nan::SetPrototypeMethod(tpl, "render", Render);
Nan::SetPrototypeMethod(tpl, "release", Release);
+
+ Nan::SetPrototypeMethod(tpl, "addClass", AddClass);
+ Nan::SetPrototypeMethod(tpl, "setPaintProperty", SetPaintProperty);
+
Nan::SetPrototypeMethod(tpl, "dumpDebugLogs", DumpDebugLogs);
Nan::SetPrototypeMethod(tpl, "queryRenderedFeatures", QueryRenderedFeatures);
@@ -197,6 +205,21 @@ NAN_METHOD(NodeMap::Load) {
info.GetReturnValue().SetUndefined();
}
+NAN_METHOD(NodeMap::Loaded) {
+ auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder());
+ if (!nodeMap->map) return Nan::ThrowError(releasedMessage());
+
+ bool loaded = false;
+
+ try {
+ loaded = nodeMap->map->isFullyLoaded();
+ } catch (const std::exception &ex) {
+ return Nan::ThrowError(ex.what());
+ }
+
+ info.GetReturnValue().Set(Nan::New(loaded));
+}
+
NodeMap::RenderOptions NodeMap::ParseOptions(v8::Local<v8::Object> obj) {
Nan::HandleScope scope;
@@ -436,6 +459,59 @@ void NodeMap::release() {
map.reset();
}
+NAN_METHOD(NodeMap::AddClass) {
+ auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder());
+ if (!nodeMap->map) return Nan::ThrowError(releasedMessage());
+
+ if (info.Length() <= 0 || !info[0]->IsString()) {
+ return Nan::ThrowTypeError("First argument must be a string");
+ }
+
+ try {
+ nodeMap->map->addClass(*Nan::Utf8String(info[0]));
+ } catch (const std::exception &ex) {
+ return Nan::ThrowError(ex.what());
+ }
+
+ info.GetReturnValue().SetUndefined();
+}
+
+NAN_METHOD(NodeMap::SetPaintProperty) {
+ auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder());
+ if (!nodeMap->map) return Nan::ThrowError(releasedMessage());
+
+ if (info.Length() < 3) {
+ return Nan::ThrowTypeError("Three arguments required");
+ }
+
+ if (!info[0]->IsString()) {
+ return Nan::ThrowTypeError("First argument must be a string");
+ }
+
+ mbgl::style::Layer* layer = nodeMap->map->getLayer(*Nan::Utf8String(info[0]));
+ if (!layer) {
+ return Nan::ThrowTypeError("layer not found");
+ }
+
+ if (!info[1]->IsString()) {
+ return Nan::ThrowTypeError("Second argument must be a string");
+ }
+
+ static const PropertySetters setters = makePaintPropertySetters();
+
+ auto it = setters.find(*Nan::Utf8String(info[1]));
+ if (it == setters.end()) {
+ return Nan::ThrowTypeError("property not found");
+ }
+
+ if (!it->second(*layer, info[2])) {
+ return;
+ }
+
+ nodeMap->map->update(mbgl::Update::RecalculateStyle);
+ info.GetReturnValue().SetUndefined();
+}
+
NAN_METHOD(NodeMap::DumpDebugLogs) {
auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder());
if (!nodeMap->map) return Nan::ThrowError(releasedMessage());
diff --git a/platform/node/src/node_map.hpp b/platform/node/src/node_map.hpp
index d3f7bd9e41..8dfb96eef8 100644
--- a/platform/node/src/node_map.hpp
+++ b/platform/node/src/node_map.hpp
@@ -24,8 +24,11 @@ public:
static NAN_METHOD(New);
static NAN_METHOD(Load);
+ static NAN_METHOD(Loaded);
static NAN_METHOD(Render);
static NAN_METHOD(Release);
+ static NAN_METHOD(AddClass);
+ static NAN_METHOD(SetPaintProperty);
static NAN_METHOD(DumpDebugLogs);
static NAN_METHOD(QueryRenderedFeatures);
diff --git a/platform/node/src/node_style.hpp b/platform/node/src/node_style.hpp
new file mode 100644
index 0000000000..b81d14c700
--- /dev/null
+++ b/platform/node/src/node_style.hpp
@@ -0,0 +1,126 @@
+#include <mbgl/style/property_value.hpp>
+#include <mbgl/style/layer.hpp>
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/enum.hpp>
+
+namespace node_mbgl {
+
+template <class V, class Enable = void>
+struct ValueConverter {};
+
+template <>
+struct ValueConverter<bool> {
+ mbgl::optional<mbgl::style::PropertyValue<bool>> operator()(const v8::Local<v8::Value>& value) const {
+ if (!value->IsBoolean()) {
+ Nan::ThrowTypeError("boolean required");
+ return {};
+ }
+
+ return { value->BooleanValue() };
+ }
+};
+
+template <>
+struct ValueConverter<float> {
+ mbgl::optional<mbgl::style::PropertyValue<float>> operator()(const v8::Local<v8::Value>& value) const {
+ if (!value->IsNumber()) {
+ Nan::ThrowTypeError("number required");
+ return {};
+ }
+
+ return { value->NumberValue() };
+ }
+};
+
+template <>
+struct ValueConverter<std::string> {
+ mbgl::optional<mbgl::style::PropertyValue<std::string>> operator()(const v8::Local<v8::Value>& value) const {
+ if (!value->IsString()) {
+ Nan::ThrowTypeError("string required");
+ return {};
+ }
+
+ return { std::string(*Nan::Utf8String(value)) };
+ }
+};
+
+template <typename T>
+struct ValueConverter<T, std::enable_if_t<std::is_enum<T>::value>> {
+ mbgl::optional<mbgl::style::PropertyValue<T>> operator()(const v8::Local<v8::Value>& value) const {
+ if (!value->IsString()) {
+ Nan::ThrowTypeError("string required");
+ return {};
+ }
+
+ mbgl::optional<T> result = mbgl::Enum<T>::toEnum(*Nan::Utf8String(value));
+ if (!result) {
+ Nan::ThrowTypeError("invalid enumeration value");
+ return {};
+ }
+
+ return { *result };
+ }
+};
+
+template <>
+struct ValueConverter<mbgl::Color> {
+ mbgl::optional<mbgl::style::PropertyValue<mbgl::Color>> operator()(const v8::Local<v8::Value>& value) const {
+ (void)value;
+ return {};
+ }
+};
+
+template <>
+struct ValueConverter<std::array<float, 2>> {
+ mbgl::optional<mbgl::style::PropertyValue<std::array<float, 2>>> operator()(const v8::Local<v8::Value>& value) const {
+ (void)value;
+ return {};
+ }
+};
+
+template <>
+struct ValueConverter<std::vector<float>> {
+ mbgl::optional<mbgl::style::PropertyValue<std::vector<float>>> operator()(const v8::Local<v8::Value>& value) const {
+ (void)value;
+ return {};
+ }
+};
+
+template <>
+struct ValueConverter<std::vector<std::string>> {
+ mbgl::optional<mbgl::style::PropertyValue<std::vector<std::string>>> operator()(const v8::Local<v8::Value>& value) const {
+ (void)value;
+ return {};
+ }
+};
+
+using PropertySetter = std::function<bool (mbgl::style::Layer&, const v8::Local<v8::Value>&)>;
+using PropertySetters = std::unordered_map<std::string, PropertySetter>;
+
+template <class L, class V>
+PropertySetter makePropertySetter(void (L::*setter)(mbgl::style::PropertyValue<V>)) {
+ return [setter] (mbgl::style::Layer& layer, const v8::Local<v8::Value>& value) {
+ L* typedLayer = layer.as<L>();
+ if (!typedLayer) {
+ Nan::ThrowTypeError("layer doesn't support this property");
+ return false;
+ }
+
+ mbgl::optional<mbgl::style::PropertyValue<V>> typedValue;
+
+ if (value->IsNull() || value->IsUndefined()) {
+ typedValue = mbgl::style::PropertyValue<V>();
+ } else {
+ typedValue = ValueConverter<V>()(value);
+ }
+
+ if (!typedValue) {
+ return false;
+ }
+
+ (typedLayer->*setter)(*typedValue);
+ return true;
+ };
+}
+
+}
diff --git a/platform/node/src/node_style_properties.hpp b/platform/node/src/node_style_properties.hpp
new file mode 100644
index 0000000000..4702918ae1
--- /dev/null
+++ b/platform/node/src/node_style_properties.hpp
@@ -0,0 +1,120 @@
+#include "node_style.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/raster_layer.hpp>
+#include <mbgl/style/layers/background_layer.hpp>
+
+namespace node_mbgl {
+
+inline PropertySetters makeLayoutPropertySetters() {
+ using namespace mbgl::style;
+ PropertySetters result;
+
+
+ result["line-cap"] = makePropertySetter(&LineLayer::setLineCap);
+ result["line-join"] = makePropertySetter(&LineLayer::setLineJoin);
+ result["line-miter-limit"] = makePropertySetter(&LineLayer::setLineMiterLimit);
+ result["line-round-limit"] = makePropertySetter(&LineLayer::setLineRoundLimit);
+
+ result["symbol-placement"] = makePropertySetter(&SymbolLayer::setSymbolPlacement);
+ result["symbol-spacing"] = makePropertySetter(&SymbolLayer::setSymbolSpacing);
+ result["symbol-avoid-edges"] = makePropertySetter(&SymbolLayer::setSymbolAvoidEdges);
+ result["icon-allow-overlap"] = makePropertySetter(&SymbolLayer::setIconAllowOverlap);
+ result["icon-ignore-placement"] = makePropertySetter(&SymbolLayer::setIconIgnorePlacement);
+ result["icon-optional"] = makePropertySetter(&SymbolLayer::setIconOptional);
+ result["icon-rotation-alignment"] = makePropertySetter(&SymbolLayer::setIconRotationAlignment);
+ result["icon-size"] = makePropertySetter(&SymbolLayer::setIconSize);
+ result["icon-image"] = makePropertySetter(&SymbolLayer::setIconImage);
+ result["icon-rotate"] = makePropertySetter(&SymbolLayer::setIconRotate);
+ result["icon-padding"] = makePropertySetter(&SymbolLayer::setIconPadding);
+ result["icon-keep-upright"] = makePropertySetter(&SymbolLayer::setIconKeepUpright);
+ result["icon-offset"] = makePropertySetter(&SymbolLayer::setIconOffset);
+ result["text-rotation-alignment"] = makePropertySetter(&SymbolLayer::setTextRotationAlignment);
+ result["text-field"] = makePropertySetter(&SymbolLayer::setTextField);
+ result["text-font"] = makePropertySetter(&SymbolLayer::setTextFont);
+ result["text-size"] = makePropertySetter(&SymbolLayer::setTextSize);
+ result["text-max-width"] = makePropertySetter(&SymbolLayer::setTextMaxWidth);
+ result["text-line-height"] = makePropertySetter(&SymbolLayer::setTextLineHeight);
+ result["text-letter-spacing"] = makePropertySetter(&SymbolLayer::setTextLetterSpacing);
+ result["text-justify"] = makePropertySetter(&SymbolLayer::setTextJustify);
+ result["text-anchor"] = makePropertySetter(&SymbolLayer::setTextAnchor);
+ result["text-max-angle"] = makePropertySetter(&SymbolLayer::setTextMaxAngle);
+ result["text-rotate"] = makePropertySetter(&SymbolLayer::setTextRotate);
+ result["text-padding"] = makePropertySetter(&SymbolLayer::setTextPadding);
+ result["text-keep-upright"] = makePropertySetter(&SymbolLayer::setTextKeepUpright);
+ result["text-transform"] = makePropertySetter(&SymbolLayer::setTextTransform);
+ result["text-offset"] = makePropertySetter(&SymbolLayer::setTextOffset);
+ result["text-allow-overlap"] = makePropertySetter(&SymbolLayer::setTextAllowOverlap);
+ result["text-ignore-placement"] = makePropertySetter(&SymbolLayer::setTextIgnorePlacement);
+ result["text-optional"] = makePropertySetter(&SymbolLayer::setTextOptional);
+
+
+
+
+ return result;
+}
+
+inline PropertySetters makePaintPropertySetters() {
+ using namespace mbgl::style;
+ PropertySetters result;
+
+ result["fill-antialias"] = makePropertySetter(&FillLayer::setFillAntialias);
+ result["fill-opacity"] = makePropertySetter(&FillLayer::setFillOpacity);
+ result["fill-color"] = makePropertySetter(&FillLayer::setFillColor);
+ result["fill-outline-color"] = makePropertySetter(&FillLayer::setFillOutlineColor);
+ result["fill-translate"] = makePropertySetter(&FillLayer::setFillTranslate);
+ result["fill-translate-anchor"] = makePropertySetter(&FillLayer::setFillTranslateAnchor);
+ result["fill-pattern"] = makePropertySetter(&FillLayer::setFillPattern);
+
+ result["line-opacity"] = makePropertySetter(&LineLayer::setLineOpacity);
+ result["line-color"] = makePropertySetter(&LineLayer::setLineColor);
+ result["line-translate"] = makePropertySetter(&LineLayer::setLineTranslate);
+ result["line-translate-anchor"] = makePropertySetter(&LineLayer::setLineTranslateAnchor);
+ result["line-width"] = makePropertySetter(&LineLayer::setLineWidth);
+ result["line-gap-width"] = makePropertySetter(&LineLayer::setLineGapWidth);
+ result["line-offset"] = makePropertySetter(&LineLayer::setLineOffset);
+ result["line-blur"] = makePropertySetter(&LineLayer::setLineBlur);
+ result["line-dasharray"] = makePropertySetter(&LineLayer::setLineDasharray);
+ result["line-pattern"] = makePropertySetter(&LineLayer::setLinePattern);
+
+ result["icon-opacity"] = makePropertySetter(&SymbolLayer::setIconOpacity);
+ result["icon-color"] = makePropertySetter(&SymbolLayer::setIconColor);
+ result["icon-halo-color"] = makePropertySetter(&SymbolLayer::setIconHaloColor);
+ result["icon-halo-width"] = makePropertySetter(&SymbolLayer::setIconHaloWidth);
+ result["icon-halo-blur"] = makePropertySetter(&SymbolLayer::setIconHaloBlur);
+ result["icon-translate"] = makePropertySetter(&SymbolLayer::setIconTranslate);
+ result["icon-translate-anchor"] = makePropertySetter(&SymbolLayer::setIconTranslateAnchor);
+ result["text-opacity"] = makePropertySetter(&SymbolLayer::setTextOpacity);
+ result["text-color"] = makePropertySetter(&SymbolLayer::setTextColor);
+ result["text-halo-color"] = makePropertySetter(&SymbolLayer::setTextHaloColor);
+ result["text-halo-width"] = makePropertySetter(&SymbolLayer::setTextHaloWidth);
+ result["text-halo-blur"] = makePropertySetter(&SymbolLayer::setTextHaloBlur);
+ result["text-translate"] = makePropertySetter(&SymbolLayer::setTextTranslate);
+ result["text-translate-anchor"] = makePropertySetter(&SymbolLayer::setTextTranslateAnchor);
+
+ result["circle-radius"] = makePropertySetter(&CircleLayer::setCircleRadius);
+ result["circle-color"] = makePropertySetter(&CircleLayer::setCircleColor);
+ result["circle-blur"] = makePropertySetter(&CircleLayer::setCircleBlur);
+ result["circle-opacity"] = makePropertySetter(&CircleLayer::setCircleOpacity);
+ result["circle-translate"] = makePropertySetter(&CircleLayer::setCircleTranslate);
+ result["circle-translate-anchor"] = makePropertySetter(&CircleLayer::setCircleTranslateAnchor);
+
+ result["raster-opacity"] = makePropertySetter(&RasterLayer::setRasterOpacity);
+ result["raster-hue-rotate"] = makePropertySetter(&RasterLayer::setRasterHueRotate);
+ result["raster-brightness-min"] = makePropertySetter(&RasterLayer::setRasterBrightnessMin);
+ result["raster-brightness-max"] = makePropertySetter(&RasterLayer::setRasterBrightnessMax);
+ result["raster-saturation"] = makePropertySetter(&RasterLayer::setRasterSaturation);
+ result["raster-contrast"] = makePropertySetter(&RasterLayer::setRasterContrast);
+ result["raster-fade-duration"] = makePropertySetter(&RasterLayer::setRasterFadeDuration);
+
+ result["background-color"] = makePropertySetter(&BackgroundLayer::setBackgroundColor);
+ result["background-pattern"] = makePropertySetter(&BackgroundLayer::setBackgroundPattern);
+ result["background-opacity"] = makePropertySetter(&BackgroundLayer::setBackgroundOpacity);
+
+ return result;
+}
+
+}
diff --git a/platform/node/src/node_style_properties.hpp.ejs b/platform/node/src/node_style_properties.hpp.ejs
new file mode 100644
index 0000000000..1937421fa5
--- /dev/null
+++ b/platform/node/src/node_style_properties.hpp.ejs
@@ -0,0 +1,35 @@
+#include "node_style.hpp"
+
+<% for (const layer of locals.layers) { -%>
+#include <mbgl/style/layers/<%- layer.type %>_layer.hpp>
+<% } -%>
+
+namespace node_mbgl {
+
+inline PropertySetters makeLayoutPropertySetters() {
+ using namespace mbgl::style;
+ PropertySetters result;
+
+<% for (const layer of locals.layers) { -%>
+<% for (const property of layer.layoutProperties) { -%>
+ result["<%- property.name %>"] = makePropertySetter(&<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>);
+<% } -%>
+
+<% } -%>
+ return result;
+}
+
+inline PropertySetters makePaintPropertySetters() {
+ using namespace mbgl::style;
+ PropertySetters result;
+
+<% for (const layer of locals.layers) { -%>
+<% for (const property of layer.paintProperties) { -%>
+ result["<%- property.name %>"] = makePropertySetter(&<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>);
+<% } -%>
+
+<% } -%>
+ return result;
+}
+
+}
diff --git a/platform/node/test/suite_implementation.js b/platform/node/test/suite_implementation.js
index da226a68f4..4369c81ffc 100644
--- a/platform/node/test/suite_implementation.js
+++ b/platform/node/test/suite_implementation.js
@@ -41,16 +41,39 @@ module.exports = function (style, options, callback) {
map.load(style);
- map.render(options, function (err, pixels) {
- var results = options.queryGeometry ?
- map.queryRenderedFeatures(options.queryGeometry) :
- [];
- map.release();
- if (timedOut) return;
- clearTimeout(watchdog);
- callback(err, pixels, results.map(prepareFeatures));
+ applyOperations(options.operations, function() {
+ map.render(options, function (err, pixels) {
+ var results = options.queryGeometry ?
+ map.queryRenderedFeatures(options.queryGeometry) :
+ [];
+ map.release();
+ if (timedOut) return;
+ clearTimeout(watchdog);
+ callback(err, pixels, results.map(prepareFeatures));
+ });
});
+ function applyOperations(operations, callback) {
+ var operation = operations && operations[0];
+ if (!operations || operations.length === 0) {
+ callback();
+
+ } else if (operation[0] === 'wait') {
+ var wait = function() {
+ if (map.loaded()) {
+ applyOperations(operations.slice(1), callback);
+ } else {
+ map.render(options, wait);
+ }
+ };
+ wait();
+
+ } else {
+ map[operation[0]].apply(map, operation.slice(1));
+ applyOperations(operations.slice(1), callback);
+ }
+ }
+
function prepareFeatures(r) {
delete r.layer;
return r;