#include "node_conversion.hpp" #include "node_expression.hpp" #include "node_feature.hpp" #include #include #include #include #include using namespace mbgl::style; using namespace mbgl::style::expression; namespace node_mbgl { Nan::Persistent NodeExpression::constructor; void NodeExpression::Init(v8::Local target) { v8::Local tpl = Nan::New(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::SetPrototypeMethod(tpl, "serialize", Serialize); 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 type) { static std::unordered_map types = { {"string", type::String}, {"number", type::Number}, {"noolean", type::Boolean}, {"object", type::Object}, {"color", type::Color}, {"value", type::Value} }; v8::Local 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 N; v8::Local 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& info) { v8::Local cons = Nan::New(constructor); if (info.Length() < 1 || info[0]->IsUndefined()) { return Nan::ThrowTypeError("Requires a JSON style expression argument."); } mbgl::optional 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 argv[0] = {}; auto wrapped = Nan::NewInstance(cons, argc, argv).ToLocalChecked(); nodeExpr->Wrap(wrapped); info.GetReturnValue().Set(wrapped); return; } v8::Local result = Nan::New(); for (std::size_t i = 0; i < ctx.getErrors().size(); i++) { const auto& error = ctx.getErrors()[i]; v8::Local err = Nan::New(); 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& 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 operator()(mbgl::NullValue) { Nan::EscapableHandleScope scope; return scope.Escape(Nan::Null()); } v8::Local operator()(bool t) { Nan::EscapableHandleScope scope; return scope.Escape(Nan::New(t)); } v8::Local operator()(double t) { Nan::EscapableHandleScope scope; return scope.Escape(Nan::New(t)); } v8::Local operator()(const std::string& t) { Nan::EscapableHandleScope scope; return scope.Escape(Nan::New(t).ToLocalChecked()); } v8::Local operator()(const std::vector& array) { Nan::EscapableHandleScope scope; v8::Local result = Nan::New(); for (unsigned int i = 0; i < array.size(); i++) { result->Set(i, toJS(array[i])); } return scope.Escape(result); } v8::Local operator()(const mbgl::Color& color) { return operator()(std::vector { static_cast(color.r), static_cast(color.g), static_cast(color.b), static_cast(color.a) }); } v8::Local operator()(const std::unordered_map& map) { Nan::EscapableHandleScope scope; v8::Local result = Nan::New(); for (const auto& entry : map) { Nan::Set(result, Nan::New(entry.first).ToLocalChecked(), toJS(entry.second)); } return scope.Escape(result); } }; v8::Local toJS(const Value& value) { return Value::visit(value, ToValue()); } void NodeExpression::Evaluate(const Nan::FunctionCallbackInfo& info) { NodeExpression* nodeExpr = ObjectWrap::Unwrap(info.Holder()); const std::unique_ptr& expression = nodeExpr->expression; if (info.Length() < 2 || !info[0]->IsObject()) { return Nan::ThrowTypeError("Requires globals and feature arguments."); } mbgl::optional zoom; v8::Local v8zoom = Nan::Get(info[0]->ToObject(), Nan::New("zoom").ToLocalChecked()).ToLocalChecked(); if (v8zoom->IsNumber()) zoom = v8zoom->NumberValue(); mbgl::optional heatmapDensity; v8::Local 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 geoJSON = conversion::convert(info[1], conversionError); if (!geoJSON) { Nan::ThrowTypeError(conversionError.message.c_str()); return; } try { mapbox::geojson::feature feature = geoJSON->get(); auto result = expression->evaluate(zoom, feature, heatmapDensity); if (result) { info.GetReturnValue().Set(toJS(*result)); } else { v8::Local res = Nan::New(); 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& info) { NodeExpression* nodeExpr = ObjectWrap::Unwrap(info.Holder()); const std::unique_ptr& 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& info) { NodeExpression* nodeExpr = ObjectWrap::Unwrap(info.Holder()); const std::unique_ptr& expression = nodeExpr->expression; info.GetReturnValue().Set(Nan::New(isFeatureConstant(*expression))); } void NodeExpression::IsZoomConstant(const Nan::FunctionCallbackInfo& info) { NodeExpression* nodeExpr = ObjectWrap::Unwrap(info.Holder()); const std::unique_ptr& expression = nodeExpr->expression; info.GetReturnValue().Set(Nan::New(isZoomConstant(*expression))); } void NodeExpression::Serialize(const Nan::FunctionCallbackInfo& info) { NodeExpression* nodeExpr = ObjectWrap::Unwrap(info.Holder()); const std::unique_ptr& expression = nodeExpr->expression; const mbgl::Value serialized = expression->serialize(); info.GetReturnValue().Set(toJS(serialized)); } } // namespace node_mbgl