diff options
author | Anand Thakker <github@anandthakker.net> | 2017-07-06 09:37:59 -0400 |
---|---|---|
committer | Anand Thakker <github@anandthakker.net> | 2017-07-06 17:02:12 -0400 |
commit | 25c5c3bbb527509b4b840005510cbb0d85208b40 (patch) | |
tree | 2638b7602061d0172f3ae24bd90f311d40f74735 | |
parent | 9db5832d362fb7f1930d4b182a3fe37af492a9a0 (diff) | |
download | qtlocation-mapboxgl-25c5c3bbb527509b4b840005510cbb0d85208b40.tar.gz |
Expose Expression in node and hook up integration test
-rw-r--r-- | cmake/node.cmake | 2 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | platform/node/src/node_expression.cpp | 126 | ||||
-rw-r--r-- | platform/node/src/node_expression.hpp | 36 | ||||
-rw-r--r-- | platform/node/src/node_mapbox_gl_native.cpp | 2 | ||||
-rw-r--r-- | platform/node/test/expression.test.js | 52 |
6 files changed, 219 insertions, 1 deletions
diff --git a/cmake/node.cmake b/cmake/node.cmake index 502edd8293..add66e7fbb 100644 --- a/cmake/node.cmake +++ b/cmake/node.cmake @@ -21,6 +21,8 @@ target_sources(mbgl-node PRIVATE platform/node/src/node_feature.cpp PRIVATE platform/node/src/node_thread_pool.hpp PRIVATE platform/node/src/node_thread_pool.cpp + PRIVATE platform/node/src/node_expression.hpp + PRIVATE platform/node/src/node_expression.cpp PRIVATE platform/node/src/util/async_queue.hpp ) diff --git a/package.json b/package.json index e428a0db43..cdf09636c7 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "license": "BSD-2-Clause", "dependencies": { "node-pre-gyp": "^0.6.28", - "nan": "^2.4.0" + "nan": "^2.6.2" }, "devDependencies": { "aws-sdk": "^2.3.5", diff --git a/platform/node/src/node_expression.cpp b/platform/node/src/node_expression.cpp new file mode 100644 index 0000000000..51a9fca5ef --- /dev/null +++ b/platform/node/src/node_expression.cpp @@ -0,0 +1,126 @@ +#include "node_expression.hpp" +#include "node_conversion.hpp" + +#include <mbgl/style/conversion/geojson.hpp> +#include <mbgl/style/conversion/expression.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); + + constructor.Reset(tpl->GetFunction()); // what is this doing? + Nan::Set(target, Nan::New("Expression").ToLocalChecked(), tpl->GetFunction()); +} + +void NodeExpression::New(const Nan::FunctionCallbackInfo<v8::Value>& info) { + if (!info.IsConstructCall()) { + return Nan::ThrowTypeError("Use the new operator to create new Expression objects"); + } + + if (info.Length() < 1 || info[0]->IsUndefined()) { + return Nan::ThrowTypeError("Requires a JSON style expression argument."); + } + + auto expr = info[0]; + + try { + conversion::Error error; + auto expression = conversion::convert<std::unique_ptr<Expression>>(expr, error); + if (!expression) { + return Nan::ThrowTypeError(error.message.c_str()); + } + auto nodeExpr = new NodeExpression(std::move(*expression)); + nodeExpr->Wrap(info.This()); + } catch(std::exception &ex) { + return Nan::ThrowError(ex.what()); + } + + info.GetReturnValue().Set(info.This()); +} + +void NodeExpression::Evaluate(const Nan::FunctionCallbackInfo<v8::Value>& info) { + NodeExpression* nodeExpr = ObjectWrap::Unwrap<NodeExpression>(info.Holder()); + + if (info.Length() < 2) { + return Nan::ThrowTypeError("Requires arguments zoom and feature arguments."); + } + + float zoom = info[0]->NumberValue(); + + // Pending https://github.com/mapbox/mapbox-gl-native/issues/5623, + // stringify the geojson feature in order to use convert<GeoJSON, string>() + Nan::JSON NanJSON; + Nan::MaybeLocal<v8::String> geojsonString = NanJSON.Stringify(Nan::To<v8::Object>(info[1]).ToLocalChecked()); + if (geojsonString.IsEmpty()) { + return Nan::ThrowTypeError("couldn't stringify JSON"); + } + conversion::Error conversionError; + mbgl::optional<mbgl::GeoJSON> geoJSON = conversion::convert<mbgl::GeoJSON, std::string>(*Nan::Utf8String(geojsonString.ToLocalChecked()), conversionError); + if (!geoJSON) { + Nan::ThrowTypeError(conversionError.message.c_str()); + return; + } + + try { + mapbox::geojson::feature feature = geoJSON->get<mapbox::geojson::feature>(); + Error error; + auto result = nodeExpr->expression->evaluate(zoom, feature, error); + if (result) { + result->match( + [&] (const std::array<float, 2>&) {}, + [&] (const std::array<float, 4>&) {}, + [&] (const std::string s) { + info.GetReturnValue().Set(Nan::New(s.c_str()).ToLocalChecked()); + }, + [&] (const mbgl::Color& c) { + info.GetReturnValue().Set(Nan::New(c.stringify().c_str()).ToLocalChecked()); + }, + [&] (const auto& v) { + info.GetReturnValue().Set(Nan::New(v)); + } + ); + } else { + v8::Local<v8::Object> res = Nan::New<v8::Object>(); + Nan::Set(res, + Nan::New("error").ToLocalChecked(), + Nan::New(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 auto& type = nodeExpr->expression->getType(); + const auto& 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()); + info.GetReturnValue().Set(Nan::New(nodeExpr->expression->isFeatureConstant())); +} + +void NodeExpression::IsZoomConstant(const Nan::FunctionCallbackInfo<v8::Value>& info) { + NodeExpression* nodeExpr = ObjectWrap::Unwrap<NodeExpression>(info.Holder()); + info.GetReturnValue().Set(Nan::New(nodeExpr->expression->isZoomConstant())); +} + +} // 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..8913bead1b --- /dev/null +++ b/platform/node/src/node_expression.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include <exception> +#include <memory> +#include <mbgl/style/function/expression.hpp> + +#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 { + +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 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 Nan::Persistent<v8::Function> constructor; + + std::unique_ptr<Expression> expression; +}; + +} // namespace node_mbgl 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>(); diff --git a/platform/node/test/expression.test.js b/platform/node/test/expression.test.js new file mode 100644 index 0000000000..c243ba1e0e --- /dev/null +++ b/platform/node/test/expression.test.js @@ -0,0 +1,52 @@ +'use strict'; + +var suite = require('../../../mapbox-gl-js/test/integration').expression; +var mbgl = require('../index'); + +var tests; + +if (process.argv[1] === __filename && process.argv.length > 2) { + tests = process.argv.slice(2); +} + +suite.run('native', {tests: tests}, (fixture) => { + const compileResult = {}; + const testResult = { + compileResult + }; + + try { + const expression = new mbgl.Expression(fixture.expression); + compileResult.result = 'success'; + compileResult.isFeatureConstant = expression.isFeatureConstant(); + compileResult.isZoomConstant = expression.isZoomConstant(); + compileResult.type = expression.getType(); + + if (fixture.evaluate) { + const evaluateResults = []; + for (const input of fixture.evaluate) { + const zoom = typeof input[0].zoom === 'number' ? + input[0].zoom : -1; + + const feature = Object.assign({ + type: 'Feature', + properties: {}, + geometry: { type: 'Unknown', coordinates: [] } + }, input[1]) + + const output = expression.evaluate(zoom, feature); + evaluateResults.push(output); + } + + if (evaluateResults.length) { + testResult.evaluateResults = evaluateResults; + } + } + } catch (e) { + compileResult.result = 'error'; + compileResult.errors = [e.message]; + } + + return testResult; +}); + |