diff options
Diffstat (limited to 'platform/node/src/node_expression.cpp')
-rw-r--r-- | platform/node/src/node_expression.cpp | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/platform/node/src/node_expression.cpp b/platform/node/src/node_expression.cpp new file mode 100644 index 0000000000..8958d5c6c7 --- /dev/null +++ b/platform/node/src/node_expression.cpp @@ -0,0 +1,230 @@ +#include "node_conversion.hpp" +#include "node_expression.hpp" + +#include <mbgl/style/expression/parsing_context.hpp> +#include <mbgl/style/expression/is_constant.hpp> +#include <mbgl/style/conversion/geojson.hpp> +#include <mbgl/util/geojson.hpp> +#include <nan.h> + +using namespace mbgl::style; +using namespace mbgl::style::expression; + +namespace node_mbgl { + +Nan::Persistent<v8::Function> NodeExpression::constructor; + +void NodeExpression::Init(v8::Local<v8::Object> target) { + v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New); + tpl->SetClassName(Nan::New("Expression").ToLocalChecked()); + tpl->InstanceTemplate()->SetInternalFieldCount(1); // what is this doing? + + Nan::SetPrototypeMethod(tpl, "evaluate", Evaluate); + Nan::SetPrototypeMethod(tpl, "getType", GetType); + Nan::SetPrototypeMethod(tpl, "isFeatureConstant", IsFeatureConstant); + Nan::SetPrototypeMethod(tpl, "isZoomConstant", IsZoomConstant); + + Nan::SetMethod(tpl, "parse", Parse); + + constructor.Reset(tpl->GetFunction()); // what is this doing? + Nan::Set(target, Nan::New("Expression").ToLocalChecked(), tpl->GetFunction()); +} + +type::Type parseType(v8::Local<v8::Object> type) { + static std::unordered_map<std::string, type::Type> types = { + {"string", type::String}, + {"number", type::Number}, + {"noolean", type::Boolean}, + {"object", type::Object}, + {"color", type::Color}, + {"value", type::Value} + }; + + v8::Local<v8::Value> v8kind = Nan::Get(type, Nan::New("kind").ToLocalChecked()).ToLocalChecked(); + std::string kind(*v8::String::Utf8Value(v8kind)); + + if (kind == "array") { + type::Type itemType = parseType(Nan::Get(type, Nan::New("itemType").ToLocalChecked()).ToLocalChecked()->ToObject()); + mbgl::optional<std::size_t> N; + + v8::Local<v8::String> Nkey = Nan::New("N").ToLocalChecked(); + if (Nan::Has(type, Nkey).FromMaybe(false)) { + N = Nan::Get(type, Nkey).ToLocalChecked()->ToInt32()->Value(); + } + return type::Array(itemType, N); + } + + return types[kind]; +} + +void NodeExpression::Parse(const Nan::FunctionCallbackInfo<v8::Value>& info) { + v8::Local<v8::Function> cons = Nan::New(constructor); + + if (info.Length() < 1 || info[0]->IsUndefined()) { + return Nan::ThrowTypeError("Requires a JSON style expression argument."); + } + + mbgl::optional<type::Type> expected; + if (info.Length() > 1 && info[1]->IsObject()) { + expected = parseType(info[1]->ToObject()); + } + + auto expr = info[0]; + + try { + ParsingContext ctx(expected); + ParseResult parsed = ctx.parse(mbgl::style::conversion::Convertible(expr)); + if (parsed) { + assert(ctx.getErrors().size() == 0); + auto nodeExpr = new NodeExpression(std::move(*parsed)); + const int argc = 0; + v8::Local<v8::Value> argv[0] = {}; + auto wrapped = Nan::NewInstance(cons, argc, argv).ToLocalChecked(); + nodeExpr->Wrap(wrapped); + info.GetReturnValue().Set(wrapped); + return; + } + + v8::Local<v8::Array> result = Nan::New<v8::Array>(); + for (std::size_t i = 0; i < ctx.getErrors().size(); i++) { + const auto& error = ctx.getErrors()[i]; + v8::Local<v8::Object> err = Nan::New<v8::Object>(); + Nan::Set(err, + Nan::New("key").ToLocalChecked(), + Nan::New(error.key.c_str()).ToLocalChecked()); + Nan::Set(err, + Nan::New("error").ToLocalChecked(), + Nan::New(error.message.c_str()).ToLocalChecked()); + Nan::Set(result, Nan::New((uint32_t)i), err); + } + info.GetReturnValue().Set(result); + } catch(std::exception &ex) { + return Nan::ThrowError(ex.what()); + } +} + +void NodeExpression::New(const Nan::FunctionCallbackInfo<v8::Value>& info) { + if (!info.IsConstructCall()) { + return Nan::ThrowTypeError("Use the new operator to create new Expression objects"); + } + + info.GetReturnValue().Set(info.This()); +} + +struct ToValue { + v8::Local<v8::Value> operator()(mbgl::NullValue) { + Nan::EscapableHandleScope scope; + return scope.Escape(Nan::Null()); + } + + v8::Local<v8::Value> operator()(bool t) { + Nan::EscapableHandleScope scope; + return scope.Escape(Nan::New(t)); + } + + v8::Local<v8::Value> operator()(double t) { + Nan::EscapableHandleScope scope; + return scope.Escape(Nan::New(t)); + } + + v8::Local<v8::Value> operator()(const std::string& t) { + Nan::EscapableHandleScope scope; + return scope.Escape(Nan::New(t).ToLocalChecked()); + } + + v8::Local<v8::Value> operator()(const std::vector<Value>& array) { + Nan::EscapableHandleScope scope; + v8::Local<v8::Array> result = Nan::New<v8::Array>(); + for (unsigned int i = 0; i < array.size(); i++) { + result->Set(i, toJS(array[i])); + } + return scope.Escape(result); + } + + v8::Local<v8::Value> operator()(const mbgl::Color& color) { + return operator()(std::vector<Value> { + static_cast<double>(color.r), + static_cast<double>(color.g), + static_cast<double>(color.b), + static_cast<double>(color.a) + }); + } + + v8::Local<v8::Value> operator()(const std::unordered_map<std::string, Value>& map) { + Nan::EscapableHandleScope scope; + v8::Local<v8::Object> result = Nan::New<v8::Object>(); + for (const auto& entry : map) { + Nan::Set(result, Nan::New(entry.first).ToLocalChecked(), toJS(entry.second)); + } + + return scope.Escape(result); + } +}; + +v8::Local<v8::Value> toJS(const Value& value) { + return Value::visit(value, ToValue()); +} + +void NodeExpression::Evaluate(const Nan::FunctionCallbackInfo<v8::Value>& info) { + NodeExpression* nodeExpr = ObjectWrap::Unwrap<NodeExpression>(info.Holder()); + const std::unique_ptr<Expression>& expression = nodeExpr->expression; + + if (info.Length() < 2 || !info[0]->IsObject()) { + return Nan::ThrowTypeError("Requires globals and feature arguments."); + } + + mbgl::optional<float> zoom; + v8::Local<v8::Value> v8zoom = Nan::Get(info[0]->ToObject(), Nan::New("zoom").ToLocalChecked()).ToLocalChecked(); + if (v8zoom->IsNumber()) zoom = v8zoom->NumberValue(); + + mbgl::optional<double> heatmapDensity; + v8::Local<v8::Value> v8heatmapDensity = Nan::Get(info[0]->ToObject(), Nan::New("heatmapDensity").ToLocalChecked()).ToLocalChecked(); + if (v8heatmapDensity->IsNumber()) heatmapDensity = v8heatmapDensity->NumberValue(); + + Nan::JSON NanJSON; + conversion::Error conversionError; + mbgl::optional<mbgl::GeoJSON> geoJSON = conversion::convert<mbgl::GeoJSON>(info[1], conversionError); + if (!geoJSON) { + Nan::ThrowTypeError(conversionError.message.c_str()); + return; + } + + try { + mapbox::geojson::feature feature = geoJSON->get<mapbox::geojson::feature>(); + auto result = expression->evaluate(zoom, feature, heatmapDensity); + if (result) { + info.GetReturnValue().Set(toJS(*result)); + } else { + v8::Local<v8::Object> res = Nan::New<v8::Object>(); + Nan::Set(res, + Nan::New("error").ToLocalChecked(), + Nan::New(result.error().message.c_str()).ToLocalChecked()); + info.GetReturnValue().Set(res); + } + } catch(std::exception &ex) { + return Nan::ThrowTypeError(ex.what()); + } +} + +void NodeExpression::GetType(const Nan::FunctionCallbackInfo<v8::Value>& info) { + NodeExpression* nodeExpr = ObjectWrap::Unwrap<NodeExpression>(info.Holder()); + const std::unique_ptr<Expression>& expression = nodeExpr->expression; + + const type::Type type = expression->getType(); + const std::string name = type.match([&] (const auto& t) { return t.getName(); }); + info.GetReturnValue().Set(Nan::New(name.c_str()).ToLocalChecked()); +} + +void NodeExpression::IsFeatureConstant(const Nan::FunctionCallbackInfo<v8::Value>& info) { + NodeExpression* nodeExpr = ObjectWrap::Unwrap<NodeExpression>(info.Holder()); + const std::unique_ptr<Expression>& expression = nodeExpr->expression; + info.GetReturnValue().Set(Nan::New(isFeatureConstant(*expression))); +} + +void NodeExpression::IsZoomConstant(const Nan::FunctionCallbackInfo<v8::Value>& info) { + NodeExpression* nodeExpr = ObjectWrap::Unwrap<NodeExpression>(info.Holder()); + const std::unique_ptr<Expression>& expression = nodeExpr->expression; + info.GetReturnValue().Set(Nan::New(isZoomConstant(*expression))); +} + +} // namespace node_mbgl |