diff options
Diffstat (limited to 'platform/node/src')
-rw-r--r-- | platform/node/src/node_conversion.hpp | 183 | ||||
-rw-r--r-- | platform/node/src/node_expression.cpp | 241 | ||||
-rw-r--r-- | platform/node/src/node_expression.hpp | 43 | ||||
-rw-r--r-- | platform/node/src/node_geojson.hpp | 17 | ||||
-rw-r--r-- | platform/node/src/node_map.cpp | 167 | ||||
-rw-r--r-- | platform/node/src/node_map.hpp | 5 | ||||
-rw-r--r-- | platform/node/src/node_mapbox_gl_native.cpp | 2 |
7 files changed, 551 insertions, 107 deletions
diff --git a/platform/node/src/node_conversion.hpp b/platform/node/src/node_conversion.hpp index d266745548..7c5bbf4386 100644 --- a/platform/node/src/node_conversion.hpp +++ b/platform/node/src/node_conversion.hpp @@ -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()) { - return {}; + 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())}; } - Nan::MaybeLocal<v8::Value> result = Nan::Get(Nan::To<v8::Object>(value).ToLocalChecked(), Nan::New(name).ToLocalChecked()); - if (result.IsEmpty()) { + + 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 {}; } - 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(); } - return {}; -} -inline optional<bool> toBool(v8::Local<v8::Value> value) { - Nan::HandleScope scope; - if (!value->IsBoolean()) { - return {}; + static optional<float> toNumber(const v8::Local<v8::Value>& value) { + Nan::HandleScope scope; + if (!value->IsNumber()) { + return {}; + } + return value->NumberValue(); } - return value->BooleanValue(); -} -inline optional<float> toNumber(v8::Local<v8::Value> value) { - Nan::HandleScope scope; - if (!value->IsNumber()) { - return {}; + static optional<double> toDouble(const v8::Local<v8::Value>& value) { + Nan::HandleScope scope; + if (!value->IsNumber()) { + return {}; + } + return value->NumberValue(); } - return value->NumberValue(); -} -inline optional<double> toDouble(v8::Local<v8::Value> value) { - Nan::HandleScope scope; - if (!value->IsNumber()) { - return {}; + static optional<std::string> toString(const v8::Local<v8::Value>& value) { + Nan::HandleScope scope; + if (!value->IsString()) { + return {}; + } + return std::string(*Nan::Utf8String(value)); } - return value->NumberValue(); -} -inline optional<std::string> toString(v8::Local<v8::Value> value) { - Nan::HandleScope scope; - if (!value->IsString()) { - return {}; + 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 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 {}; + 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 {}; + } } +}; + +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 diff --git a/platform/node/src/node_expression.cpp b/platform/node/src/node_expression.cpp new file mode 100644 index 0000000000..84515060a3 --- /dev/null +++ b/platform/node/src/node_expression.cpp @@ -0,0 +1,241 @@ +#include "node_conversion.hpp" +#include "node_expression.hpp" +#include "node_feature.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::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<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))); +} + +void NodeExpression::Serialize(const Nan::FunctionCallbackInfo<v8::Value>& info) { + NodeExpression* nodeExpr = ObjectWrap::Unwrap<NodeExpression>(info.Holder()); + const std::unique_ptr<Expression>& expression = nodeExpr->expression; + + const mbgl::Value serialized = expression->serialize(); + info.GetReturnValue().Set(toJS(serialized)); +} + +} // namespace node_mbgl diff --git a/platform/node/src/node_expression.hpp b/platform/node/src/node_expression.hpp new file mode 100644 index 0000000000..05af217bde --- /dev/null +++ b/platform/node/src/node_expression.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include <mbgl/style/conversion.hpp> +#include <mbgl/style/expression/expression.hpp> +#include <exception> +#include <memory> + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Wshadow" +#include <nan.h> +#pragma GCC diagnostic pop + +using namespace mbgl::style::expression; + +namespace node_mbgl { + +v8::Local<v8::Value> toJS(const Value&); + +class NodeExpression : public Nan::ObjectWrap { +public: + static void Init(v8::Local<v8::Object>); + +private: + NodeExpression(std::unique_ptr<Expression> expression_) : + expression(std::move(expression_)) + {}; + + static void New(const Nan::FunctionCallbackInfo<v8::Value>&); + static void Parse(const Nan::FunctionCallbackInfo<v8::Value>&); + static void Evaluate(const Nan::FunctionCallbackInfo<v8::Value>&); + static void GetType(const Nan::FunctionCallbackInfo<v8::Value>&); + static void IsFeatureConstant(const Nan::FunctionCallbackInfo<v8::Value>&); + static void IsZoomConstant(const Nan::FunctionCallbackInfo<v8::Value>&); + + static void Serialize(const Nan::FunctionCallbackInfo<v8::Value>&); + + static Nan::Persistent<v8::Function> constructor; + + std::unique_ptr<Expression> expression; +}; + +} // namespace node_mbgl diff --git a/platform/node/src/node_geojson.hpp b/platform/node/src/node_geojson.hpp deleted file mode 100644 index 8a3927e2cf..0000000000 --- a/platform/node/src/node_geojson.hpp +++ /dev/null @@ -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 diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp index 9a7ebce445..ac14df0228 100644 --- a/platform/node/src/node_map.cpp +++ b/platform/node/src/node_map.cpp @@ -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,8 +9,21 @@ #include <mbgl/style/conversion/source.hpp> #include <mbgl/style/conversion/layer.hpp> #include <mbgl/style/conversion/filter.hpp> +#include <mbgl/style/conversion/light.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/heatmap_layer.hpp> +#include <mbgl/style/layers/hillshade_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/style/light.hpp> #include <mbgl/map/map_observer.hpp> #include <mbgl/util/premultiply.hpp> @@ -22,10 +34,14 @@ namespace node_mbgl { struct NodeMap::RenderOptions { double zoom = 0; double bearing = 0; + mbgl::style::Light light; double pitch = 0; double latitude = 0; double longitude = 0; mbgl::Size size = { 512, 512 }; + bool axonometric = false; + double xSkew = 0; + double ySkew = 1; std::vector<std::string> classes; mbgl::MapDebugOptions debugOptions = mbgl::MapDebugOptions::NoDebug; }; @@ -65,6 +81,10 @@ void NodeMap::Init(v8::Local<v8::Object> target) { Nan::SetPrototypeMethod(tpl, "setZoom", SetZoom); Nan::SetPrototypeMethod(tpl, "setBearing", SetBearing); Nan::SetPrototypeMethod(tpl, "setPitch", SetPitch); + Nan::SetPrototypeMethod(tpl, "setLight", SetLight); + Nan::SetPrototypeMethod(tpl, "setAxonometric", SetAxonometric); + Nan::SetPrototypeMethod(tpl, "setXSkew", SetXSkew); + Nan::SetPrototypeMethod(tpl, "setYSkew", SetYSkew); Nan::SetPrototypeMethod(tpl, "dumpDebugLogs", DumpDebugLogs); Nan::SetPrototypeMethod(tpl, "queryRenderedFeatures", QueryRenderedFeatures); @@ -251,6 +271,29 @@ NodeMap::RenderOptions NodeMap::ParseOptions(v8::Local<v8::Object> obj) { options.pitch = Nan::Get(obj, Nan::New("pitch").ToLocalChecked()).ToLocalChecked()->NumberValue(); } + if (Nan::Has(obj, Nan::New("light").ToLocalChecked()).FromJust()) { + auto lightObj = Nan::Get(obj, Nan::New("light").ToLocalChecked()).ToLocalChecked(); + mbgl::style::conversion::Error conversionError; + if (auto light = mbgl::style::conversion::convert<mbgl::style::Light>(lightObj, conversionError)) { + options.light = *light; + } else { + throw conversionError; + } + } + + if (Nan::Has(obj, Nan::New("axonometric").ToLocalChecked()).FromJust()) { + options.axonometric = Nan::Get(obj, Nan::New("axonometric").ToLocalChecked()).ToLocalChecked()->BooleanValue(); + } + + if (Nan::Has(obj, Nan::New("skew").ToLocalChecked()).FromJust()) { + auto skewObj = Nan::Get(obj, Nan::New("skew").ToLocalChecked()).ToLocalChecked(); + if (skewObj->IsArray()) { + auto skew = skewObj.As<v8::Array>(); + if (skew->Length() > 0) { options.xSkew = Nan::Get(skew, 0).ToLocalChecked()->NumberValue(); } + if (skew->Length() > 1) { options.ySkew = Nan::Get(skew, 1).ToLocalChecked()->NumberValue(); } + } + } + if (Nan::Has(obj, Nan::New("center").ToLocalChecked()).FromJust()) { auto centerObj = Nan::Get(obj, Nan::New("center").ToLocalChecked()).ToLocalChecked(); if (centerObj->IsArray()) { @@ -345,14 +388,14 @@ void NodeMap::Render(const Nan::FunctionCallbackInfo<v8::Value>& info) { return Nan::ThrowError("Map is currently rendering an image"); } - auto options = ParseOptions(Nan::To<v8::Object>(info[0]).ToLocalChecked()); - - assert(!nodeMap->callback); - assert(!nodeMap->image.data); - nodeMap->callback = std::make_unique<Nan::Callback>(info[1].As<v8::Function>()); - try { + auto options = ParseOptions(Nan::To<v8::Object>(info[0]).ToLocalChecked()); + assert(!nodeMap->callback); + assert(!nodeMap->image.data); + nodeMap->callback = std::make_unique<Nan::Callback>(info[1].As<v8::Function>()); nodeMap->startRender(std::move(options)); + } catch (mbgl::style::conversion::Error& err) { + return Nan::ThrowTypeError(err.message.c_str()); } catch (mbgl::util::Exception &ex) { return Nan::ThrowError(ex.what()); } @@ -370,6 +413,18 @@ void NodeMap::startRender(NodeMap::RenderOptions options) { camera.angle = -options.bearing * mbgl::util::DEG2RAD; camera.pitch = options.pitch * mbgl::util::DEG2RAD; + if (map->getAxonometric() != options.axonometric) { + map->setAxonometric(options.axonometric); + } + + if (map->getXSkew() != options.xSkew) { + map->setXSkew(options.xSkew); + } + + if (map->getYSkew() != options.ySkew) { + map->setYSkew(options.ySkew); + } + map->renderStill(camera, options.debugOptions, [this](const std::exception_ptr eptr) { if (eptr) { error = std::move(eptr); @@ -477,6 +532,7 @@ void NodeMap::release() { }); map.reset(); + frontend.reset(); } /** @@ -508,7 +564,7 @@ void NodeMap::cancel() { frontend = std::make_unique<mbgl::HeadlessFrontend>(mbgl::Size{ 256, 256 }, pixelRatio, *this, threadpool); map = std::make_unique<mbgl::Map>(*frontend, mapObserver, frontend->getSize(), pixelRatio, - *this, threadpool, mbgl::MapMode::Still); + *this, threadpool, mode); // FIXME: Reload the style after recreating the map. We need to find // a better way of canceling an ongoing rendering on the core level @@ -709,7 +765,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()); } @@ -741,7 +797,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()); } @@ -760,6 +816,10 @@ struct SetFilterVisitor { Nan::ThrowTypeError("layer doesn't support filters"); } + void operator()(mbgl::style::HillshadeLayer&) { + Nan::ThrowTypeError("layer doesn't support filters"); + } + void operator()(mbgl::style::BackgroundLayer&) { Nan::ThrowTypeError("layer doesn't support filters"); } @@ -879,6 +939,82 @@ void NodeMap::SetPitch(const Nan::FunctionCallbackInfo<v8::Value>& info) { info.GetReturnValue().SetUndefined(); } +void NodeMap::SetLight(const Nan::FunctionCallbackInfo<v8::Value>& info) { + using namespace mbgl::style; + using namespace mbgl::style::conversion; + + auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder()); + if (!nodeMap->map) return Nan::ThrowError(releasedMessage()); + + if (info.Length() <= 0 || !info[0]->IsObject()) { + return Nan::ThrowTypeError("First argument must be an object"); + } + + try { + Error conversionError; + if (auto light = convert<mbgl::style::Light>(info[0], conversionError)) { + nodeMap->map->getStyle().setLight(std::make_unique<Light>(*light)); + } else { + return Nan::ThrowTypeError(conversionError.message.c_str()); + } + } catch (const std::exception &ex) { + return Nan::ThrowError(ex.what()); + } + + info.GetReturnValue().SetUndefined(); +} + +void NodeMap::SetAxonometric(const Nan::FunctionCallbackInfo<v8::Value>& info) { + auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder()); + if (!nodeMap->map) return Nan::ThrowError(releasedMessage()); + + if (info.Length() <= 0 || !info[0]->IsBoolean()) { + return Nan::ThrowTypeError("First argument must be a boolean"); + } + + try { + nodeMap->map->setAxonometric(info[0]->BooleanValue()); + } catch (const std::exception &ex) { + return Nan::ThrowError(ex.what()); + } + + info.GetReturnValue().SetUndefined(); +} + +void NodeMap::SetXSkew(const Nan::FunctionCallbackInfo<v8::Value>& info) { + auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder()); + if (!nodeMap->map) return Nan::ThrowError(releasedMessage()); + + if (info.Length() <= 0 || !info[0]->IsNumber()) { + return Nan::ThrowTypeError("First argument must be a number"); + } + + try { + nodeMap->map->setXSkew(info[0]->NumberValue()); + } catch (const std::exception &ex) { + return Nan::ThrowError(ex.what()); + } + + info.GetReturnValue().SetUndefined(); +} + +void NodeMap::SetYSkew(const Nan::FunctionCallbackInfo<v8::Value>& info) { + auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder()); + if (!nodeMap->map) return Nan::ThrowError(releasedMessage()); + + if (info.Length() <= 0 || !info[0]->IsNumber()) { + return Nan::ThrowTypeError("First argument must be a number"); + } + + try { + nodeMap->map->setYSkew(info[0]->NumberValue()); + } catch (const std::exception &ex) { + return Nan::ThrowError(ex.what()); + } + + info.GetReturnValue().SetUndefined(); +} + void NodeMap::DumpDebugLogs(const Nan::FunctionCallbackInfo<v8::Value>& info) { auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder()); if (!nodeMap->map) return Nan::ThrowError(releasedMessage()); @@ -983,6 +1119,15 @@ NodeMap::NodeMap(v8::Local<v8::Object> options) ->NumberValue() : 1.0; }()) + , mode([&] { + Nan::HandleScope scope; + if (Nan::Has(options, Nan::New("mode").ToLocalChecked()).FromJust() && + std::string(*v8::String::Utf8Value(Nan::Get(options, Nan::New("mode").ToLocalChecked()).ToLocalChecked()->ToString())) == "tile") { + return mbgl::MapMode::Tile; + } else { + return mbgl::MapMode::Static; + } + }()) , mapObserver(NodeMapObserver()) , frontend(std::make_unique<mbgl::HeadlessFrontend>(mbgl::Size { 256, 256 }, pixelRatio, *this, threadpool)) , map(std::make_unique<mbgl::Map>(*frontend, @@ -991,7 +1136,7 @@ NodeMap::NodeMap(v8::Local<v8::Object> options) pixelRatio, *this, threadpool, - mbgl::MapMode::Still)), + mode)), async(new uv_async_t) { async->data = this; diff --git a/platform/node/src/node_map.hpp b/platform/node/src/node_map.hpp index c8e33bb347..efa6f6cee0 100644 --- a/platform/node/src/node_map.hpp +++ b/platform/node/src/node_map.hpp @@ -57,6 +57,10 @@ public: static void SetZoom(const Nan::FunctionCallbackInfo<v8::Value>&); static void SetBearing(const Nan::FunctionCallbackInfo<v8::Value>&); static void SetPitch(const Nan::FunctionCallbackInfo<v8::Value>&); + static void SetLight(const Nan::FunctionCallbackInfo<v8::Value>&); + static void SetAxonometric(const Nan::FunctionCallbackInfo<v8::Value>&); + static void SetXSkew(const Nan::FunctionCallbackInfo<v8::Value>&); + static void SetYSkew(const Nan::FunctionCallbackInfo<v8::Value>&); static void DumpDebugLogs(const Nan::FunctionCallbackInfo<v8::Value>&); static void QueryRenderedFeatures(const Nan::FunctionCallbackInfo<v8::Value>&); @@ -71,6 +75,7 @@ public: std::unique_ptr<mbgl::AsyncRequest> request(const mbgl::Resource&, mbgl::FileSource::Callback); const float pixelRatio; + mbgl::MapMode mode; NodeThreadPool threadpool; NodeMapObserver mapObserver; std::unique_ptr<mbgl::HeadlessFrontend> frontend; diff --git a/platform/node/src/node_mapbox_gl_native.cpp b/platform/node/src/node_mapbox_gl_native.cpp index cdcc982220..96e96e4298 100644 --- a/platform/node/src/node_mapbox_gl_native.cpp +++ b/platform/node/src/node_mapbox_gl_native.cpp @@ -10,6 +10,7 @@ #include "node_map.hpp" #include "node_logging.hpp" #include "node_request.hpp" +#include "node_expression.hpp" void RegisterModule(v8::Local<v8::Object> target, v8::Local<v8::Object> module) { // This has the effect of: @@ -20,6 +21,7 @@ void RegisterModule(v8::Local<v8::Object> target, v8::Local<v8::Object> module) node_mbgl::NodeMap::Init(target); node_mbgl::NodeRequest::Init(); + node_mbgl::NodeExpression::Init(target); // Exports Resource constants. v8::Local<v8::Object> resource = Nan::New<v8::Object>(); |