summaryrefslogtreecommitdiff
path: root/platform/node
diff options
context:
space:
mode:
Diffstat (limited to 'platform/node')
-rw-r--r--platform/node/CHANGELOG.md19
-rw-r--r--platform/node/README.md6
-rw-r--r--platform/node/bitrise.yml2
-rw-r--r--platform/node/index.js27
-rwxr-xr-xplatform/node/scripts/after_success.sh9
-rw-r--r--platform/node/src/node_conversion.hpp8
-rw-r--r--platform/node/src/node_geojson.hpp8
-rw-r--r--platform/node/src/node_map.cpp208
-rw-r--r--platform/node/src/node_map.hpp20
-rw-r--r--platform/node/src/node_request.cpp10
-rw-r--r--platform/node/src/node_request.hpp6
-rw-r--r--platform/node/test/ignores.json59
-rw-r--r--platform/node/test/js/map.test.js2
-rw-r--r--platform/node/test/js/request.test.js167
-rw-r--r--platform/node/test/js/request_fail.test.js59
-rw-r--r--platform/node/test/js/request_notfound.test.js74
-rw-r--r--platform/node/test/mockfs.js6
-rw-r--r--platform/node/test/query.test.js9
-rw-r--r--platform/node/test/render.test.js13
-rw-r--r--platform/node/test/suite_implementation.js71
20 files changed, 459 insertions, 324 deletions
diff --git a/platform/node/CHANGELOG.md b/platform/node/CHANGELOG.md
index e77ceff31f..e75b1687f1 100644
--- a/platform/node/CHANGELOG.md
+++ b/platform/node/CHANGELOG.md
@@ -1,3 +1,22 @@
+# master
+* Increased the default maximum zoom level from 20 to 22 ([#9835](https://github.com/mapbox/mapbox-gl-native/pull/9835))
+
+# 3.5.5 - July 14, 2017
+- Provide debuggable release builds for node packages [#9497](https://github.com/mapbox/mapbox-gl-native/pull/9497)
+
+# 3.5.4 - June 6, 2017
+- Add support for ImageSource [#8968](https://github.com/mapbox/mapbox-gl-native/pull/8968)
+- Fixed an issue with `map.addImage()` which would cause added images to randomly be replaced with images found the style's sprite sheet ([#9119](https://github.com/mapbox/mapbox-gl-native/pull/9119))
+
+# 3.5.3 - May 30, 2017
+
+- Fixed a regression around `line-dasharrary` and `fill-pattern` that caused these properties to sometimes not render correctly ([#9130](https://github.com/mapbox/mapbox-gl-native/pull/9130))
+
+# 3.5.2 - May 18, 2017
+
+- Fixed a memory leak ([#8884](https://github.com/mapbox/mapbox-gl-native/pull/9035))
+
+
# 3.5.1 - May 8, 2017
- Adds Node v6 binaries. **Note, Node v4 binaries will be removed on August 1st.** ([#8884](https://github.com/mapbox/mapbox-gl-native/pull/8884))
diff --git a/platform/node/README.md b/platform/node/README.md
index 545d87861f..d19b2a9343 100644
--- a/platform/node/README.md
+++ b/platform/node/README.md
@@ -1,6 +1,6 @@
# node-mapbox-gl-native
-[![NPM](https://nodei.co/npm/mapbox-gl-native.png)](https://npmjs.org/package/mapbox-gl-native)
+[![NPM](https://nodei.co/npm/@mapbox/mapbox-gl-native.png)](https://npmjs.org/package/@mapbox/mapbox-gl-native)
## Installing
@@ -14,7 +14,7 @@ By default, installs binaries. On these platforms no additional dependencies are
Run:
```
-npm install mapbox-gl-native
+npm install @mapbox/mapbox-gl-native
```
Other platforms will fall back to a source compile with `make node`; see INSTALL.md in the repository root directory for prequisites.
@@ -31,7 +31,7 @@ npm run test-suite
```js
var fs = require('fs');
var path = require('path');
-var mbgl = require('mapbox-gl-native');
+var mbgl = require('@mapbox/mapbox-gl-native');
var sharp = require('sharp');
var options = {
diff --git a/platform/node/bitrise.yml b/platform/node/bitrise.yml
index 00005f0f36..fab3093d6e 100644
--- a/platform/node/bitrise.yml
+++ b/platform/node/bitrise.yml
@@ -64,7 +64,7 @@ workflows:
brew install cmake awscli node@4 node@6
brew link node@4 --force
gem install xcpretty --no-rdoc --no-ri
- export BUILDTYPE=Release
+ export BUILDTYPE=RelWithDebInfo
export PUBLISH=true
make test-node && ./platform/node/scripts/after_success.sh
brew unlink node@4
diff --git a/platform/node/index.js b/platform/node/index.js
index 54ba5c0dc6..5944a0a27d 100644
--- a/platform/node/index.js
+++ b/platform/node/index.js
@@ -4,6 +4,7 @@
var mbgl = require('../../lib/mapbox_gl_native.node');
var constructor = mbgl.Map.prototype.constructor;
+var process = require('process');
var Map = function(options) {
if (!(options instanceof Object)) {
@@ -18,9 +19,29 @@ var Map = function(options) {
return new constructor(Object.assign(options, {
request: function(req) {
- request(req, function() {
- req.respond.apply(req, arguments);
- });
+ // Protect against `request` implementations that call the callback synchronously,
+ // call it multiple times, or throw exceptions.
+ // http://blog.izs.me/post/59142742143/designing-apis-for-asynchrony
+
+ var responded = false;
+ var callback = function() {
+ var args = arguments;
+ if (!responded) {
+ responded = true;
+ process.nextTick(function() {
+ req.respond.apply(req, args);
+ });
+ } else {
+ console.warn('request function responded multiple times; it should call the callback only once');
+ }
+ };
+
+ try {
+ request(req, callback);
+ } catch (e) {
+ console.warn('request function threw an exception; it should call the callback with an error instead');
+ callback(e);
+ }
}
}));
};
diff --git a/platform/node/scripts/after_success.sh b/platform/node/scripts/after_success.sh
index ae34446927..18e00a4f0d 100755
--- a/platform/node/scripts/after_success.sh
+++ b/platform/node/scripts/after_success.sh
@@ -3,10 +3,13 @@
set -e
set -o pipefail
-if [[ -n ${PUBLISH:-} ]]; then
- if [[ "${BUILDTYPE}" == "Release" ]]; then
+if [[ "${PUBLISH:-}" == true ]]; then
+ if [[ "${BUILDTYPE}" == "RelWithDebInfo" ]]; then
./node_modules/.bin/node-pre-gyp package publish info
- else
+ elif [[ "${BUILDTYPE}" == "Debug" ]]; then
./node_modules/.bin/node-pre-gyp package publish info --debug
+ else
+ echo "error: must provide either Debug or RelWithDebInfo for BUILDTYPE"
+ exit 1
fi
fi
diff --git a/platform/node/src/node_conversion.hpp b/platform/node/src/node_conversion.hpp
index 22daedef6a..d266745548 100644
--- a/platform/node/src/node_conversion.hpp
+++ b/platform/node/src/node_conversion.hpp
@@ -82,6 +82,14 @@ inline optional<float> toNumber(v8::Local<v8::Value> value) {
return value->NumberValue();
}
+inline optional<double> toDouble(v8::Local<v8::Value> value) {
+ Nan::HandleScope scope;
+ if (!value->IsNumber()) {
+ return {};
+ }
+ return value->NumberValue();
+}
+
inline optional<std::string> toString(v8::Local<v8::Value> value) {
Nan::HandleScope scope;
if (!value->IsString()) {
diff --git a/platform/node/src/node_geojson.hpp b/platform/node/src/node_geojson.hpp
index 9bae86b76a..8a3927e2cf 100644
--- a/platform/node/src/node_geojson.hpp
+++ b/platform/node/src/node_geojson.hpp
@@ -1,13 +1,15 @@
#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>&, Error& error) const {
- error = { "not implemented" };
- return {};
+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
diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp
index 1c8bf6d1d2..9a7ebce445 100644
--- a/platform/node/src/node_map.cpp
+++ b/platform/node/src/node_map.cpp
@@ -4,14 +4,15 @@
#include "node_conversion.hpp"
#include "node_geojson.hpp"
-#include <mbgl/gl/headless_display.hpp>
#include <mbgl/util/exception.hpp>
+#include <mbgl/renderer/renderer.hpp>
+#include <mbgl/gl/headless_frontend.hpp>
#include <mbgl/style/conversion/source.hpp>
#include <mbgl/style/conversion/layer.hpp>
#include <mbgl/style/conversion/filter.hpp>
+#include <mbgl/style/style.hpp>
#include <mbgl/style/image.hpp>
-#include <mbgl/map/backend_scope.hpp>
-#include <mbgl/map/query.hpp>
+#include <mbgl/map/map_observer.hpp>
#include <mbgl/util/premultiply.hpp>
#include <unistd.h>
@@ -24,27 +25,18 @@ struct NodeMap::RenderOptions {
double pitch = 0;
double latitude = 0;
double longitude = 0;
- unsigned int width = 512;
- unsigned int height = 512;
+ mbgl::Size size = { 512, 512 };
std::vector<std::string> classes;
mbgl::MapDebugOptions debugOptions = mbgl::MapDebugOptions::NoDebug;
};
Nan::Persistent<v8::Function> NodeMap::constructor;
-static std::shared_ptr<mbgl::HeadlessDisplay> sharedDisplay() {
- static auto display = std::make_shared<mbgl::HeadlessDisplay>();
- return display;
-}
-
static const char* releasedMessage() {
return "Map resources have already been released";
}
-NodeBackend::NodeBackend()
- : HeadlessBackend(sharedDisplay()) {}
-
-void NodeBackend::onDidFailLoadingMap(std::exception_ptr error) {
+void NodeMapObserver::onDidFailLoadingMap(std::exception_ptr error) {
std::rethrow_exception(error);
}
@@ -60,8 +52,8 @@ void NodeMap::Init(v8::Local<v8::Object> target) {
Nan::SetPrototypeMethod(tpl, "release", Release);
Nan::SetPrototypeMethod(tpl, "cancel", Cancel);
- Nan::SetPrototypeMethod(tpl, "addClass", AddClass);
Nan::SetPrototypeMethod(tpl, "addSource", AddSource);
+ Nan::SetPrototypeMethod(tpl, "removeSource", RemoveSource);
Nan::SetPrototypeMethod(tpl, "addLayer", AddLayer);
Nan::SetPrototypeMethod(tpl, "removeLayer", RemoveLayer);
Nan::SetPrototypeMethod(tpl, "addImage", AddImage);
@@ -79,9 +71,6 @@ void NodeMap::Init(v8::Local<v8::Object> target) {
constructor.Reset(tpl->GetFunction());
Nan::Set(target, Nan::New("Map").ToLocalChecked(), tpl->GetFunction());
-
- // Initialize display connection on module load.
- sharedDisplay();
}
/**
@@ -163,25 +152,15 @@ void NodeMap::New(const Nan::FunctionCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(info.This());
}
-std::string StringifyStyle(v8::Local<v8::Value> styleHandle) {
- Nan::HandleScope scope;
-
- v8::Local<v8::Object> JSON = Nan::To<v8::Object>(
- Nan::Get(
- Nan::GetCurrentContext()->Global(),
- Nan::New("JSON").ToLocalChecked()
- ).ToLocalChecked()
- ).ToLocalChecked();
-
- return *Nan::Utf8String(Nan::MakeCallback(JSON, "stringify", 1, &styleHandle));
-}
-
/**
* Load a stylesheet
*
* @function
* @name load
* @param {string|Object} stylesheet either an object or a JSON representation
+ * @param {Object} options
+ * @param {boolean} options.defaultStyleCamera if true, sets the default style
+ * camera
* @returns {undefined} loads stylesheet into map
* @throws {Error} if stylesheet is missing or invalid
* @example
@@ -206,7 +185,8 @@ void NodeMap::Load(const Nan::FunctionCallbackInfo<v8::Value>& info) {
std::string style;
if (info[0]->IsObject()) {
- style = StringifyStyle(info[0]);
+ Nan::JSON JSON;
+ style = *Nan::Utf8String(JSON.Stringify(info[0]->ToObject()).ToLocalChecked());
} else if (info[0]->IsString()) {
style = *Nan::Utf8String(info[0]);
} else {
@@ -214,11 +194,26 @@ void NodeMap::Load(const Nan::FunctionCallbackInfo<v8::Value>& info) {
}
try {
- nodeMap->map->setStyleJSON(style);
+ nodeMap->map->getStyle().loadJSON(style);
} catch (const std::exception &ex) {
return Nan::ThrowError(ex.what());
}
+ if (info.Length() == 2) {
+ if (!info[1]->IsObject()) {
+ return Nan::ThrowTypeError("Second argument must be an options object");
+ }
+ auto options = Nan::To<v8::Object>(info[1]).ToLocalChecked();
+ if (Nan::Has(options, Nan::New("defaultStyleCamera").ToLocalChecked()).FromJust()) {
+ if (!Nan::Get(options, Nan::New("defaultStyleCamera").ToLocalChecked()).ToLocalChecked()->IsBoolean()) {
+ return Nan::ThrowError("Options object 'defaultStyleCamera' property must be a boolean");
+ }
+ if (Nan::Get(options, Nan::New("cameraMutated").ToLocalChecked()).ToLocalChecked()->BooleanValue()) {
+ nodeMap->map->jumpTo(nodeMap->map->getStyle().getDefaultCamera());
+ }
+ }
+ }
+
nodeMap->loaded = true;
info.GetReturnValue().SetUndefined();
@@ -266,11 +261,11 @@ NodeMap::RenderOptions NodeMap::ParseOptions(v8::Local<v8::Object> obj) {
}
if (Nan::Has(obj, Nan::New("width").ToLocalChecked()).FromJust()) {
- options.width = Nan::Get(obj, Nan::New("width").ToLocalChecked()).ToLocalChecked()->IntegerValue();
+ options.size.width = Nan::Get(obj, Nan::New("width").ToLocalChecked()).ToLocalChecked()->IntegerValue();
}
if (Nan::Has(obj, Nan::New("height").ToLocalChecked()).FromJust()) {
- options.height = Nan::Get(obj, Nan::New("height").ToLocalChecked()).ToLocalChecked()->IntegerValue();
+ options.size.height = Nan::Get(obj, Nan::New("height").ToLocalChecked()).ToLocalChecked()->IntegerValue();
}
if (Nan::Has(obj, Nan::New("classes").ToLocalChecked()).FromJust()) {
@@ -278,7 +273,7 @@ NodeMap::RenderOptions NodeMap::ParseOptions(v8::Local<v8::Object> obj) {
const int length = classes->Length();
options.classes.reserve(length);
for (int i = 0; i < length; i++) {
- options.classes.push_back(std::string { *Nan::Utf8String(Nan::To<v8::String>(Nan::Get(classes, i).ToLocalChecked()).ToLocalChecked()) });
+ options.classes.emplace_back(std::string { *Nan::Utf8String(Nan::To<v8::String>(Nan::Get(classes, i).ToLocalChecked()).ToLocalChecked()) });
}
}
@@ -366,48 +361,22 @@ void NodeMap::Render(const Nan::FunctionCallbackInfo<v8::Value>& info) {
}
void NodeMap::startRender(NodeMap::RenderOptions options) {
- map->setSize({ options.width, options.height });
+ frontend->setSize(options.size);
+ map->setSize(options.size);
- const mbgl::Size fbSize{ static_cast<uint32_t>(options.width * pixelRatio),
- static_cast<uint32_t>(options.height * pixelRatio) };
- if (!view || view->getSize() != fbSize) {
- view.reset();
- mbgl::BackendScope scope { backend };
- view = std::make_unique<mbgl::OffscreenView>(backend.getContext(), fbSize);
- }
-
- if (map->getClasses() != options.classes) {
- map->setClasses(options.classes);
- }
-
- if (map->getZoom() != options.zoom) {
- map->setZoom(options.zoom);
- }
-
- mbgl::LatLng latLng(options.latitude, options.longitude);
- if (map->getLatLng() != latLng) {
- map->setLatLng(latLng);
- }
-
- if (map->getBearing() != options.bearing) {
- map->setBearing(options.bearing);
- }
+ mbgl::CameraOptions camera;
+ camera.center = mbgl::LatLng { options.latitude, options.longitude };
+ camera.zoom = options.zoom;
+ camera.angle = -options.bearing * mbgl::util::DEG2RAD;
+ camera.pitch = options.pitch * mbgl::util::DEG2RAD;
- if (map->getPitch() != options.pitch) {
- map->setPitch(options.pitch);
- }
-
- if (map->getDebug() != options.debugOptions) {
- map->setDebug(options.debugOptions);
- }
-
- map->renderStill(*view, [this](const std::exception_ptr eptr) {
+ map->renderStill(camera, options.debugOptions, [this](const std::exception_ptr eptr) {
if (eptr) {
error = std::move(eptr);
uv_async_send(async);
} else {
assert(!image.data);
- image = view->readStillImage();
+ image = frontend->readStillImage();
uv_async_send(async);
}
});
@@ -506,7 +475,7 @@ void NodeMap::release() {
uv_close(reinterpret_cast<uv_handle_t *>(async), [] (uv_handle_t *h) {
delete reinterpret_cast<uv_async_t *>(h);
});
-
+
map.reset();
}
@@ -532,37 +501,24 @@ void NodeMap::Cancel(const Nan::FunctionCallbackInfo<v8::Value>& info) {
}
void NodeMap::cancel() {
- auto style = map->getStyleJSON();
+ auto style = map->getStyle().getJSON();
+
+ // Reset map explicitly as it resets the renderer frontend
+ map.reset();
- map = std::make_unique<mbgl::Map>(backend, mbgl::Size{ 256, 256 },
- pixelRatio, *this, threadpool, mbgl::MapMode::Still);
+ 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);
// FIXME: Reload the style after recreating the map. We need to find
// a better way of canceling an ongoing rendering on the core level
// without resetting the map, which is way too expensive.
- map->setStyleJSON(style);
+ map->getStyle().loadJSON(style);
error = std::make_exception_ptr(std::runtime_error("Canceled"));
renderFinished();
}
-void NodeMap::AddClass(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]->IsString()) {
- return Nan::ThrowTypeError("First argument must be a string");
- }
-
- try {
- nodeMap->map->addClass(*Nan::Utf8String(info[0]));
- } catch (const std::exception &ex) {
- return Nan::ThrowError(ex.what());
- }
-
- info.GetReturnValue().SetUndefined();
-}
-
void NodeMap::AddSource(const Nan::FunctionCallbackInfo<v8::Value>& info) {
using namespace mbgl::style;
using namespace mbgl::style::conversion;
@@ -578,6 +534,10 @@ void NodeMap::AddSource(const Nan::FunctionCallbackInfo<v8::Value>& info) {
return Nan::ThrowTypeError("First argument must be a string");
}
+ if (!info[1]->IsObject()) {
+ return Nan::ThrowTypeError("Second argument must be an object");
+ }
+
Error error;
mbgl::optional<std::unique_ptr<Source>> source = convert<std::unique_ptr<Source>>(info[1], error, *Nan::Utf8String(info[0]));
if (!source) {
@@ -585,7 +545,25 @@ void NodeMap::AddSource(const Nan::FunctionCallbackInfo<v8::Value>& info) {
return;
}
- nodeMap->map->addSource(std::move(*source));
+ nodeMap->map->getStyle().addSource(std::move(*source));
+}
+
+void NodeMap::RemoveSource(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() != 1) {
+ return Nan::ThrowTypeError("One argument required");
+ }
+
+ if (!info[0]->IsString()) {
+ return Nan::ThrowTypeError("First argument must be a string");
+ }
+
+ nodeMap->map->getStyle().removeSource(*Nan::Utf8String(info[0]));
}
void NodeMap::AddLayer(const Nan::FunctionCallbackInfo<v8::Value>& info) {
@@ -606,7 +584,7 @@ void NodeMap::AddLayer(const Nan::FunctionCallbackInfo<v8::Value>& info) {
return;
}
- nodeMap->map->addLayer(std::move(*layer));
+ nodeMap->map->getStyle().addLayer(std::move(*layer));
}
void NodeMap::RemoveLayer(const Nan::FunctionCallbackInfo<v8::Value>& info) {
@@ -624,7 +602,7 @@ void NodeMap::RemoveLayer(const Nan::FunctionCallbackInfo<v8::Value>& info) {
return Nan::ThrowTypeError("First argument must be a string");
}
- nodeMap->map->removeLayer(*Nan::Utf8String(info[0]));
+ nodeMap->map->getStyle().removeLayer(*Nan::Utf8String(info[0]));
}
void NodeMap::AddImage(const Nan::FunctionCallbackInfo<v8::Value>& info) {
@@ -660,7 +638,7 @@ void NodeMap::AddImage(const Nan::FunctionCallbackInfo<v8::Value>& info) {
return Nan::ThrowTypeError("width parameter required");
}
- if (!Nan::Get(optionObject, Nan::New("pixelRatio").ToLocalChecked()).ToLocalChecked()->IsUint32()) {
+ if (!Nan::Get(optionObject, Nan::New("pixelRatio").ToLocalChecked()).ToLocalChecked()->IsNumber()) {
return Nan::ThrowTypeError("pixelRatio parameter required");
}
@@ -671,7 +649,7 @@ void NodeMap::AddImage(const Nan::FunctionCallbackInfo<v8::Value>& info) {
return Nan::ThrowTypeError("Max height and width is 1024");
}
- uint32_t pixelRatio = Nan::Get(optionObject, Nan::New("pixelRatio").ToLocalChecked()).ToLocalChecked()->Uint32Value();
+ float pixelRatio = Nan::Get(optionObject, Nan::New("pixelRatio").ToLocalChecked()).ToLocalChecked()->NumberValue();
auto imageBuffer = Nan::To<v8::Object>(info[1]).ToLocalChecked()->ToObject();
char * imageDataBuffer = node::Buffer::Data(imageBuffer);
@@ -686,7 +664,7 @@ void NodeMap::AddImage(const Nan::FunctionCallbackInfo<v8::Value>& info) {
mbgl::UnassociatedImage cImage({ imageWidth, imageHeight}, std::move(data));
mbgl::PremultipliedImage cPremultipliedImage = mbgl::util::premultiply(std::move(cImage));
- nodeMap->map->addImage(*Nan::Utf8String(info[0]), std::make_unique<mbgl::style::Image>(std::move(cPremultipliedImage), pixelRatio));
+ nodeMap->map->getStyle().addImage(std::make_unique<mbgl::style::Image>(*Nan::Utf8String(info[0]), std::move(cPremultipliedImage), pixelRatio));
}
void NodeMap::RemoveImage(const Nan::FunctionCallbackInfo<v8::Value>& info) {
@@ -704,7 +682,7 @@ void NodeMap::RemoveImage(const Nan::FunctionCallbackInfo<v8::Value>& info) {
return Nan::ThrowTypeError("First argument must be a string");
}
- nodeMap->map->removeImage(*Nan::Utf8String(info[0]));
+ nodeMap->map->getStyle().removeImage(*Nan::Utf8String(info[0]));
}
void NodeMap::SetLayoutProperty(const Nan::FunctionCallbackInfo<v8::Value>& info) {
@@ -722,7 +700,7 @@ void NodeMap::SetLayoutProperty(const Nan::FunctionCallbackInfo<v8::Value>& info
return Nan::ThrowTypeError("First argument must be a string");
}
- mbgl::style::Layer* layer = nodeMap->map->getLayer(*Nan::Utf8String(info[0]));
+ mbgl::style::Layer* layer = nodeMap->map->getStyle().getLayer(*Nan::Utf8String(info[0]));
if (!layer) {
return Nan::ThrowTypeError("layer not found");
}
@@ -754,7 +732,7 @@ void NodeMap::SetPaintProperty(const Nan::FunctionCallbackInfo<v8::Value>& info)
return Nan::ThrowTypeError("First argument must be a string");
}
- mbgl::style::Layer* layer = nodeMap->map->getLayer(*Nan::Utf8String(info[0]));
+ mbgl::style::Layer* layer = nodeMap->map->getStyle().getLayer(*Nan::Utf8String(info[0]));
if (!layer) {
return Nan::ThrowTypeError("layer not found");
}
@@ -763,12 +741,7 @@ void NodeMap::SetPaintProperty(const Nan::FunctionCallbackInfo<v8::Value>& info)
return Nan::ThrowTypeError("Second argument must be a string");
}
- mbgl::optional<std::string> klass;
- if (info.Length() == 4 && info[3]->IsString()) {
- klass = std::string(*Nan::Utf8String(info[3]));
- }
-
- mbgl::optional<Error> error = setPaintProperty(*layer, *Nan::Utf8String(info[1]), info[2], klass);
+ mbgl::optional<Error> error = setPaintProperty(*layer, *Nan::Utf8String(info[1]), info[2]);
if (error) {
return Nan::ThrowTypeError(error->message.c_str());
}
@@ -812,7 +785,7 @@ void NodeMap::SetFilter(const Nan::FunctionCallbackInfo<v8::Value>& info) {
return Nan::ThrowTypeError("First argument must be a string");
}
- mbgl::style::Layer* layer = nodeMap->map->getLayer(*Nan::Utf8String(info[0]));
+ mbgl::style::Layer* layer = nodeMap->map->getStyle().getLayer(*Nan::Utf8String(info[0]));
if (!layer) {
return Nan::ThrowTypeError("layer not found");
}
@@ -911,6 +884,8 @@ void NodeMap::DumpDebugLogs(const Nan::FunctionCallbackInfo<v8::Value>& info) {
if (!nodeMap->map) return Nan::ThrowError(releasedMessage());
nodeMap->map->dumpDebugLogs();
+ nodeMap->frontend->getRenderer()->dumpDebugLogs();
+
info.GetReturnValue().SetUndefined();
}
@@ -947,7 +922,7 @@ void NodeMap::QueryRenderedFeatures(const Nan::FunctionCallbackInfo<v8::Value>&
auto layers = layersOption.As<v8::Array>();
std::vector<std::string> layersVec;
for (uint32_t i=0; i < layers->Length(); i++) {
- layersVec.push_back(*Nan::Utf8String(Nan::Get(layers,i).ToLocalChecked()));
+ layersVec.emplace_back(*Nan::Utf8String(Nan::Get(layers,i).ToLocalChecked()));
}
queryOptions.layerIDs = layersVec;
}
@@ -972,7 +947,7 @@ void NodeMap::QueryRenderedFeatures(const Nan::FunctionCallbackInfo<v8::Value>&
auto pos0 = Nan::Get(posOrBox, 0).ToLocalChecked().As<v8::Array>();
auto pos1 = Nan::Get(posOrBox, 1).ToLocalChecked().As<v8::Array>();
- optional = nodeMap->map->queryRenderedFeatures(mbgl::ScreenBox {
+ optional = nodeMap->frontend->getRenderer()->queryRenderedFeatures(mbgl::ScreenBox {
{
Nan::Get(pos0, 0).ToLocalChecked()->NumberValue(),
Nan::Get(pos0, 1).ToLocalChecked()->NumberValue()
@@ -983,7 +958,7 @@ void NodeMap::QueryRenderedFeatures(const Nan::FunctionCallbackInfo<v8::Value>&
}, queryOptions);
} else {
- optional = nodeMap->map->queryRenderedFeatures(mbgl::ScreenCoordinate {
+ optional = nodeMap->frontend->getRenderer()->queryRenderedFeatures(mbgl::ScreenCoordinate {
Nan::Get(posOrBox, 0).ToLocalChecked()->NumberValue(),
Nan::Get(posOrBox, 1).ToLocalChecked()->NumberValue()
}, queryOptions);
@@ -1007,9 +982,12 @@ NodeMap::NodeMap(v8::Local<v8::Object> options)
.ToLocalChecked()
->NumberValue()
: 1.0;
- }()),
- map(std::make_unique<mbgl::Map>(backend,
- mbgl::Size{ 256, 256 },
+ }())
+ , mapObserver(NodeMapObserver())
+ , 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,
diff --git a/platform/node/src/node_map.hpp b/platform/node/src/node_map.hpp
index cdc8f1e51f..c8e33bb347 100644
--- a/platform/node/src/node_map.hpp
+++ b/platform/node/src/node_map.hpp
@@ -4,8 +4,7 @@
#include <mbgl/map/map.hpp>
#include <mbgl/storage/file_source.hpp>
-#include <mbgl/gl/headless_backend.hpp>
-#include <mbgl/gl/offscreen_view.hpp>
+#include <mbgl/util/image.hpp>
#include <exception>
@@ -15,12 +14,15 @@
#include <nan.h>
#pragma GCC diagnostic pop
+namespace mbgl {
+class Map;
+class HeadlessFrontend;
+} // namespace mbgl
+
namespace node_mbgl {
-class NodeBackend : public mbgl::HeadlessBackend {
-public:
- NodeBackend();
- void onDidFailLoadingMap(std::exception_ptr) final;
+class NodeMapObserver : public mbgl::MapObserver {
+ void onDidFailLoadingMap(std::exception_ptr) override;
};
class NodeMap : public Nan::ObjectWrap,
@@ -42,8 +44,8 @@ public:
static void Render(const Nan::FunctionCallbackInfo<v8::Value>&);
static void Release(const Nan::FunctionCallbackInfo<v8::Value>&);
static void Cancel(const Nan::FunctionCallbackInfo<v8::Value>&);
- static void AddClass(const Nan::FunctionCallbackInfo<v8::Value>&);
static void AddSource(const Nan::FunctionCallbackInfo<v8::Value>&);
+ static void RemoveSource(const Nan::FunctionCallbackInfo<v8::Value>&);
static void AddLayer(const Nan::FunctionCallbackInfo<v8::Value>&);
static void RemoveLayer(const Nan::FunctionCallbackInfo<v8::Value>&);
static void AddImage(const Nan::FunctionCallbackInfo<v8::Value>&);
@@ -69,9 +71,9 @@ public:
std::unique_ptr<mbgl::AsyncRequest> request(const mbgl::Resource&, mbgl::FileSource::Callback);
const float pixelRatio;
- NodeBackend backend;
- std::unique_ptr<mbgl::OffscreenView> view;
NodeThreadPool threadpool;
+ NodeMapObserver mapObserver;
+ std::unique_ptr<mbgl::HeadlessFrontend> frontend;
std::unique_ptr<mbgl::Map> map;
std::exception_ptr error;
diff --git a/platform/node/src/node_request.cpp b/platform/node/src/node_request.cpp
index 09373b1779..de16710f78 100644
--- a/platform/node/src/node_request.cpp
+++ b/platform/node/src/node_request.cpp
@@ -122,19 +122,9 @@ void NodeRequest::HandleCallback(const Nan::FunctionCallbackInfo<v8::Value>& inf
}
void NodeRequest::Execute() {
- asyncExecute = std::make_unique<mbgl::util::AsyncTask>([this] { doExecute(); Unref(); });
- asyncExecute->send();
-
- Ref();
-}
-
-void NodeRequest::doExecute() {
- Nan::HandleScope scope;
-
v8::Local<v8::Value> argv[] = { handle() };
Nan::MakeCallback(Nan::To<v8::Object>(target->handle()->GetInternalField(1)).ToLocalChecked(), "request", 1, argv);
- asyncExecute.reset();
}
NodeRequest::NodeAsyncRequest::NodeAsyncRequest(NodeRequest* request_) : request(request_) {
diff --git a/platform/node/src/node_request.hpp b/platform/node/src/node_request.hpp
index 356566132b..7d7679a3c7 100644
--- a/platform/node/src/node_request.hpp
+++ b/platform/node/src/node_request.hpp
@@ -8,9 +8,6 @@
#include <mbgl/storage/resource.hpp>
#include <mbgl/storage/file_source.hpp>
-#include <mbgl/util/async_task.hpp>
-
-#include <memory>
namespace node_mbgl {
@@ -38,12 +35,9 @@ public:
void Execute();
private:
- void doExecute();
-
NodeMap* target;
mbgl::FileSource::Callback callback;
NodeAsyncRequest* asyncRequest = nullptr;
- std::unique_ptr<mbgl::util::AsyncTask> asyncExecute;
};
}
diff --git a/platform/node/test/ignores.json b/platform/node/test/ignores.json
new file mode 100644
index 0000000000..1084a5c923
--- /dev/null
+++ b/platform/node/test/ignores.json
@@ -0,0 +1,59 @@
+{
+ "query-tests/geometry/multilinestring": "needs investigation",
+ "query-tests/geometry/multipolygon": "needs investigation",
+ "query-tests/geometry/polygon": "needs investigation",
+ "query-tests/regressions/mapbox-gl-js#3534": "https://github.com/mapbox/mapbox-gl-native/issues/8193",
+ "query-tests/regressions/mapbox-gl-js#4417": "https://github.com/mapbox/mapbox-gl-native/issues/8007",
+ "query-tests/symbol-features-in/pitched-screen": "https://github.com/mapbox/mapbox-gl-native/issues/6817",
+ "query-tests/symbol-features-in/tilted-inside": "https://github.com/mapbox/mapbox-gl-native/issues/5056",
+ "query-tests/symbol-features-in/tilted-outside": "https://github.com/mapbox/mapbox-gl-native/issues/9435",
+ "query-tests/world-wrapping/box": "skip - needs issue",
+ "query-tests/world-wrapping/point": "skip - needs issue",
+ "render-tests/debug/collision-overscaled": "https://github.com/mapbox/mapbox-gl-native/issues/3841",
+ "render-tests/debug/collision-pitched-wrapped": "https://github.com/mapbox/mapbox-gl-native/issues/3841",
+ "render-tests/debug/collision-pitched": "https://github.com/mapbox/mapbox-gl-native/issues/3841",
+ "render-tests/debug/collision": "https://github.com/mapbox/mapbox-gl-native/issues/3841",
+ "render-tests/debug/tile-overscaled": "https://github.com/mapbox/mapbox-gl-native/issues/3841",
+ "render-tests/debug/tile": "https://github.com/mapbox/mapbox-gl-native/issues/3841",
+ "render-tests/extent/1024-circle": "needs investigation",
+ "render-tests/extent/1024-symbol": "needs investigation",
+ "render-tests/fill-extrusion-pattern/@2x": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
+ "render-tests/fill-extrusion-pattern/function-2": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
+ "render-tests/fill-extrusion-pattern/function": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
+ "render-tests/fill-extrusion-pattern/literal": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
+ "render-tests/fill-extrusion-pattern/missing": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
+ "render-tests/fill-extrusion-pattern/opacity": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
+ "render-tests/geojson/inline-linestring-fill": "current behavior is arbitrary",
+ "render-tests/geojson/inline-polygon-symbol": "behavior needs reconciliation with gl-js",
+ "render-tests/icon-size/composite-function-high-base-plain": "https://github.com/mapbox/mapbox-gl-native/issues/8654",
+ "render-tests/icon-size/composite-function-high-base-sdf": "https://github.com/mapbox/mapbox-gl-native/issues/8654",
+ "render-tests/icon-text-fit/both-padding": "https://github.com/mapbox/mapbox-gl-native/issues/5602",
+ "render-tests/icon-text-fit/both": "https://github.com/mapbox/mapbox-gl-native/issues/5602",
+ "render-tests/icon-text-fit/height": "https://github.com/mapbox/mapbox-gl-native/issues/5602",
+ "render-tests/icon-text-fit/width-padding": "https://github.com/mapbox/mapbox-gl-native/issues/5602",
+ "render-tests/icon-text-fit/width": "https://github.com/mapbox/mapbox-gl-native/issues/5602",
+ "render-tests/line-width/property-function": "https://github.com/mapbox/mapbox-gl-js/issues/3682#issuecomment-264348200",
+ "render-tests/line-join/property-function": "https://github.com/mapbox/mapbox-gl-js/pull/5020",
+ "render-tests/line-join/property-function-dasharray": "https://github.com/mapbox/mapbox-gl-js/pull/5020",
+ "render-tests/line-opacity/step-curve": "https://github.com/mapbox/mapbox-gl-native/pull/9439",
+ "render-tests/regressions/mapbox-gl-js#2305": "https://github.com/mapbox/mapbox-gl-native/issues/6927",
+ "render-tests/regressions/mapbox-gl-js#3682": "https://github.com/mapbox/mapbox-gl-js/issues/3682",
+ "render-tests/regressions/mapbox-gl-native#7357": "https://github.com/mapbox/mapbox-gl-native/issues/7357",
+ "render-tests/runtime-styling/image-add-sdf": "https://github.com/mapbox/mapbox-gl-native/issues/9847",
+ "render-tests/runtime-styling/paint-property-fill-flat-to-extrude": "https://github.com/mapbox/mapbox-gl-native/issues/6745",
+ "render-tests/runtime-styling/set-style-paint-property-fill-flat-to-extrude": "https://github.com/mapbox/mapbox-gl-native/issues/6745",
+ "render-tests/symbol-placement/line": "needs issue",
+ "render-tests/text-font/camera-function": "https://github.com/mapbox/mapbox-gl-native/pull/9439",
+ "render-tests/text-keep-upright/line-placement-true-offset": "https://github.com/mapbox/mapbox-gl-native/issues/9271",
+ "render-tests/text-pitch-alignment/auto-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
+ "render-tests/text-pitch-alignment/auto-text-rotation-alignment-viewport": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
+ "render-tests/text-pitch-alignment/map-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
+ "render-tests/text-pitch-alignment/map-text-rotation-alignment-viewport": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
+ "render-tests/text-pitch-alignment/viewport-overzoomed": "https://github.com/mapbox/mapbox-gl-js/issues/5095",
+ "render-tests/text-pitch-alignment/viewport-overzoomed-single-glyph": "https://github.com/mapbox/mapbox-gl-js/issues/5095",
+ "render-tests/text-pitch-alignment/viewport-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
+ "render-tests/text-pitch-alignment/viewport-text-rotation-alignment-viewport": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
+ "render-tests/text-pitch-scaling/line-half": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
+ "render-tests/text-size/composite-expression": "https://github.com/mapbox/mapbox-gl-native/pull/9439",
+ "render-tests/video/default": "skip - https://github.com/mapbox/mapbox-gl-native/issues/601"
+}
diff --git a/platform/node/test/js/map.test.js b/platform/node/test/js/map.test.js
index 4ab76b937a..39665e41ef 100644
--- a/platform/node/test/js/map.test.js
+++ b/platform/node/test/js/map.test.js
@@ -108,8 +108,8 @@ test('Map', function(t) {
'render',
'release',
'cancel',
- 'addClass',
'addSource',
+ 'removeSource',
'addLayer',
'removeLayer',
'addImage',
diff --git a/platform/node/test/js/request.test.js b/platform/node/test/js/request.test.js
new file mode 100644
index 0000000000..4f8d1cabb0
--- /dev/null
+++ b/platform/node/test/js/request.test.js
@@ -0,0 +1,167 @@
+'use strict';
+
+var mockfs = require('../mockfs');
+var mbgl = require('../../index');
+var test = require('tape');
+
+[ 'sprite_png', 'sprite_json', 'source_vector', 'glyph' ].forEach(function (resource) {
+ test(`render reports an error when the request function responds with an error (${resource})`, function(t) {
+ var map = new mbgl.Map({
+ request: function(req, callback) {
+ var data = mockfs.dataForRequest(req);
+ if (mockfs[resource] === data) {
+ callback(new Error('message'));
+ } else {
+ callback(null, { data: data });
+ }
+ }
+ });
+ map.load(mockfs.style_vector);
+ map.render({ zoom: 16 }, function(err, pixels) {
+ t.assert(err);
+ t.assert(/message/.test(err.message));
+ t.assert(!pixels);
+ t.end();
+ });
+ });
+});
+
+[ 'vector', 'raster' ].forEach(function (type) {
+ test(`render does not report an error when the request function responds with no data for a tile (${type})`, function(t) {
+ var map = new mbgl.Map({
+ request: function(req, callback) {
+ var data = mockfs.dataForRequest(req);
+ if (mockfs[`tile_${type}`] === data) {
+ callback();
+ } else {
+ callback(null, { data: data });
+ }
+ }
+ });
+ map.load(mockfs[`style_${type}`]);
+ map.render({ zoom: 16 }, function(err, pixels) {
+ t.error(err);
+ t.assert(pixels);
+ t.end();
+ });
+ });
+
+ test(`render reports an error when the request function responds with an error for a tile (${type})`, function(t) {
+ var map = new mbgl.Map({
+ request: function(req, callback) {
+ var data = mockfs.dataForRequest(req);
+ if (mockfs[`tile_${type}`] === data) {
+ callback(new Error('message'));
+ } else {
+ callback(null, { data: data });
+ }
+ }
+ });
+ map.load(mockfs[`style_${type}`]);
+ map.render({ zoom: 16 }, function(err, pixels) {
+ t.assert(err);
+ t.assert(/message/.test(err.message));
+ t.assert(!pixels);
+ t.end();
+ });
+ });
+});
+
+test(`render reports an error if the request function throws an exception`, function(t) {
+ var map = new mbgl.Map({
+ request: function() {
+ throw new Error('message');
+ }
+ });
+ map.load(mockfs.style_vector);
+ map.render({ zoom: 16 }, function(err, pixels) {
+ t.assert(err);
+ t.assert(/message/.test(err.message));
+ t.assert(!pixels);
+ t.end();
+ });
+});
+
+test(`render ignores request functions throwing an exception after calling the callback`, function(t) {
+ var map = new mbgl.Map({
+ request: function(req, callback) {
+ var data = mockfs.dataForRequest(req);
+ callback(null, { data: data });
+ throw new Error('message');
+ }
+ });
+ map.load(mockfs.style_vector);
+ map.render({ zoom: 16 }, function(err, pixels) {
+ t.error(err);
+ t.assert(pixels);
+ t.end();
+ });
+});
+
+test(`render ignores request functions calling the callback a second time`, function(t) {
+ var map = new mbgl.Map({
+ request: function(req, callback) {
+ var data = mockfs.dataForRequest(req);
+ callback(null, { data: data });
+ callback(null, { data: data });
+ }
+ });
+ map.load(mockfs.style_vector);
+ map.render({ zoom: 16 }, function(err, pixels) {
+ t.error(err);
+ t.assert(pixels);
+ t.end();
+ });
+});
+
+test(`render reports an error from loading the current style`, function(t) {
+ var map = new mbgl.Map({
+ request: function(req, callback) {
+ var data = mockfs.dataForRequest(req);
+ if (mockfs.source_vector === data) {
+ callback(new Error('message'));
+ } else {
+ callback(null, { data: data });
+ }
+ }
+ });
+ map.load(mockfs.style_vector);
+ map.render({ zoom: 16 }, function(err, pixels) {
+ t.assert(err);
+ t.assert(/message/.test(err.message));
+ t.assert(!pixels);
+
+ map.render({ zoom: 16 }, function(err, pixels) {
+ t.assert(err);
+ t.assert(/message/.test(err.message));
+ t.assert(!pixels);
+ t.end();
+ });
+ });
+});
+
+test(`render does not report an error from rendering a previous style`, function(t) {
+ var map = new mbgl.Map({
+ request: function(req, callback) {
+ var data = mockfs.dataForRequest(req);
+ if (mockfs.source_vector === data) {
+ callback(new Error('message'));
+ } else {
+ callback(null, { data: data });
+ }
+ }
+ });
+ map.load(mockfs.style_vector);
+ map.render({ zoom: 16 }, function(err, pixels) {
+ t.assert(err);
+ t.assert(/message/.test(err.message));
+ t.assert(!pixels);
+
+ map.load(mockfs.style_raster);
+ map.render({ zoom: 16 }, function(err, pixels) {
+ t.error(err);
+ t.assert(pixels);
+ t.end();
+ });
+ });
+});
diff --git a/platform/node/test/js/request_fail.test.js b/platform/node/test/js/request_fail.test.js
deleted file mode 100644
index fad116a2b8..0000000000
--- a/platform/node/test/js/request_fail.test.js
+++ /dev/null
@@ -1,59 +0,0 @@
-'use strict';
-
-var mockfs = require('../mockfs');
-var mbgl = require('../../index');
-var test = require('tape');
-
-function asyncReply(callback, data) {
- setTimeout(function() { callback(null, { data: data }); }, 0);
-};
-
-function asyncFail(callback) {
- setTimeout(function() { callback(new Error('not found')); }, 0);
-};
-
-function failRequest(t, style, failedResource) {
- var options = {
- request: function(req, callback) {
- var data = mockfs.dataForRequest(req);
-
- if (failedResource != data) {
- asyncReply(callback, data);
- } else {
- asyncFail(callback);
- }
- },
- ratio: 2,
- };
-
- var map = new mbgl.Map(options);
- map.load(style);
-
- map.render({ zoom: 16 }, function(err, pixels) {
- if (err) {
- t.pass("pass");
- map.release();
- }
- });
-};
-
-test('Vector', function(t) {
- t.plan(5);
-
- failRequest(t, mockfs.style_vector, null);
- failRequest(t, mockfs.style_vector, mockfs.sprite_png);
- failRequest(t, mockfs.style_vector, mockfs.sprite_json);
- failRequest(t, mockfs.style_vector, mockfs.source_vector);
- failRequest(t, mockfs.style_vector, mockfs.tile_vector);
- failRequest(t, mockfs.style_vector, mockfs.glyph);
-});
-
-test('Raster', function(t) {
- t.plan(4);
-
- failRequest(t, mockfs.style_raster, null);
- failRequest(t, mockfs.style_raster, mockfs.sprite_png);
- failRequest(t, mockfs.style_raster, mockfs.sprite_json);
- failRequest(t, mockfs.style_raster, mockfs.source_raster);
- failRequest(t, mockfs.style_raster, mockfs.tile_raster);
-});
diff --git a/platform/node/test/js/request_notfound.test.js b/platform/node/test/js/request_notfound.test.js
deleted file mode 100644
index d2d2812784..0000000000
--- a/platform/node/test/js/request_notfound.test.js
+++ /dev/null
@@ -1,74 +0,0 @@
-'use strict';
-
-var mockfs = require('../mockfs');
-var mbgl = require('../../index');
-var test = require('tape');
-
-function isTile(data) {
- return data == mockfs.tile_vector || data == mockfs.tile_raster;
-}
-
-function asyncReply(callback, data) {
- setTimeout(function() { callback(null, { data: data }); }, 0);
-};
-
-function asyncFail(callback, data) {
- // Do not set an error for tile when not found. A not found
- // tile is a valid tile.
- if (isTile(data)) {
- setTimeout(function() { callback(); }, 0);
- } else {
- setTimeout(function() { callback(new Error('not found')); }, 0);
- }
-};
-
-function notfoundRequest(t, style, notfoundResource) {
- var options = {
- request: function(req, callback) {
- var data = mockfs.dataForRequest(req);
-
- if (notfoundResource != data) {
- asyncReply(callback, data);
- } else {
- asyncFail(callback, data);
- }
- },
- ratio: 2,
- };
-
- var map = new mbgl.Map(options);
- map.load(style);
-
- map.render({ zoom: 16 }, function(err, pixels) {
- if (err && !isTile(notfoundResource)) {
- t.pass("pass");
- return;
- }
-
- if (!err && isTile(notfoundResource)) {
- t.pass("pass");
- return;
- }
-
- t.fail("fail");
- });
-};
-
-test('Vector', function(t) {
- t.plan(5);
-
- notfoundRequest(t, mockfs.style_vector, mockfs.sprite_png);
- notfoundRequest(t, mockfs.style_vector, mockfs.sprite_json);
- notfoundRequest(t, mockfs.style_vector, mockfs.source_vector);
- notfoundRequest(t, mockfs.style_vector, mockfs.tile_vector);
- notfoundRequest(t, mockfs.style_vector, mockfs.glyph);
-});
-
-test('Raster', function(t) {
- t.plan(4);
-
- notfoundRequest(t, mockfs.style_raster, mockfs.sprite_png);
- notfoundRequest(t, mockfs.style_raster, mockfs.sprite_json);
- notfoundRequest(t, mockfs.style_raster, mockfs.source_raster);
- notfoundRequest(t, mockfs.style_raster, mockfs.tile_raster);
-});
diff --git a/platform/node/test/mockfs.js b/platform/node/test/mockfs.js
index dfa5a425e3..2d27f3bbbe 100644
--- a/platform/node/test/mockfs.js
+++ b/platform/node/test/mockfs.js
@@ -5,7 +5,7 @@ var path = require('path');
function readFixture(file) {
return fs.readFileSync(path.join('test/fixtures/resources', file));
-};
+}
var style_raster = readFixture('style_raster.json').toString('utf8');
var style_vector = readFixture('style_vector.json').toString('utf8');
@@ -18,7 +18,7 @@ var tile_raster = readFixture('raster.tile');
var tile_vector = readFixture('vector.tile');
function dataForRequest(req) {
- if (req.url == null) {
+ if (req.url === null) {
return null;
} else if (req.url.indexOf('sprite') > -1 && req.url.endsWith('json')) {
return sprite_json;
@@ -37,7 +37,7 @@ function dataForRequest(req) {
} else {
return null;
}
-};
+}
module.exports = {
dataForRequest: dataForRequest,
diff --git a/platform/node/test/query.test.js b/platform/node/test/query.test.js
index 8125f1c7cd..02602d3f5a 100644
--- a/platform/node/test/query.test.js
+++ b/platform/node/test/query.test.js
@@ -1,12 +1,13 @@
'use strict';
-var suite = require('../../../mapbox-gl-js/test/integration').query;
-var suiteImplementation = require('./suite_implementation');
+const suite = require('../../../mapbox-gl-js/test/integration').query;
+const suiteImplementation = require('./suite_implementation');
+const ignores = require('./ignores.json');
-var tests;
+let tests;
if (process.argv[1] === __filename && process.argv.length > 2) {
tests = process.argv.slice(2);
}
-suite.run('native', {tests: tests}, suiteImplementation);
+suite.run('native', {tests: tests, ignores: ignores}, suiteImplementation);
diff --git a/platform/node/test/render.test.js b/platform/node/test/render.test.js
index e2e13534c9..812a531f20 100644
--- a/platform/node/test/render.test.js
+++ b/platform/node/test/render.test.js
@@ -1,12 +1,7 @@
'use strict';
-var suite = require('../../../mapbox-gl-js/test/integration').render;
-var suiteImplementation = require('./suite_implementation');
+const suite = require('../../../mapbox-gl-js/test/integration').render;
+const suiteImplementation = require('./suite_implementation');
+const ignores = require('./ignores.json');
-var tests;
-
-if (process.argv[1] === __filename && process.argv.length > 2) {
- tests = process.argv.slice(2);
-}
-
-suite.run('native', {tests: tests}, suiteImplementation);
+suite.run('native', ignores, suiteImplementation);
diff --git a/platform/node/test/suite_implementation.js b/platform/node/test/suite_implementation.js
index 8ac372b7c3..323f429bed 100644
--- a/platform/node/test/suite_implementation.js
+++ b/platform/node/test/suite_implementation.js
@@ -10,23 +10,27 @@ mbgl.on('message', function(msg) {
console.log('%s (%s): %s', msg.severity, msg.class, msg.text);
});
+// Map of map objects by pixel ratio
+var maps = new Map();
+
module.exports = function (style, options, callback) {
- var map = new mbgl.Map({
- ratio: options.pixelRatio,
- request: function(req, callback) {
- request(req.url, {encoding: null}, function (err, response, body) {
- if (err) {
- callback(err);
- } else if (response.statusCode == 404) {
- callback();
- } else if (response.statusCode != 200) {
- callback(new Error(response.statusMessage));
- } else {
- callback(null, {data: body});
- }
- });
+ if (options.recycleMap) {
+ if (maps.has(options.pixelRatio)) {
+ var map = maps.get(options.pixelRatio);
+ map.request = mapRequest;
+ } else {
+ maps.set(options.pixelRatio, new mbgl.Map({
+ ratio: options.pixelRatio,
+ request: mapRequest
+ }));
+ var map = maps.get(options.pixelRatio);
}
- });
+ } else {
+ var map = new mbgl.Map({
+ ratio: options.pixelRatio,
+ request: mapRequest
+ });
+ }
var timedOut = false;
var watchdog = setTimeout(function () {
@@ -46,14 +50,30 @@ module.exports = function (style, options, callback) {
options.bearing = style.bearing || 0;
options.pitch = style.pitch || 0;
- map.load(style);
+ map.load(style, { defaultStyleCamera: true });
+
+ function mapRequest(req, callback) {
+ request(req.url, {encoding: null}, function (err, response, body) {
+ if (err) {
+ callback(err);
+ } else if (response.statusCode == 404) {
+ callback();
+ } else if (response.statusCode != 200) {
+ callback(new Error(response.statusMessage));
+ } else {
+ callback(null, {data: body});
+ }
+ });
+ };
applyOperations(options.operations, function() {
map.render(options, function (err, pixels) {
var results = options.queryGeometry ?
map.queryRenderedFeatures(options.queryGeometry, options.queryOptions || {}) :
[];
- map.release();
+ if (!options.recycleMap) {
+ map.release();
+ }
if (timedOut) return;
clearTimeout(watchdog);
callback(err, pixels, results.map(prepareFeatures));
@@ -70,16 +90,25 @@ module.exports = function (style, options, callback) {
applyOperations(operations.slice(1), callback);
});
- } else if (operation[0] === 'addImage') {
+ } else if (operation[0] === 'addImage' || operation[0] === 'updateImage') {
var img = PNG.sync.read(fs.readFileSync(path.join(__dirname, '../../../mapbox-gl-js/test/integration', operation[2])));
+ const testOpts = (operation.length > 3) ? operation[3] : {};
- map.addImage(operation[1], img.data, {
+ const options = {
height: img.height,
width: img.width,
- pixelRatio: operation[3] || 1
- });
+ pixelRatio: testOpts.pixelRatio || 1,
+ sdf: testOpts.sdf || false
+ }
+ map.addImage(operation[1], img.data, options);
+
+ applyOperations(operations.slice(1), callback);
+
+ } else if (operation[0] === 'setStyle') {
+ map.load(operation[1]);
applyOperations(operations.slice(1), callback);
+
} else {
// Ensure that the next `map.render(options)` does not overwrite this change.
if (operation[0] === 'setCenter') {