Node platform improvements (#891)
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
- *...Add new stuff here...*
|
||||
|
||||
# 5.2.0
|
||||
* Node platform improvements (added setSize and a new render call without render options object) by @tdcosta100 in https://github.com/maplibre/maplibre-gl-native/pull/891
|
||||
* Move node ci+release to self hosted Ubuntu arm64 by @acalcutt in https://github.com/maplibre/maplibre-gl-native/pull/873
|
||||
* Add windows support by @tdcosta100 in https://github.com/maplibre/maplibre-gl-native/pull/707
|
||||
* Add Typings for Node Platform by @KiwiKilian in https://github.com/maplibre/maplibre-gl-native/pull/766
|
||||
|
||||
@@ -173,6 +173,8 @@ var map = new mbgl.Map({
|
||||
response.data = body;
|
||||
|
||||
callback(null, response);
|
||||
} else if (res.statusCode == 204) {
|
||||
callback();
|
||||
} else {
|
||||
callback(new Error(JSON.parse(body).message));
|
||||
}
|
||||
|
||||
@@ -102,6 +102,7 @@ ParseError)JS").ToLocalChecked()).ToLocalChecked();
|
||||
Nan::SetPrototypeMethod(tpl, "setLayoutProperty", SetLayerProperty);
|
||||
Nan::SetPrototypeMethod(tpl, "setPaintProperty", SetLayerProperty);
|
||||
Nan::SetPrototypeMethod(tpl, "setFilter", SetFilter);
|
||||
Nan::SetPrototypeMethod(tpl, "setSize", SetSize);
|
||||
Nan::SetPrototypeMethod(tpl, "setCenter", SetCenter);
|
||||
Nan::SetPrototypeMethod(tpl, "setZoom", SetZoom);
|
||||
Nan::SetPrototypeMethod(tpl, "setBearing", SetBearing);
|
||||
@@ -426,11 +427,11 @@ void NodeMap::Render(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]->IsObject()) {
|
||||
return Nan::ThrowTypeError("First argument must be an options object");
|
||||
if (info.Length() <= 0 || (!info[0]->IsObject() && !info[0]->IsFunction())) {
|
||||
return Nan::ThrowTypeError("First argument must be an options object or a callback function");
|
||||
}
|
||||
|
||||
if (info.Length() <= 1 || !info[1]->IsFunction()) {
|
||||
if ((info.Length() <= 1 && !info[0]->IsFunction()) || (info.Length() > 1 && !info[1]->IsFunction())) {
|
||||
return Nan::ThrowTypeError("Second argument must be a callback function");
|
||||
}
|
||||
|
||||
@@ -443,12 +444,20 @@ void NodeMap::Render(const Nan::FunctionCallbackInfo<v8::Value>& info) {
|
||||
}
|
||||
|
||||
try {
|
||||
auto options = ParseOptions(Nan::To<v8::Object>(info[0]).ToLocalChecked());
|
||||
assert(!nodeMap->req);
|
||||
assert(!nodeMap->image.data);
|
||||
nodeMap->req = std::make_unique<RenderRequest>(Nan::To<v8::Function>(info[1]).ToLocalChecked());
|
||||
if (info[0]->IsFunction()) {
|
||||
assert(!nodeMap->req);
|
||||
assert(!nodeMap->image.data);
|
||||
nodeMap->req = std::make_unique<RenderRequest>(Nan::To<v8::Function>(info[0]).ToLocalChecked());
|
||||
|
||||
nodeMap->startRender(options);
|
||||
nodeMap->startRender();
|
||||
} else {
|
||||
auto options = ParseOptions(Nan::To<v8::Object>(info[0]).ToLocalChecked());
|
||||
assert(!nodeMap->req);
|
||||
assert(!nodeMap->image.data);
|
||||
nodeMap->req = std::make_unique<RenderRequest>(Nan::To<v8::Function>(info[1]).ToLocalChecked());
|
||||
|
||||
nodeMap->startRender(options);
|
||||
}
|
||||
} catch (const mbgl::style::conversion::Error& err) {
|
||||
return Nan::ThrowTypeError(err.message.c_str());
|
||||
} catch (const mbgl::util::StyleParseException& ex) {
|
||||
@@ -460,6 +469,27 @@ void NodeMap::Render(const Nan::FunctionCallbackInfo<v8::Value>& info) {
|
||||
info.GetReturnValue().SetUndefined();
|
||||
}
|
||||
|
||||
void NodeMap::startRender() {
|
||||
map->renderStill([this](const std::exception_ptr& eptr) {
|
||||
if (eptr) {
|
||||
error = eptr;
|
||||
uv_async_send(async);
|
||||
} else {
|
||||
assert(!image.data);
|
||||
image = frontend->readStillImage();
|
||||
uv_async_send(async);
|
||||
}
|
||||
});
|
||||
|
||||
// Retain this object, otherwise it might get destructed before we are finished rendering the
|
||||
// still image.
|
||||
Ref();
|
||||
|
||||
// Similarly, we're now waiting for the async to be called, so we need to make sure that it
|
||||
// keeps the loop alive.
|
||||
uv_ref(reinterpret_cast<uv_handle_t*>(async));
|
||||
}
|
||||
|
||||
void NodeMap::startRender(const NodeMap::RenderOptions& options) {
|
||||
frontend->setSize(options.size);
|
||||
map->setSize(options.size);
|
||||
@@ -640,7 +670,7 @@ void NodeMap::cancel() {
|
||||
reinterpret_cast<NodeMap *>(h->data)->renderFinished();
|
||||
});
|
||||
|
||||
frontend = std::make_unique<mbgl::HeadlessFrontend>(mbgl::Size{ 256, 256 }, pixelRatio);
|
||||
frontend = std::make_unique<mbgl::HeadlessFrontend>(mbgl::Size{ 512, 512 }, pixelRatio);
|
||||
map = std::make_unique<mbgl::Map>(*frontend, mapObserver,
|
||||
mbgl::MapOptions().withSize(frontend->getSize())
|
||||
.withPixelRatio(pixelRatio)
|
||||
@@ -925,6 +955,30 @@ void NodeMap::SetFilter(const Nan::FunctionCallbackInfo<v8::Value>& info) {
|
||||
layer->setFilter(filter);
|
||||
}
|
||||
|
||||
void NodeMap::SetSize(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]->IsArray()) {
|
||||
return Nan::ThrowTypeError("First argument must be an array");
|
||||
}
|
||||
|
||||
auto size = info[0].As<v8::Array>();
|
||||
uint32_t width= 0;
|
||||
uint32_t height = 0;
|
||||
if (size->Length() > 0) { width = Nan::To<uint32_t>(Nan::Get(size, 0).ToLocalChecked()).ToChecked(); }
|
||||
if (size->Length() > 1) { height = Nan::To<uint32_t>(Nan::Get(size, 1).ToLocalChecked()).ToChecked(); }
|
||||
|
||||
try {
|
||||
nodeMap->frontend->setSize({ width, height });
|
||||
nodeMap->map->setSize({ width, height });
|
||||
} catch (const std::exception &ex) {
|
||||
return Nan::ThrowError(ex.what());
|
||||
}
|
||||
|
||||
info.GetReturnValue().SetUndefined();
|
||||
}
|
||||
|
||||
void NodeMap::SetCenter(const Nan::FunctionCallbackInfo<v8::Value>& info) {
|
||||
auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder());
|
||||
if (!nodeMap->map) return Nan::ThrowError(releasedMessage());
|
||||
@@ -1419,7 +1473,7 @@ NodeMap::NodeMap(v8::Local<v8::Object> options)
|
||||
: true;
|
||||
}())
|
||||
, mapObserver(NodeMapObserver())
|
||||
, frontend(std::make_unique<mbgl::HeadlessFrontend>(mbgl::Size { 256, 256 }, pixelRatio))
|
||||
, frontend(std::make_unique<mbgl::HeadlessFrontend>(mbgl::Size { 512, 512 }, pixelRatio))
|
||||
, map(std::make_unique<mbgl::Map>(*frontend, mapObserver,
|
||||
mbgl::MapOptions().withSize(frontend->getSize())
|
||||
.withPixelRatio(pixelRatio)
|
||||
|
||||
@@ -55,6 +55,7 @@ public:
|
||||
static void SetLayerZoomRange(const Nan::FunctionCallbackInfo<v8::Value>&);
|
||||
static void SetLayerProperty(const Nan::FunctionCallbackInfo<v8::Value>&);
|
||||
static void SetFilter(const Nan::FunctionCallbackInfo<v8::Value>&);
|
||||
static void SetSize(const Nan::FunctionCallbackInfo<v8::Value>&);
|
||||
static void SetCenter(const Nan::FunctionCallbackInfo<v8::Value>&);
|
||||
static void SetZoom(const Nan::FunctionCallbackInfo<v8::Value>&);
|
||||
static void SetBearing(const Nan::FunctionCallbackInfo<v8::Value>&);
|
||||
@@ -72,6 +73,7 @@ public:
|
||||
|
||||
static v8::Local<v8::Value> ParseError(const char* msg);
|
||||
|
||||
void startRender();
|
||||
void startRender(const RenderOptions& options);
|
||||
void renderFinished();
|
||||
|
||||
|
||||
@@ -118,6 +118,7 @@ test('Map', function(t) {
|
||||
'setLayoutProperty',
|
||||
'setPaintProperty',
|
||||
'setFilter',
|
||||
'setSize',
|
||||
'setCenter',
|
||||
'setZoom',
|
||||
'setBearing',
|
||||
@@ -415,16 +416,16 @@ test('Map', function(t) {
|
||||
ratio: 1
|
||||
};
|
||||
|
||||
t.test('requires an object as the first parameter', function(t) {
|
||||
t.test('requires an object or a callback function as the first parameter', function(t) {
|
||||
var map = new mbgl.Map(options);
|
||||
|
||||
t.throws(function() {
|
||||
map.render();
|
||||
}, /First argument must be an options object/);
|
||||
}, /First argument must be an options object or a callback function/);
|
||||
|
||||
t.throws(function() {
|
||||
map.render('invalid');
|
||||
}, /First argument must be an options object/);
|
||||
}, /First argument must be an options object or a callback function/);
|
||||
|
||||
map.release();
|
||||
t.end();
|
||||
@@ -456,6 +457,17 @@ test('Map', function(t) {
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test('requires a style to be set (without render options object)', function(t) {
|
||||
var map = new mbgl.Map(options);
|
||||
|
||||
t.throws(function() {
|
||||
map.render(function() {});
|
||||
}, /Style is not loaded/);
|
||||
|
||||
map.release();
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test('returns an error delayed', function(t) {
|
||||
var delay = 0;
|
||||
var map = new mbgl.Map({
|
||||
@@ -476,6 +488,27 @@ test('Map', function(t) {
|
||||
});
|
||||
});
|
||||
|
||||
t.test('returns an error delayed (without render options object)', function(t) {
|
||||
var delay = 0;
|
||||
var map = new mbgl.Map({
|
||||
request: function(req, callback) {
|
||||
delay += 100;
|
||||
setTimeout(function() {
|
||||
callback(new Error('not found'));
|
||||
}, delay);
|
||||
},
|
||||
ratio: 1
|
||||
});
|
||||
map.load(style);
|
||||
map.setZoom(1);
|
||||
map.render(function(err, data) {
|
||||
map.release();
|
||||
|
||||
t.ok(err, 'returns error');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
t.test('returns an error', function(t) {
|
||||
var map = new mbgl.Map(options);
|
||||
map.load(style);
|
||||
@@ -487,6 +520,18 @@ test('Map', function(t) {
|
||||
});
|
||||
});
|
||||
|
||||
t.test('returns an error (without render options object)', function(t) {
|
||||
var map = new mbgl.Map(options);
|
||||
map.load(style);
|
||||
map.setZoom(1);
|
||||
map.render(function(err, data) {
|
||||
map.release();
|
||||
|
||||
t.ok(err, 'returns error');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
t.test('double release', function(t) {
|
||||
var map = new mbgl.Map(options);
|
||||
map.release();
|
||||
@@ -511,6 +556,19 @@ test('Map', function(t) {
|
||||
});
|
||||
});
|
||||
|
||||
t.test('returns an image (without render options object)', function(t) {
|
||||
var map = new mbgl.Map(options);
|
||||
map.load(style);
|
||||
map.render(function(err, pixels) {
|
||||
t.error(err);
|
||||
map.release();
|
||||
t.ok(pixels);
|
||||
t.ok(pixels instanceof Buffer);
|
||||
t.equal(pixels.length, 512 * 512 * 4)
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
t.test('can be called several times in serial', function(t) {
|
||||
var completed = 0;
|
||||
var remaining = 10;
|
||||
@@ -536,6 +594,31 @@ test('Map', function(t) {
|
||||
render();
|
||||
});
|
||||
|
||||
t.test('can be called several times in serial (without render options object)', function(t) {
|
||||
var completed = 0;
|
||||
var remaining = 10;
|
||||
var start = +new Date;
|
||||
|
||||
var map = new mbgl.Map(options);
|
||||
map.load(style);
|
||||
|
||||
function render() {
|
||||
map.render(function(err, data) {
|
||||
t.error(err);
|
||||
|
||||
t.ok(true, 'render @ ' + ((+new Date) - start) + 'ms');
|
||||
if (++completed === remaining) {
|
||||
map.release();
|
||||
t.end();
|
||||
} else {
|
||||
render();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render();
|
||||
});
|
||||
|
||||
t.test('throws if called in parallel', function(t) {
|
||||
var map = new mbgl.Map(options);
|
||||
map.load(style);
|
||||
@@ -549,6 +632,19 @@ test('Map', function(t) {
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test('throws if called in parallel (without render options object)', function(t) {
|
||||
var map = new mbgl.Map(options);
|
||||
map.load(style);
|
||||
|
||||
t.throws(function() {
|
||||
map.render(function() {});
|
||||
map.render(function() {});
|
||||
}, /Map is currently processing a RenderRequest/);
|
||||
|
||||
map.release();
|
||||
t.end();
|
||||
});
|
||||
|
||||
// This can't be tested with a test-suite render test because zoom and center
|
||||
// are set via a different code path when included as style properties.
|
||||
t.test('sets zoom before center', function(t) {
|
||||
@@ -589,7 +685,50 @@ test('Map', function(t) {
|
||||
t.equal(numPixels, 0);
|
||||
t.end();
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
t.test('sets zoom before center (without render options object)', function(t) {
|
||||
var map = new mbgl.Map(options);
|
||||
map.load({
|
||||
"version": 8,
|
||||
"sources": {
|
||||
"geojson": {
|
||||
"type": "geojson",
|
||||
"data": {
|
||||
"type": "Point",
|
||||
"coordinates": [
|
||||
18.05489,
|
||||
59.32744
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"id": "circle",
|
||||
"type": "circle",
|
||||
"source": "geojson",
|
||||
"paint": {
|
||||
"circle-color": "red"
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
map.setSize([400, 400]);
|
||||
map.setZoom(5);
|
||||
map.setCenter([18.05489, 59.32744]);
|
||||
map.render(function(err, actual) {
|
||||
t.error(err);
|
||||
|
||||
var PNG = require('pngjs').PNG;
|
||||
var pixelmatch = require('pixelmatch');
|
||||
var expected = PNG.sync.read(
|
||||
fs.readFileSync(path.join(__dirname, '../fixtures/zoom-center/expected.png'))).data;
|
||||
var numPixels = pixelmatch(actual, expected, undefined, 400, 400, { threshold: 0.13 });
|
||||
t.equal(numPixels, 0);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
t.test('request callback', function (t) {
|
||||
|
||||
Reference in New Issue
Block a user