Feature - Bring back node support (#217)

This PR brings back support for node.js later versions, as well as actually making it work. Maps now render, properly, using node v10, v12, and v16.
This commit is contained in:
Justas Brazauskas
2022-02-08 21:40:19 +02:00
committed by GitHub
parent b7a42e9974
commit cfcbc62fad
25 changed files with 3357 additions and 131 deletions

138
.github/workflows/node-ci.yml vendored Normal file
View File

@@ -0,0 +1,138 @@
name: node-ci
on:
workflow_dispatch:
push:
tags-ignore:
- '**'
paths-ignore:
- "platform/android/**"
- "platform/ios/**"
- "platform/qt/**"
- ".gitignore"
branches:
- master
pull_request:
branches:
- master
paths-ignore:
- "platform/android/**"
- "platform/ios/**"
- ".gitignore"
jobs:
build:
strategy:
matrix:
os:
- macos-11.0
- macos-10.15
- ubuntu-20.04
- ubuntu-18.04
runs-on: ${{ matrix.os }}
env:
BUILDTYPE: ${{github.ref == 'refs/heads/master' && 'Release' || 'Debug'}}
defaults:
run:
working-directory: ./
shell: bash
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
fetch-depth: 0
- name: Install dependencies
if: runner.os == 'macOS'
env:
HOMEBREW_NO_AUTO_UPDATE: 1
HOMEBREW_NO_INSTALL_CLEANUP: 1
run: |
brew list cmake || brew install cmake
brew list ccache || brew install ccache
brew list ninja || brew install ninja
brew list pkg-config || brew install pkg-config
brew list glfw3 || brew install glfw3
brew list libuv || brew install libuv
- name: Install dependencies
if: runner.os == 'Linux'
env:
DEBIAN_FRONTEND: noninteractive
run: |
sudo apt-get install -y \
ccache \
cmake \
ninja-build \
pkg-config \
xvfb \
libcurl4-openssl-dev \
libglfw3-dev \
libuv1-dev \
g++-10 \
libc++-9-dev \
libc++abi-9-dev
/usr/sbin/update-ccache-symlinks
- name: npm ci
run: npm ci --ignore-scripts
- name: Prepare ccache
run: ccache --clear --set-config cache_dir=~/.ccache
- name: Cache ccache
uses: actions/cache@v2
env:
cache-name: ccache-v1
with:
path: ~/.ccache
key: ${{ env.cache-name }}-${{ matrix.os }}-${{ github.job }}-${{ github.ref }}-${{ github.sha }}-${{ github.head_ref }}
restore-keys: |
${{ env.cache-name }}-${{ matrix.os }}-${{ github.job }}-${{ github.ref }}-${{ github.sha }}
${{ env.cache-name }}-${{ matrix.os }}-${{ github.job }}-${{ github.ref }}
${{ env.cache-name }}-${{ matrix.os }}-${{ github.job }}
- name: Clear ccache statistics
run: |
ccache --zero-stats --set-config cache_dir=~/.ccache
ccache --max-size=2G --set-config cache_dir=~/.ccache
ccache --show-stats --set-config cache_dir=~/.ccache
- name: CMake
if: runner.os == 'macOS'
run: |
cmake . -B build -G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_BUILD_TYPE=${{ env.BUILDTYPE }} -DMBGL_WITH_COVERAGE=ON
sed -i -e 's/$(ARCHS_STANDARD)/x86_64/g' build/build.ninja
sed -i -e 's/-arch arm64e/-arch x86_64/g' build/build.ninja
- name: CMake
if: runner.os == 'Linux'
run: |
cmake . -B build -G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10
- name: Build
run: |
cmake --build build -j $(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null)
- name: Test
if: runner.os == 'Linux'
run: xvfb-run --auto-servernum npm test
- name: Test
if: runner.os == 'macOS'
run: npm test
# On PRs make sure that the npm package can be packaged.
- name: Pack
if: github.ref != 'refs/heads/master'
run: |
npm pack --dry-run
# Uncomment to start publishing the package
# @TODO: Figure out the binary distribution method.
# - name: Publish package
# if: github.ref == 'refs/heads/master'
# run: |
# npm publish

3
.gitignore vendored
View File

@@ -35,3 +35,6 @@
/platform/ios/platform/ios/benchmark/assets/glyphs/Roboto Condensed Italic,Noto Sans Italic /platform/ios/platform/ios/benchmark/assets/glyphs/Roboto Condensed Italic,Noto Sans Italic
/platform/ios/platform/ios/benchmark/assets/glyphs/Noto Sans Regular /platform/ios/platform/ios/benchmark/assets/glyphs/Noto Sans Regular
/platform/android/key.json /platform/android/key.json
node_modules
# Node binaries.
/lib

3
.npmignore Normal file
View File

@@ -0,0 +1,3 @@
*
platform/node/src
platform/node/test

2954
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,15 +1,21 @@
{ {
"name": "@mapbox/mapbox-gl-native", "name": "@maplibre/maplibre-gl-native",
"version": "5.0.0", "version": "5.0.0",
"description": "Renders map tiles with Mapbox GL", "description": "Renders map tiles with Maplibre GL",
"keywords": [ "keywords": [
"mapbox", "maplibre",
"gl" "gl"
], ],
"directories": {
"lib": "lib"
},
"files": [
"lib"
],
"main": "platform/node/index.js", "main": "platform/node/index.js",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git://github.com/mapbox/mapbox-gl-native.git" "url": "git://github.com/maplibre/maplibre-gl-native.git"
}, },
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"dependencies": { "dependencies": {

View File

@@ -27,6 +27,7 @@ public:
virtual gl::ProcAddress getExtensionFunctionPointer(const char*) = 0; virtual gl::ProcAddress getExtensionFunctionPointer(const char*) = 0;
virtual void activateContext() = 0; virtual void activateContext() = 0;
virtual void deactivateContext() {} virtual void deactivateContext() {}
virtual void getContext() {}
}; };
private: private:

View File

@@ -32,7 +32,7 @@ HeadlessFrontend::HeadlessFrontend(Size size_,
asyncInvalidate([this] { asyncInvalidate([this] {
if (renderer && updateParameters) { if (renderer && updateParameters) {
auto startTime = mbgl::util::MonotonicTimer::now(); auto startTime = mbgl::util::MonotonicTimer::now();
gfx::BackendScope guard{*getBackend()}; gfx::BackendScope guard {*getBackend()};
// onStyleImageMissing might be called during a render. The user implemented method // onStyleImageMissing might be called during a render. The user implemented method
// could trigger a call to MGLRenderFrontend#update which overwrites `updateParameters`. // could trigger a call to MGLRenderFrontend#update which overwrites `updateParameters`.
@@ -143,6 +143,7 @@ PremultipliedImage HeadlessFrontend::readStillImage() {
HeadlessFrontend::RenderResult HeadlessFrontend::render(Map& map) { HeadlessFrontend::RenderResult HeadlessFrontend::render(Map& map) {
HeadlessFrontend::RenderResult result; HeadlessFrontend::RenderResult result;
std::exception_ptr error; std::exception_ptr error;
gfx::BackendScope guard { *getBackend() };
map.renderStill([&](const std::exception_ptr& e) { map.renderStill([&](const std::exception_ptr& e) {
if (e) { if (e) {

View File

@@ -42,7 +42,7 @@ HeadlessBackend::HeadlessBackend(const Size size_,
: mbgl::gl::RendererBackend(contextMode_), mbgl::gfx::HeadlessBackend(size_), swapBehaviour(swapBehaviour_) {} : mbgl::gl::RendererBackend(contextMode_), mbgl::gfx::HeadlessBackend(size_), swapBehaviour(swapBehaviour_) {}
HeadlessBackend::~HeadlessBackend() { HeadlessBackend::~HeadlessBackend() {
gfx::BackendScope guard{*this}; gfx::BackendScope guard{ *this, gfx::BackendScope::ScopeType::Implicit };
resource.reset(); resource.reset();
// Explicitly reset the context so that it is destructed and cleaned up before we destruct // Explicitly reset the context so that it is destructed and cleaned up before we destruct
// the impl object. // the impl object.
@@ -106,5 +106,3 @@ std::unique_ptr<gfx::HeadlessBackend> Backend::Create<gfx::Backend::Type::OpenGL
} // namespace gfx } // namespace gfx
} // namespace mbgl } // namespace mbgl

View File

@@ -31,7 +31,9 @@ public:
~ForwardingRendererObserver() override { mailbox->close(); } ~ForwardingRendererObserver() override { mailbox->close(); }
void onInvalidate() override { delegate.invoke(&RendererObserver::onInvalidate); } void onInvalidate() override {
delegate.invoke(&RendererObserver::onInvalidate);
}
void onResourceError(std::exception_ptr err) override { delegate.invoke(&RendererObserver::onResourceError, err); } void onResourceError(std::exception_ptr err) override { delegate.invoke(&RendererObserver::onResourceError, err); }
@@ -62,7 +64,9 @@ public:
frontend.reset(); frontend.reset();
} }
void onInvalidate() override { rendererObserver->onInvalidate(); } void onInvalidate() override {
rendererObserver->onInvalidate();
}
void onResourceError(std::exception_ptr err) override { void onResourceError(std::exception_ptr err) override {
hasPendingStillImageRequest = false; hasPendingStillImageRequest = false;

View File

@@ -9,17 +9,39 @@ include(${PROJECT_SOURCE_DIR}/node_modules/@mapbox/cmake-node-module/module.cmak
add_node_module( add_node_module(
mbgl-node mbgl-node
INSTALL_PATH ${PROJECT_SOURCE_DIR}/lib/{node_abi}/mbgl.node INSTALL_PATH ${PROJECT_SOURCE_DIR}/lib/{node_abi}/mbgl.node
NAN_VERSION 2.10.0 NAN_VERSION 2.14.2
EXCLUDE_NODE_ABIS EXCLUDE_NODE_ABIS
46 46
47 47
48 48
51 51
59 )
67 find_package(PkgConfig REQUIRED)
72 pkg_search_module(LIBUV libuv REQUIRED)
79
83 add_library(mbgl-node-loop STATIC)
target_sources(
mbgl-node-loop
PRIVATE
${PROJECT_SOURCE_DIR}/platform/default/src/mbgl/util/async_task.cpp
${PROJECT_SOURCE_DIR}/platform/default/src/mbgl/util/run_loop.cpp
${PROJECT_SOURCE_DIR}/platform/default/src/mbgl/util/timer.cpp
)
target_include_directories(
mbgl-node-loop
PUBLIC
${PROJECT_SOURCE_DIR}/include
${PROJECT_SOURCE_DIR}/platform/default/include
${PROJECT_SOURCE_DIR}/src
PRIVATE ${LIBUV_INCLUDE_DIRS}
)
target_link_libraries(
mbgl-node-loop
PUBLIC
Mapbox::Base
) )
target_sources( target_sources(
@@ -42,6 +64,7 @@ target_sources(
target_link_libraries( target_link_libraries(
mbgl-node mbgl-node
INTERFACE mbgl-node-loop
INTERFACE Mapbox::Map INTERFACE Mapbox::Map
) )

View File

@@ -1,6 +1,6 @@
# Developing the Mapbox GL Native Node.js module # Developing the Maplibre GL Native Node.js module
This document explains how to build the [Node.js](https://nodejs.org/) bindings for [Mapbox GL Native](../../README.md) for contributing to the development of the bindings themselves. If you just want to use the module, you can simply install it via `npm`; see [README.md](README.md) for installation and usage instructions. This document explains how to build the [Node.js](https://nodejs.org/) bindings for [Maplibre GL Native](../../README.md) for contributing to the development of the bindings themselves. If you just want to use the module, you can simply install it via `npm`; see [README.md](README.md) for installation and usage instructions.
## Building ## Building
@@ -8,12 +8,69 @@ To develop these bindings, you’ll need to build them from source. Building req
the [macOS](../macos/INSTALL.md#requirements) or [Linux](../linux/README.md#prerequisites) install documentation, depending the [macOS](../macos/INSTALL.md#requirements) or [Linux](../linux/README.md#prerequisites) install documentation, depending
on the target platform. on the target platform.
First you'll need to install dependencies:
#### MacOS
```bash
brew install \
cmake \
ccache \
ninja \
pkg-config \
glfw3 \
libuv
```
#### Linux (Ubuntu)
```bash
sudo apt-get install -y \
ccache \
cmake \
ninja-build \
pkg-config \
xvfb \
libcurl4-openssl-dev \
libglfw3-dev \
libuv1-dev \
g++-10 \
libc++-9-dev \
libc++abi-9-dev
/usr/sbin/update-ccache-symlinks
```
### Compiling
To compile the Node.js bindings and install module dependencies, from the repository root directory, first run: To compile the Node.js bindings and install module dependencies, from the repository root directory, first run:
#### MacOS
```bash
cmake . -B build -G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_BUILD_TYPE=Debug -DMBGL_WITH_COVERAGE=ON
``` ```
make distclean
Then, as a temporary step until build system is changed, run:
```bash
sed -i -e 's/$(ARCHS_STANDARD)/x86_64/g' build/build.ninja
sed -i -e 's/-arch arm64e/-arch x86_64/g' build/build.ninja
``` ```
#### Linux
```bash
cmake . -B build -G Ninja -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10
```
### Building
Finally, build:
```bash
cmake --build build -j $(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null)
```
If you are rebuilding after time has passed. If you are rebuilding after time has passed.
Then do: Then do:

View File

@@ -1,9 +1,12 @@
# node-mapbox-gl-native # @maplibre/maplibre-gl-native
[![NPM](https://nodei.co/npm/@mapbox/mapbox-gl-native.png)](https://npmjs.org/package/@mapbox/mapbox-gl-native) [![NPM](https://nodei.co/npm/@maplibre/maplibre-gl-native.png)](https://npmjs.org/package/@maplibre/maplibre-gl-native)
[![node-ci](https://github.com/maplibre/maplibre-gl-native/actions/workflows/node-ci.yml/badge.svg)](https://github.com/maplibre/maplibre-gl-native/actions/workflows/node-ci.yml)
## Installing ## Installing
#### :warn: This package isn't live yet.
Requires a modern C++ runtime that supports C++14. Requires a modern C++ runtime that supports C++14.
By default, installs binaries. On these platforms no additional dependencies are needed. By default, installs binaries. On these platforms no additional dependencies are needed.
@@ -14,7 +17,7 @@ By default, installs binaries. On these platforms no additional dependencies are
Run: Run:
``` ```
npm install @mapbox/mapbox-gl-native npm install @maplibre/maplibre-gl-native
``` ```
Other platforms will fall back to a source compile with `make node`; see [DEVELOPING.md](DEVELOPING.md) for details on Other platforms will fall back to a source compile with `make node`; see [DEVELOPING.md](DEVELOPING.md) for details on
@@ -32,7 +35,7 @@ npm run test-suite
```js ```js
var fs = require('fs'); var fs = require('fs');
var path = require('path'); var path = require('path');
var mbgl = require('@mapbox/mapbox-gl-native'); var mbgl = require('@maplibre/maplibre-gl-native');
var sharp = require('sharp'); var sharp = require('sharp');
var options = { var options = {
@@ -106,7 +109,7 @@ The `request()` method handles a request for a resource. The `ratio` sets the sc
} }
``` ```
The `kind` is an enum and defined in [`mbgl.Resource`](https://github.com/mapbox/mapbox-gl-native/blob/master/include/mbgl/storage/resource.hpp): The `kind` is an enum and defined in [`mbgl.Resource`](https://github.com/maplibre/maplibre-gl-native/blob/master/include/mbgl/storage/resource.hpp):
```json ```json
{ {
@@ -150,7 +153,7 @@ This is a very barebones implementation and you'll probably want a better implem
A sample implementation that uses [`request`](https://github.com/request/request) to fetch data from a remote source: A sample implementation that uses [`request`](https://github.com/request/request) to fetch data from a remote source:
```js ```js
var mbgl = require('mapbox-gl-native'); var mbgl = require('@maplibre/maplibre-gl-native');
var request = require('request'); var request = require('request');
var map = new mbgl.Map({ var map = new mbgl.Map({
@@ -184,10 +187,10 @@ Stylesheets are free to use any protocols, but your implementation of `request`
## Listening for log events ## Listening for log events
The module imported with `require('mapbox-gl-native')` inherits from [`EventEmitter`](https://nodejs.org/api/events.html), and the `NodeLogObserver` will push log events to this. Log messages can have [`class`](https://github.com/mapbox/mapbox-gl-native/blob/node-v2.1.0/include/mbgl/platform/event.hpp#L43-L60), [`severity`](https://github.com/mapbox/mapbox-gl-native/blob/node-v2.1.0/include/mbgl/platform/event.hpp#L17-L23), `code` ([HTTP status codes](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html)), and `text` parameters. The module imported with `require('maplibre-gl-native')` inherits from [`EventEmitter`](https://nodejs.org/api/events.html), and the `NodeLogObserver` will push log events to this. Log messages can have [`class`](https://github.com/maplibre/maplibre-gl-native/blob/node-v2.1.0/include/mbgl/platform/event.hpp#L43-L60), [`severity`](https://github.com/maplibre/maplibre-gl-native/blob/node-v2.1.0/include/mbgl/platform/event.hpp#L17-L23), `code` ([HTTP status codes](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html)), and `text` parameters.
```js ```js
var mbgl = require('mapbox-gl-native'); var mbgl = require('@maplibre/maplibre-gl-native');
mbgl.on('message', function(msg) { mbgl.on('message', function(msg) {
t.ok(msg, 'emits error'); t.ok(msg, 'emits error');
t.equal(msg.class, 'Style'); t.equal(msg.class, 'Style');

View File

@@ -79,7 +79,7 @@ public:
if (!value->IsBoolean()) { if (!value->IsBoolean()) {
return {}; return {};
} }
return value->BooleanValue(); return Nan::To<bool>(value).ToChecked();
} }
static optional<float> toNumber(const v8::Local<v8::Value>& value) { static optional<float> toNumber(const v8::Local<v8::Value>& value) {
@@ -87,7 +87,7 @@ public:
if (!value->IsNumber()) { if (!value->IsNumber()) {
return {}; return {};
} }
return value->NumberValue(); return static_cast<float>(Nan::To<double>(value).ToChecked());
} }
static optional<double> toDouble(const v8::Local<v8::Value>& value) { static optional<double> toDouble(const v8::Local<v8::Value>& value) {
@@ -95,7 +95,7 @@ public:
if (!value->IsNumber()) { if (!value->IsNumber()) {
return {}; return {};
} }
return value->NumberValue(); return Nan::To<double>(value).ToChecked();
} }
static optional<std::string> toString(const v8::Local<v8::Value>& value) { static optional<std::string> toString(const v8::Local<v8::Value>& value) {
@@ -114,11 +114,11 @@ public:
} else if (value->IsString()) { } else if (value->IsString()) {
return { std::string(*Nan::Utf8String(value)) }; return { std::string(*Nan::Utf8String(value)) };
} else if (value->IsUint32()) { } else if (value->IsUint32()) {
return { std::uint64_t(value->Uint32Value()) }; return { Nan::To<std::uint32_t>(value).ToChecked() };
} else if (value->IsInt32()) { } else if (value->IsInt32()) {
return { std::int64_t(value->Int32Value()) }; return { Nan::To<std::int32_t>(value).ToChecked() };
} else if (value->IsNumber()) { } else if (value->IsNumber()) {
return { value->NumberValue() }; return { Nan::To<double>(value).ToChecked() };
} else { } else {
return {}; return {};
} }
@@ -126,8 +126,10 @@ public:
static optional<GeoJSON> toGeoJSON(const v8::Local<v8::Value>& value, Error& error) { static optional<GeoJSON> toGeoJSON(const v8::Local<v8::Value>& value, Error& error) {
try { try {
Nan::JSON JSON; Nan::JSON NanJSON;
std::string string = *Nan::Utf8String(JSON.Stringify(value->ToObject()).ToLocalChecked()); v8::Local<v8::Object> obj = Nan::To<v8::Object>(value).ToLocalChecked();
v8::Local<v8::String> stringified = NanJSON.Stringify(obj).ToLocalChecked();
std::string string = *Nan::Utf8String(stringified);
return parseGeoJSON(string, error); return parseGeoJSON(string, error);
} catch (const std::exception& ex) { } catch (const std::exception& ex) {
error = { ex.what() }; error = { ex.what() };

View File

@@ -17,6 +17,7 @@ namespace node_mbgl {
Nan::Persistent<v8::Function> NodeExpression::constructor; Nan::Persistent<v8::Function> NodeExpression::constructor;
void NodeExpression::Init(v8::Local<v8::Object> target) { void NodeExpression::Init(v8::Local<v8::Object> target) {
v8::Local<v8::Context> context = target->CreationContext();
v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New); v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
tpl->SetClassName(Nan::New("Expression").ToLocalChecked()); tpl->SetClassName(Nan::New("Expression").ToLocalChecked());
tpl->InstanceTemplate()->SetInternalFieldCount(1); // what is this doing? tpl->InstanceTemplate()->SetInternalFieldCount(1); // what is this doing?
@@ -30,8 +31,8 @@ void NodeExpression::Init(v8::Local<v8::Object> target) {
Nan::SetMethod(tpl, "parse", Parse); Nan::SetMethod(tpl, "parse", Parse);
constructor.Reset(tpl->GetFunction()); // what is this doing? constructor.Reset(tpl->GetFunction(context).ToLocalChecked()); // what is this doing?
Nan::Set(target, Nan::New("Expression").ToLocalChecked(), tpl->GetFunction()); Nan::Set(target, Nan::New("Expression").ToLocalChecked(), tpl->GetFunction(context).ToLocalChecked());
} }
type::Type parseType(v8::Local<v8::Object> type) { type::Type parseType(v8::Local<v8::Object> type) {
@@ -46,10 +47,11 @@ type::Type parseType(v8::Local<v8::Object> type) {
{"resolvedImage", type::Image}}; {"resolvedImage", type::Image}};
v8::Local<v8::Value> v8kind = Nan::Get(type, Nan::New("kind").ToLocalChecked()).ToLocalChecked(); v8::Local<v8::Value> v8kind = Nan::Get(type, Nan::New("kind").ToLocalChecked()).ToLocalChecked();
std::string kind(*v8::String::Utf8Value(v8kind)); std::string kind(*v8::String::Utf8Value(v8::Isolate::GetCurrent(), v8kind));
if (kind == "array") { if (kind == "array") {
type::Type itemType = parseType(Nan::Get(type, Nan::New("itemType").ToLocalChecked()).ToLocalChecked()->ToObject()); v8::Local<v8::Context> context = type->CreationContext();
type::Type itemType = parseType(Nan::Get(type, Nan::New("itemType").ToLocalChecked()).ToLocalChecked()->ToObject(context).ToLocalChecked());
mbgl::optional<std::size_t> N; mbgl::optional<std::size_t> N;
v8::Local<v8::String> Nkey = Nan::New("N").ToLocalChecked(); v8::Local<v8::String> Nkey = Nan::New("N").ToLocalChecked();
@@ -64,6 +66,7 @@ type::Type parseType(v8::Local<v8::Object> type) {
void NodeExpression::Parse(const Nan::FunctionCallbackInfo<v8::Value>& info) { void NodeExpression::Parse(const Nan::FunctionCallbackInfo<v8::Value>& info) {
v8::Local<v8::Function> cons = Nan::New(constructor); v8::Local<v8::Function> cons = Nan::New(constructor);
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
if (info.Length() < 1 || info[0]->IsUndefined()) { if (info.Length() < 1 || info[0]->IsUndefined()) {
return Nan::ThrowTypeError("Requires a JSON style expression argument."); return Nan::ThrowTypeError("Requires a JSON style expression argument.");
@@ -71,7 +74,7 @@ void NodeExpression::Parse(const Nan::FunctionCallbackInfo<v8::Value>& info) {
mbgl::optional<type::Type> expected; mbgl::optional<type::Type> expected;
if (info.Length() > 1 && info[1]->IsObject()) { if (info.Length() > 1 && info[1]->IsObject()) {
expected = parseType(info[1]->ToObject()); expected = parseType(info[1]->ToObject(context).ToLocalChecked());
} }
auto success = [&cons, &info](std::unique_ptr<Expression> result) { auto success = [&cons, &info](std::unique_ptr<Expression> result) {
@@ -157,8 +160,8 @@ struct ToValue {
v8::Local<v8::Value> operator()(const std::vector<Value>& array) { v8::Local<v8::Value> operator()(const std::vector<Value>& array) {
Nan::EscapableHandleScope scope; Nan::EscapableHandleScope scope;
v8::Local<v8::Array> result = Nan::New<v8::Array>(); v8::Local<v8::Array> result = Nan::New<v8::Array>();
for (std::size_t i = 0; i < array.size(); i++) { for (std::uint32_t i = 0; i < array.size(); i++) {
result->Set(i, toJS(array[i])); Nan::Set(result, i, toJS(array[i]));
} }
return scope.Escape(result); return scope.Escape(result);
} }
@@ -170,7 +173,7 @@ struct ToValue {
Nan::EscapableHandleScope scope; Nan::EscapableHandleScope scope;
return scope.Escape(Nan::Null()); return scope.Escape(Nan::Null());
} }
v8::Local<v8::Value> operator()(const Formatted& formatted) { v8::Local<v8::Value> operator()(const Formatted& formatted) {
// This mimics the internal structure of the Formatted class in formatted.js // This mimics the internal structure of the Formatted class in formatted.js
// A better approach might be to use the explicit serialized form // A better approach might be to use the explicit serialized form
@@ -230,6 +233,7 @@ v8::Local<v8::Value> toJS(const Value& value) {
} }
void NodeExpression::Evaluate(const Nan::FunctionCallbackInfo<v8::Value>& info) { void NodeExpression::Evaluate(const Nan::FunctionCallbackInfo<v8::Value>& info) {
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
auto* nodeExpr = ObjectWrap::Unwrap<NodeExpression>(info.Holder()); auto* nodeExpr = ObjectWrap::Unwrap<NodeExpression>(info.Holder());
const std::unique_ptr<Expression>& expression = nodeExpr->expression; const std::unique_ptr<Expression>& expression = nodeExpr->expression;
@@ -238,12 +242,12 @@ void NodeExpression::Evaluate(const Nan::FunctionCallbackInfo<v8::Value>& info)
} }
mbgl::optional<float> zoom; mbgl::optional<float> zoom;
v8::Local<v8::Value> v8zoom = Nan::Get(info[0]->ToObject(), Nan::New("zoom").ToLocalChecked()).ToLocalChecked(); v8::Local<v8::Value> v8zoom = Nan::Get(info[0]->ToObject(context).ToLocalChecked(), Nan::New("zoom").ToLocalChecked()).ToLocalChecked();
if (v8zoom->IsNumber()) zoom = v8zoom->NumberValue(); if (v8zoom->IsNumber()) zoom = Nan::To<double>(v8zoom).FromJust();
mbgl::optional<double> heatmapDensity; mbgl::optional<double> heatmapDensity;
v8::Local<v8::Value> v8heatmapDensity = Nan::Get(info[0]->ToObject(), Nan::New("heatmapDensity").ToLocalChecked()).ToLocalChecked(); v8::Local<v8::Value> v8heatmapDensity = Nan::Get(info[0]->ToObject(context).ToLocalChecked(), Nan::New("heatmapDensity").ToLocalChecked()).ToLocalChecked();
if (v8heatmapDensity->IsNumber()) heatmapDensity = v8heatmapDensity->NumberValue(); if (v8heatmapDensity->IsNumber()) heatmapDensity = Nan::To<double>(v8heatmapDensity).FromJust();
Nan::JSON NanJSON; Nan::JSON NanJSON;
conversion::Error conversionError; conversion::Error conversionError;

View File

@@ -113,7 +113,7 @@ struct ToValue {
Nan::EscapableHandleScope scope; Nan::EscapableHandleScope scope;
v8::Local<v8::Array> result = Nan::New<v8::Array>(); v8::Local<v8::Array> result = Nan::New<v8::Array>();
for (std::size_t i = 0; i < array.size(); i++) { for (std::size_t i = 0; i < array.size(); i++) {
result->Set(i, toJS(array[i])); Nan::Set(result, i, toJS(array[i]));
} }
return scope.Escape(result); return scope.Escape(result);
} }

View File

@@ -118,9 +118,10 @@ ParseError)JS").ToLocalChecked()).ToLocalChecked();
Nan::SetPrototypeMethod(tpl, "dumpDebugLogs", DumpDebugLogs); Nan::SetPrototypeMethod(tpl, "dumpDebugLogs", DumpDebugLogs);
Nan::SetPrototypeMethod(tpl, "queryRenderedFeatures", QueryRenderedFeatures); Nan::SetPrototypeMethod(tpl, "queryRenderedFeatures", QueryRenderedFeatures);
v8::Local<v8::Context> context = target->CreationContext();
constructor.Reset(tpl->GetFunction()); constructor.Reset(tpl->GetFunction(context).ToLocalChecked());
Nan::Set(target, Nan::New("Map").ToLocalChecked(), tpl->GetFunction()); Nan::Set(target, Nan::New("Map").ToLocalChecked(), tpl->GetFunction(context).ToLocalChecked());
} }
/** /**
@@ -227,6 +228,7 @@ void NodeMap::New(const Nan::FunctionCallbackInfo<v8::Value>& info) {
* map.load(fs.readFileSync('./test/fixtures/style.json', 'utf8')); * map.load(fs.readFileSync('./test/fixtures/style.json', 'utf8'));
*/ */
void NodeMap::Load(const Nan::FunctionCallbackInfo<v8::Value>& info) { void NodeMap::Load(const Nan::FunctionCallbackInfo<v8::Value>& info) {
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder()); auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder());
if (!nodeMap->map) return Nan::ThrowError(releasedMessage()); if (!nodeMap->map) return Nan::ThrowError(releasedMessage());
@@ -242,7 +244,7 @@ void NodeMap::Load(const Nan::FunctionCallbackInfo<v8::Value>& info) {
if (info[0]->IsObject()) { if (info[0]->IsObject()) {
Nan::JSON JSON; Nan::JSON JSON;
style = *Nan::Utf8String(JSON.Stringify(info[0]->ToObject()).ToLocalChecked()); style = *Nan::Utf8String(JSON.Stringify(info[0]->ToObject(context).ToLocalChecked()).ToLocalChecked());
} else if (info[0]->IsString()) { } else if (info[0]->IsString()) {
style = *Nan::Utf8String(info[0]); style = *Nan::Utf8String(info[0]);
} else { } else {
@@ -266,7 +268,7 @@ void NodeMap::Load(const Nan::FunctionCallbackInfo<v8::Value>& info) {
if (!Nan::Get(options, Nan::New("defaultStyleCamera").ToLocalChecked()).ToLocalChecked()->IsBoolean()) { if (!Nan::Get(options, Nan::New("defaultStyleCamera").ToLocalChecked()).ToLocalChecked()->IsBoolean()) {
return Nan::ThrowError("Options object 'defaultStyleCamera' property must be a boolean"); return Nan::ThrowError("Options object 'defaultStyleCamera' property must be a boolean");
} }
if (Nan::Get(options, Nan::New("cameraMutated").ToLocalChecked()).ToLocalChecked()->BooleanValue()) { if (Nan::To<bool>(Nan::Get(options, Nan::New("cameraMutated").ToLocalChecked()).ToLocalChecked()).ToChecked()) {
nodeMap->map->jumpTo(nodeMap->map->getStyle().getDefaultCamera()); nodeMap->map->jumpTo(nodeMap->map->getStyle().getDefaultCamera());
} }
} }
@@ -298,15 +300,15 @@ NodeMap::RenderOptions NodeMap::ParseOptions(v8::Local<v8::Object> obj) {
NodeMap::RenderOptions options; NodeMap::RenderOptions options;
if (Nan::Has(obj, Nan::New("zoom").ToLocalChecked()).FromJust()) { if (Nan::Has(obj, Nan::New("zoom").ToLocalChecked()).FromJust()) {
options.zoom = Nan::Get(obj, Nan::New("zoom").ToLocalChecked()).ToLocalChecked()->NumberValue(); options.zoom = Nan::To<double>(Nan::Get(obj, Nan::New("zoom").ToLocalChecked()).ToLocalChecked()).ToChecked();
} }
if (Nan::Has(obj, Nan::New("bearing").ToLocalChecked()).FromJust()) { if (Nan::Has(obj, Nan::New("bearing").ToLocalChecked()).FromJust()) {
options.bearing = Nan::Get(obj, Nan::New("bearing").ToLocalChecked()).ToLocalChecked()->NumberValue(); options.bearing = Nan::To<double>(Nan::Get(obj, Nan::New("bearing").ToLocalChecked()).ToLocalChecked()).ToChecked();
} }
if (Nan::Has(obj, Nan::New("pitch").ToLocalChecked()).FromJust()) { if (Nan::Has(obj, Nan::New("pitch").ToLocalChecked()).FromJust()) {
options.pitch = Nan::Get(obj, Nan::New("pitch").ToLocalChecked()).ToLocalChecked()->NumberValue(); options.pitch = Nan::To<double>(Nan::Get(obj, Nan::New("pitch").ToLocalChecked()).ToLocalChecked()).ToChecked();
} }
if (Nan::Has(obj, Nan::New("light").ToLocalChecked()).FromJust()) { if (Nan::Has(obj, Nan::New("light").ToLocalChecked()).FromJust()) {
@@ -320,15 +322,15 @@ NodeMap::RenderOptions NodeMap::ParseOptions(v8::Local<v8::Object> obj) {
} }
if (Nan::Has(obj, Nan::New("axonometric").ToLocalChecked()).FromJust()) { if (Nan::Has(obj, Nan::New("axonometric").ToLocalChecked()).FromJust()) {
options.axonometric = Nan::Get(obj, Nan::New("axonometric").ToLocalChecked()).ToLocalChecked()->BooleanValue(); options.axonometric = Nan::To<bool>(Nan::Get(obj, Nan::New("axonometric").ToLocalChecked()).ToLocalChecked()).ToChecked();
} }
if (Nan::Has(obj, Nan::New("skew").ToLocalChecked()).FromJust()) { if (Nan::Has(obj, Nan::New("skew").ToLocalChecked()).FromJust()) {
auto skewObj = Nan::Get(obj, Nan::New("skew").ToLocalChecked()).ToLocalChecked(); auto skewObj = Nan::Get(obj, Nan::New("skew").ToLocalChecked()).ToLocalChecked();
if (skewObj->IsArray()) { if (skewObj->IsArray()) {
auto skew = skewObj.As<v8::Array>(); auto skew = skewObj.As<v8::Array>();
if (skew->Length() > 0) { options.xSkew = Nan::Get(skew, 0).ToLocalChecked()->NumberValue(); } if (skew->Length() > 0) { options.xSkew = Nan::To<double>(Nan::Get(skew, 0).ToLocalChecked()).ToChecked(); }
if (skew->Length() > 1) { options.ySkew = Nan::Get(skew, 1).ToLocalChecked()->NumberValue(); } if (skew->Length() > 1) { options.ySkew = Nan::To<double>(Nan::Get(skew, 1).ToLocalChecked()).ToChecked(); }
} }
} }
@@ -336,17 +338,17 @@ NodeMap::RenderOptions NodeMap::ParseOptions(v8::Local<v8::Object> obj) {
auto centerObj = Nan::Get(obj, Nan::New("center").ToLocalChecked()).ToLocalChecked(); auto centerObj = Nan::Get(obj, Nan::New("center").ToLocalChecked()).ToLocalChecked();
if (centerObj->IsArray()) { if (centerObj->IsArray()) {
auto center = centerObj.As<v8::Array>(); auto center = centerObj.As<v8::Array>();
if (center->Length() > 0) { options.longitude = Nan::Get(center, 0).ToLocalChecked()->NumberValue(); } if (center->Length() > 0) { options.longitude = Nan::To<double>(Nan::Get(center, 0).ToLocalChecked()).ToChecked(); }
if (center->Length() > 1) { options.latitude = Nan::Get(center, 1).ToLocalChecked()->NumberValue(); } if (center->Length() > 1) { options.latitude = Nan::To<double>(Nan::Get(center, 1).ToLocalChecked()).ToChecked(); }
} }
} }
if (Nan::Has(obj, Nan::New("width").ToLocalChecked()).FromJust()) { if (Nan::Has(obj, Nan::New("width").ToLocalChecked()).FromJust()) {
options.size.width = Nan::Get(obj, Nan::New("width").ToLocalChecked()).ToLocalChecked()->IntegerValue(); options.size.width = Nan::To<int64_t>(Nan::Get(obj, Nan::New("width").ToLocalChecked()).ToLocalChecked()).ToChecked();
} }
if (Nan::Has(obj, Nan::New("height").ToLocalChecked()).FromJust()) { if (Nan::Has(obj, Nan::New("height").ToLocalChecked()).FromJust()) {
options.size.height = Nan::Get(obj, Nan::New("height").ToLocalChecked()).ToLocalChecked()->IntegerValue(); options.size.height = Nan::To<int64_t>(Nan::Get(obj, Nan::New("height").ToLocalChecked()).ToLocalChecked()).ToChecked();
} }
if (Nan::Has(obj, Nan::New("classes").ToLocalChecked()).FromJust()) { if (Nan::Has(obj, Nan::New("classes").ToLocalChecked()).FromJust()) {
@@ -361,27 +363,27 @@ NodeMap::RenderOptions NodeMap::ParseOptions(v8::Local<v8::Object> obj) {
if (Nan::Has(obj, Nan::New("debug").ToLocalChecked()).FromJust()) { if (Nan::Has(obj, Nan::New("debug").ToLocalChecked()).FromJust()) {
auto debug = Nan::To<v8::Object>(Nan::Get(obj, Nan::New("debug").ToLocalChecked()).ToLocalChecked()).ToLocalChecked(); auto debug = Nan::To<v8::Object>(Nan::Get(obj, Nan::New("debug").ToLocalChecked()).ToLocalChecked()).ToLocalChecked();
if (Nan::Has(debug, Nan::New("tileBorders").ToLocalChecked()).FromJust()) { if (Nan::Has(debug, Nan::New("tileBorders").ToLocalChecked()).FromJust()) {
if (Nan::Get(debug, Nan::New("tileBorders").ToLocalChecked()).ToLocalChecked()->BooleanValue()) { if (Nan::To<bool>(Nan::Get(debug, Nan::New("tileBorders").ToLocalChecked()).ToLocalChecked()).ToChecked()) {
options.debugOptions = options.debugOptions | mbgl::MapDebugOptions::TileBorders; options.debugOptions = options.debugOptions | mbgl::MapDebugOptions::TileBorders;
} }
} }
if (Nan::Has(debug, Nan::New("parseStatus").ToLocalChecked()).FromJust()) { if (Nan::Has(debug, Nan::New("parseStatus").ToLocalChecked()).FromJust()) {
if (Nan::Get(debug, Nan::New("parseStatus").ToLocalChecked()).ToLocalChecked()->BooleanValue()) { if (Nan::To<bool>(Nan::Get(debug, Nan::New("parseStatus").ToLocalChecked()).ToLocalChecked()).ToChecked()) {
options.debugOptions = options.debugOptions | mbgl::MapDebugOptions::ParseStatus; options.debugOptions = options.debugOptions | mbgl::MapDebugOptions::ParseStatus;
} }
} }
if (Nan::Has(debug, Nan::New("timestamps").ToLocalChecked()).FromJust()) { if (Nan::Has(debug, Nan::New("timestamps").ToLocalChecked()).FromJust()) {
if (Nan::Get(debug, Nan::New("timestamps").ToLocalChecked()).ToLocalChecked()->BooleanValue()) { if (Nan::To<bool>(Nan::Get(debug, Nan::New("timestamps").ToLocalChecked()).ToLocalChecked()).ToChecked()) {
options.debugOptions = options.debugOptions | mbgl::MapDebugOptions::Timestamps; options.debugOptions = options.debugOptions | mbgl::MapDebugOptions::Timestamps;
} }
} }
if (Nan::Has(debug, Nan::New("collision").ToLocalChecked()).FromJust()) { if (Nan::Has(debug, Nan::New("collision").ToLocalChecked()).FromJust()) {
if (Nan::Get(debug, Nan::New("collision").ToLocalChecked()).ToLocalChecked()->BooleanValue()) { if (Nan::To<bool>(Nan::Get(debug, Nan::New("collision").ToLocalChecked()).ToLocalChecked()).ToChecked()) {
options.debugOptions = options.debugOptions | mbgl::MapDebugOptions::Collision; options.debugOptions = options.debugOptions | mbgl::MapDebugOptions::Collision;
} }
} }
if (Nan::Has(debug, Nan::New("overdraw").ToLocalChecked()).FromJust()) { if (Nan::Has(debug, Nan::New("overdraw").ToLocalChecked()).FromJust()) {
if (Nan::Get(debug, Nan::New("overdraw").ToLocalChecked()).ToLocalChecked()->BooleanValue()) { if (Nan::To<bool>(Nan::Get(debug, Nan::New("overdraw").ToLocalChecked()).ToLocalChecked()).ToChecked()) {
options.debugOptions = mbgl::MapDebugOptions::Overdraw; options.debugOptions = mbgl::MapDebugOptions::Overdraw;
} }
} }
@@ -593,7 +595,7 @@ void NodeMap::release() {
uv_close(reinterpret_cast<uv_handle_t *>(async), [] (uv_handle_t *h) { uv_close(reinterpret_cast<uv_handle_t *>(async), [] (uv_handle_t *h) {
delete reinterpret_cast<uv_async_t *>(h); delete reinterpret_cast<uv_async_t *>(h);
}); });
map.reset(); map.reset();
frontend.reset(); frontend.reset();
} }
@@ -621,7 +623,7 @@ void NodeMap::Cancel(const Nan::FunctionCallbackInfo<v8::Value>& info) {
void NodeMap::cancel() { void NodeMap::cancel() {
auto style = map->getStyle().getJSON(); auto style = map->getStyle().getJSON();
// Reset map explicitly as it resets the renderer frontend // Reset map explicitly as it resets the renderer frontend
map.reset(); map.reset();
@@ -640,7 +642,7 @@ void NodeMap::cancel() {
map = std::make_unique<mbgl::Map>(*frontend, mapObserver, map = std::make_unique<mbgl::Map>(*frontend, mapObserver,
mbgl::MapOptions().withSize(frontend->getSize()) mbgl::MapOptions().withSize(frontend->getSize())
.withPixelRatio(pixelRatio) .withPixelRatio(pixelRatio)
.withMapMode(mode) .withMapMode(mbgl::MapMode::Static)
.withCrossSourceCollisions(crossSourceCollisions), .withCrossSourceCollisions(crossSourceCollisions),
mbgl::ResourceOptions().withPlatformContext(reinterpret_cast<void*>(this))); mbgl::ResourceOptions().withPlatformContext(reinterpret_cast<void*>(this)));
@@ -743,6 +745,8 @@ void NodeMap::AddImage(const Nan::FunctionCallbackInfo<v8::Value>& info) {
using namespace mbgl::style; using namespace mbgl::style;
using namespace mbgl::style::conversion; using namespace mbgl::style::conversion;
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder()); auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder());
if (!nodeMap->map) return Nan::ThrowError(releasedMessage()); if (!nodeMap->map) return Nan::ThrowError(releasedMessage());
@@ -776,8 +780,8 @@ void NodeMap::AddImage(const Nan::FunctionCallbackInfo<v8::Value>& info) {
return Nan::ThrowTypeError("pixelRatio parameter required"); return Nan::ThrowTypeError("pixelRatio parameter required");
} }
uint32_t imageHeight = Nan::Get(optionObject, Nan::New("height").ToLocalChecked()).ToLocalChecked()->Uint32Value(); uint32_t imageHeight = Nan::To<uint32_t>(Nan::Get(optionObject, Nan::New("height").ToLocalChecked()).ToLocalChecked()).ToChecked();
uint32_t imageWidth = Nan::Get(optionObject, Nan::New("width").ToLocalChecked()).ToLocalChecked()->Uint32Value(); uint32_t imageWidth = Nan::To<uint32_t>(Nan::Get(optionObject, Nan::New("width").ToLocalChecked()).ToLocalChecked()).ToChecked();
if (imageWidth > 1024 || imageHeight > 1024) { if (imageWidth > 1024 || imageHeight > 1024) {
return Nan::ThrowTypeError("Max height and width is 1024"); return Nan::ThrowTypeError("Max height and width is 1024");
@@ -785,12 +789,12 @@ void NodeMap::AddImage(const Nan::FunctionCallbackInfo<v8::Value>& info) {
bool sdf = false; bool sdf = false;
if (Nan::Get(optionObject, Nan::New("sdf").ToLocalChecked()).ToLocalChecked()->IsBoolean()) { if (Nan::Get(optionObject, Nan::New("sdf").ToLocalChecked()).ToLocalChecked()->IsBoolean()) {
sdf = Nan::Get(optionObject, Nan::New("sdf").ToLocalChecked()).ToLocalChecked()->BooleanValue(); sdf = Nan::To<bool>(Nan::Get(optionObject, Nan::New("sdf").ToLocalChecked()).ToLocalChecked()).ToChecked();
} }
float pixelRatio = Nan::Get(optionObject, Nan::New("pixelRatio").ToLocalChecked()).ToLocalChecked()->NumberValue(); float pixelRatio = (float)Nan::To<double>(Nan::Get(optionObject, Nan::New("pixelRatio").ToLocalChecked()).ToLocalChecked()).ToChecked();
auto imageBuffer = Nan::To<v8::Object>(info[1]).ToLocalChecked()->ToObject(); auto imageBuffer = Nan::To<v8::Object>(info[1]).ToLocalChecked()->ToObject(context).ToLocalChecked();
char * imageDataBuffer = node::Buffer::Data(imageBuffer); char * imageDataBuffer = node::Buffer::Data(imageBuffer);
size_t imageLength = node::Buffer::Length(imageBuffer); size_t imageLength = node::Buffer::Length(imageBuffer);
@@ -800,7 +804,7 @@ void NodeMap::AddImage(const Nan::FunctionCallbackInfo<v8::Value>& info) {
std::unique_ptr<uint8_t[]> data = std::make_unique<uint8_t[]>(imageLength); std::unique_ptr<uint8_t[]> data = std::make_unique<uint8_t[]>(imageLength);
std::copy(imageDataBuffer, imageDataBuffer + imageLength, data.get()); std::copy(imageDataBuffer, imageDataBuffer + imageLength, data.get());
mbgl::UnassociatedImage cImage({ imageWidth, imageHeight}, std::move(data)); mbgl::UnassociatedImage cImage({ imageWidth, imageHeight}, std::move(data));
mbgl::PremultipliedImage cPremultipliedImage = mbgl::util::premultiply(std::move(cImage)); mbgl::PremultipliedImage cPremultipliedImage = mbgl::util::premultiply(std::move(cImage));
nodeMap->map->getStyle().addImage(std::make_unique<mbgl::style::Image>(*Nan::Utf8String(info[0]), std::move(cPremultipliedImage), pixelRatio, sdf)); nodeMap->map->getStyle().addImage(std::make_unique<mbgl::style::Image>(*Nan::Utf8String(info[0]), std::move(cPremultipliedImage), pixelRatio, sdf));
@@ -823,32 +827,32 @@ void NodeMap::RemoveImage(const Nan::FunctionCallbackInfo<v8::Value>& info) {
nodeMap->map->getStyle().removeImage(*Nan::Utf8String(info[0])); nodeMap->map->getStyle().removeImage(*Nan::Utf8String(info[0]));
} }
void NodeMap::SetLayerZoomRange(const Nan::FunctionCallbackInfo<v8::Value>& info) { void NodeMap::SetLayerZoomRange(const Nan::FunctionCallbackInfo<v8::Value>& info) {
using namespace mbgl::style; using namespace mbgl::style;
auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder()); auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder());
if (!nodeMap->map) return Nan::ThrowError(releasedMessage()); if (!nodeMap->map) return Nan::ThrowError(releasedMessage());
if (info.Length() != 3) { if (info.Length() != 3) {
return Nan::ThrowTypeError("Three arguments required"); return Nan::ThrowTypeError("Three arguments required");
} }
if (!info[0]->IsString()) { if (!info[0]->IsString()) {
return Nan::ThrowTypeError("First argument must be a string"); return Nan::ThrowTypeError("First argument must be a string");
} }
if (!info[1]->IsNumber() || !info[2]->IsNumber()) { if (!info[1]->IsNumber() || !info[2]->IsNumber()) {
return Nan::ThrowTypeError("Second and third arguments must be numbers"); return Nan::ThrowTypeError("Second and third arguments must be numbers");
} }
mbgl::style::Layer* layer = nodeMap->map->getStyle().getLayer(*Nan::Utf8String(info[0])); mbgl::style::Layer* layer = nodeMap->map->getStyle().getLayer(*Nan::Utf8String(info[0]));
if (!layer) { if (!layer) {
return Nan::ThrowTypeError("layer not found"); return Nan::ThrowTypeError("layer not found");
} }
layer->setMinZoom(info[1]->NumberValue()); layer->setMinZoom(Nan::To<double>(info[1]).ToChecked());
layer->setMaxZoom(info[2]->NumberValue()); layer->setMaxZoom(Nan::To<double>(info[2]).ToChecked());
} }
void NodeMap::SetLayerProperty(const Nan::FunctionCallbackInfo<v8::Value>& info) { void NodeMap::SetLayerProperty(const Nan::FunctionCallbackInfo<v8::Value>& info) {
@@ -929,8 +933,8 @@ void NodeMap::SetCenter(const Nan::FunctionCallbackInfo<v8::Value>& info) {
auto center = info[0].As<v8::Array>(); auto center = info[0].As<v8::Array>();
double latitude = 0; double latitude = 0;
double longitude = 0; double longitude = 0;
if (center->Length() > 0) { longitude = Nan::Get(center, 0).ToLocalChecked()->NumberValue(); } if (center->Length() > 0) { longitude = Nan::To<double>(Nan::Get(center, 0).ToLocalChecked()).ToChecked(); }
if (center->Length() > 1) { latitude = Nan::Get(center, 1).ToLocalChecked()->NumberValue(); } if (center->Length() > 1) { latitude = Nan::To<double>(Nan::Get(center, 1).ToLocalChecked()).ToChecked(); }
try { try {
nodeMap->map->jumpTo(mbgl::CameraOptions().withCenter(mbgl::LatLng { latitude, longitude })); nodeMap->map->jumpTo(mbgl::CameraOptions().withCenter(mbgl::LatLng { latitude, longitude }));
@@ -950,7 +954,7 @@ void NodeMap::SetZoom(const Nan::FunctionCallbackInfo<v8::Value>& info) {
} }
try { try {
nodeMap->map->jumpTo(mbgl::CameraOptions().withZoom(info[0]->NumberValue())); nodeMap->map->jumpTo(mbgl::CameraOptions().withZoom(Nan::To<double>(info[0]).ToChecked()));
} catch (const std::exception &ex) { } catch (const std::exception &ex) {
return Nan::ThrowError(ex.what()); return Nan::ThrowError(ex.what());
} }
@@ -967,7 +971,7 @@ void NodeMap::SetBearing(const Nan::FunctionCallbackInfo<v8::Value>& info) {
} }
try { try {
nodeMap->map->jumpTo(mbgl::CameraOptions().withBearing(info[0]->NumberValue())); nodeMap->map->jumpTo(mbgl::CameraOptions().withBearing(Nan::To<double>(info[0]).ToChecked()));
} catch (const std::exception &ex) { } catch (const std::exception &ex) {
return Nan::ThrowError(ex.what()); return Nan::ThrowError(ex.what());
} }
@@ -984,7 +988,7 @@ void NodeMap::SetPitch(const Nan::FunctionCallbackInfo<v8::Value>& info) {
} }
try { try {
nodeMap->map->jumpTo(mbgl::CameraOptions().withPitch(info[0]->NumberValue())); nodeMap->map->jumpTo(mbgl::CameraOptions().withPitch(Nan::To<double>(info[0]).ToChecked()));
} catch (const std::exception &ex) { } catch (const std::exception &ex) {
return Nan::ThrowError(ex.what()); return Nan::ThrowError(ex.what());
} }
@@ -1027,7 +1031,7 @@ void NodeMap::SetAxonometric(const Nan::FunctionCallbackInfo<v8::Value>& info) {
try { try {
nodeMap->map->setProjectionMode(mbgl::ProjectionMode() nodeMap->map->setProjectionMode(mbgl::ProjectionMode()
.withAxonometric(info[0]->BooleanValue())); .withAxonometric(Nan::To<bool>(info[0]).ToChecked()));
} catch (const std::exception &ex) { } catch (const std::exception &ex) {
return Nan::ThrowError(ex.what()); return Nan::ThrowError(ex.what());
} }
@@ -1045,7 +1049,7 @@ void NodeMap::SetXSkew(const Nan::FunctionCallbackInfo<v8::Value>& info) {
try { try {
nodeMap->map->setProjectionMode(mbgl::ProjectionMode() nodeMap->map->setProjectionMode(mbgl::ProjectionMode()
.withXSkew(info[0]->NumberValue())); .withXSkew(Nan::To<double>(info[0]).ToChecked()));
} catch (const std::exception &ex) { } catch (const std::exception &ex) {
return Nan::ThrowError(ex.what()); return Nan::ThrowError(ex.what());
} }
@@ -1063,7 +1067,7 @@ void NodeMap::SetYSkew(const Nan::FunctionCallbackInfo<v8::Value>& info) {
try { try {
nodeMap->map->setProjectionMode(mbgl::ProjectionMode() nodeMap->map->setProjectionMode(mbgl::ProjectionMode()
.withYSkew(info[0]->NumberValue())); .withYSkew(Nan::To<double>(info[0]).ToChecked()));
} catch (const std::exception &ex) { } catch (const std::exception &ex) {
return Nan::ThrowError(ex.what()); return Nan::ThrowError(ex.what());
} }
@@ -1324,9 +1328,9 @@ void NodeMap::QueryRenderedFeatures(const Nan::FunctionCallbackInfo<v8::Value>&
if (!info[1]->IsObject()) { if (!info[1]->IsObject()) {
return Nan::ThrowTypeError("options argument must be an object"); return Nan::ThrowTypeError("options argument must be an object");
} }
auto options = Nan::To<v8::Object>(info[1]).ToLocalChecked(); auto options = Nan::To<v8::Object>(info[1]).ToLocalChecked();
//Check if layers is set. If provided, it must be an array of strings //Check if layers is set. If provided, it must be an array of strings
if (Nan::Has(options, Nan::New("layers").ToLocalChecked()).FromJust()) { if (Nan::Has(options, Nan::New("layers").ToLocalChecked()).FromJust()) {
auto layersOption = Nan::Get(options, Nan::New("layers").ToLocalChecked()).ToLocalChecked(); auto layersOption = Nan::Get(options, Nan::New("layers").ToLocalChecked()).ToLocalChecked();
@@ -1340,7 +1344,7 @@ void NodeMap::QueryRenderedFeatures(const Nan::FunctionCallbackInfo<v8::Value>&
} }
queryOptions.layerIDs = layersVec; queryOptions.layerIDs = layersVec;
} }
//Check if filter is provided. If set it must be a valid Filter object //Check if filter is provided. If set it must be a valid Filter object
if (Nan::Has(options, Nan::New("filter").ToLocalChecked()).FromJust()) { if (Nan::Has(options, Nan::New("filter").ToLocalChecked()).FromJust()) {
auto filterOption = Nan::Get(options, Nan::New("filter").ToLocalChecked()).ToLocalChecked(); auto filterOption = Nan::Get(options, Nan::New("filter").ToLocalChecked()).ToLocalChecked();
@@ -1363,24 +1367,24 @@ void NodeMap::QueryRenderedFeatures(const Nan::FunctionCallbackInfo<v8::Value>&
optional = nodeMap->frontend->getRenderer()->queryRenderedFeatures(mbgl::ScreenBox { optional = nodeMap->frontend->getRenderer()->queryRenderedFeatures(mbgl::ScreenBox {
{ {
Nan::Get(pos0, 0).ToLocalChecked()->NumberValue(), Nan::To<double>(Nan::Get(pos0, 0).ToLocalChecked()).ToChecked(),
Nan::Get(pos0, 1).ToLocalChecked()->NumberValue() Nan::To<double>(Nan::Get(pos0, 1).ToLocalChecked()).ToChecked()
}, { }, {
Nan::Get(pos1, 0).ToLocalChecked()->NumberValue(), Nan::To<double>(Nan::Get(pos1, 0).ToLocalChecked()).ToChecked(),
Nan::Get(pos1, 1).ToLocalChecked()->NumberValue() Nan::To<double>(Nan::Get(pos1, 1).ToLocalChecked()).ToChecked()
} }
}, queryOptions); }, queryOptions);
} else { } else {
optional = nodeMap->frontend->getRenderer()->queryRenderedFeatures(mbgl::ScreenCoordinate { optional = nodeMap->frontend->getRenderer()->queryRenderedFeatures(mbgl::ScreenCoordinate {
Nan::Get(posOrBox, 0).ToLocalChecked()->NumberValue(), Nan::To<double>(Nan::Get(posOrBox, 0).ToLocalChecked()).ToChecked(),
Nan::Get(posOrBox, 1).ToLocalChecked()->NumberValue() Nan::To<double>(Nan::Get(posOrBox, 1).ToLocalChecked()).ToChecked()
}, queryOptions); }, queryOptions);
} }
auto array = Nan::New<v8::Array>(); auto array = Nan::New<v8::Array>();
for (std::size_t i = 0; i < optional.size(); i++) { for (std::size_t i = 0; i < optional.size(); i++) {
array->Set(i, toJS(optional[i])); Nan::Set(array, i, toJS(optional[i]));
} }
info.GetReturnValue().Set(array); info.GetReturnValue().Set(array);
} catch (const std::exception &ex) { } catch (const std::exception &ex) {
@@ -1392,15 +1396,13 @@ NodeMap::NodeMap(v8::Local<v8::Object> options)
: pixelRatio([&] { : pixelRatio([&] {
Nan::HandleScope scope; Nan::HandleScope scope;
return Nan::Has(options, Nan::New("ratio").ToLocalChecked()).FromJust() return Nan::Has(options, Nan::New("ratio").ToLocalChecked()).FromJust()
? Nan::Get(options, Nan::New("ratio").ToLocalChecked()) ? Nan::To<double>(Nan::Get(options, Nan::New("ratio").ToLocalChecked())
.ToLocalChecked() .ToLocalChecked()).ToChecked()
->NumberValue()
: 1.0; : 1.0;
}()) }())
, mode([&] { , mode([&] {
Nan::HandleScope scope; Nan::HandleScope scope;
if (Nan::Has(options, Nan::New("mode").ToLocalChecked()).FromJust() && if (std::string(*v8::String::Utf8Value(v8::Isolate::GetCurrent(), Nan::Get(options, Nan::New("mode").ToLocalChecked()).ToLocalChecked())) == "tile") {
std::string(*v8::String::Utf8Value(Nan::Get(options, Nan::New("mode").ToLocalChecked()).ToLocalChecked()->ToString())) == "tile") {
return mbgl::MapMode::Tile; return mbgl::MapMode::Tile;
} else { } else {
return mbgl::MapMode::Static; return mbgl::MapMode::Static;
@@ -1409,9 +1411,8 @@ NodeMap::NodeMap(v8::Local<v8::Object> options)
, crossSourceCollisions([&] { , crossSourceCollisions([&] {
Nan::HandleScope scope; Nan::HandleScope scope;
return Nan::Has(options, Nan::New("crossSourceCollisions").ToLocalChecked()).FromJust() return Nan::Has(options, Nan::New("crossSourceCollisions").ToLocalChecked()).FromJust()
? Nan::Get(options, Nan::New("crossSourceCollisions").ToLocalChecked()) ? Nan::To<bool>(Nan::Get(options, Nan::New("crossSourceCollisions").ToLocalChecked())
.ToLocalChecked() .ToLocalChecked()).ToChecked()
->BooleanValue()
: true; : true;
}()) }())
, mapObserver(NodeMapObserver()) , mapObserver(NodeMapObserver())
@@ -1419,7 +1420,7 @@ NodeMap::NodeMap(v8::Local<v8::Object> options)
, map(std::make_unique<mbgl::Map>(*frontend, mapObserver, , map(std::make_unique<mbgl::Map>(*frontend, mapObserver,
mbgl::MapOptions().withSize(frontend->getSize()) mbgl::MapOptions().withSize(frontend->getSize())
.withPixelRatio(pixelRatio) .withPixelRatio(pixelRatio)
.withMapMode(mode) .withMapMode(mbgl::MapMode::Static)
.withCrossSourceCollisions(crossSourceCollisions), .withCrossSourceCollisions(crossSourceCollisions),
mbgl::ResourceOptions().withPlatformContext(reinterpret_cast<void*>(this)))) mbgl::ResourceOptions().withPlatformContext(reinterpret_cast<void*>(this))))
, async(new uv_async_t) { , async(new uv_async_t) {
@@ -1468,4 +1469,12 @@ bool NodeFileSource::canRequest(const mbgl::Resource&) const {
return true; return true;
} }
void NodeFileSource::setResourceOptions(mbgl::ResourceOptions options) {
this->_resourceOptions = std::move(options);
}
mbgl::ResourceOptions NodeFileSource::getResourceOptions() {
return this->_resourceOptions.clone();
}
} // namespace node_mbgl } // namespace node_mbgl

View File

@@ -2,6 +2,7 @@
#include <mbgl/map/map.hpp> #include <mbgl/map/map.hpp>
#include <mbgl/storage/file_source.hpp> #include <mbgl/storage/file_source.hpp>
#include <mbgl/storage/resource_options.hpp>
#include <mbgl/util/async_request.hpp> #include <mbgl/util/async_request.hpp>
#include <mbgl/util/image.hpp> #include <mbgl/util/image.hpp>
@@ -100,7 +101,10 @@ struct NodeFileSource : public mbgl::FileSource {
~NodeFileSource() override = default; ~NodeFileSource() override = default;
std::unique_ptr<mbgl::AsyncRequest> request(const mbgl::Resource&, mbgl::FileSource::Callback) final; std::unique_ptr<mbgl::AsyncRequest> request(const mbgl::Resource&, mbgl::FileSource::Callback) final;
bool canRequest(const mbgl::Resource&) const override; bool canRequest(const mbgl::Resource&) const override;
void setResourceOptions(mbgl::ResourceOptions) override;
mbgl::ResourceOptions getResourceOptions() override;
NodeMap* nodeMap; NodeMap* nodeMap;
mbgl::ResourceOptions _resourceOptions;
}; };
} // namespace node_mbgl } // namespace node_mbgl

View File

@@ -7,6 +7,7 @@
#include <mbgl/util/run_loop.hpp> #include <mbgl/util/run_loop.hpp>
#include <mbgl/gfx/backend.hpp> #include <mbgl/gfx/backend.hpp>
#include <mbgl/gfx/backend_scope.hpp>
#include "node_map.hpp" #include "node_map.hpp"
#include "node_logging.hpp" #include "node_logging.hpp"
@@ -33,7 +34,7 @@ void RegisterModule(v8::Local<v8::Object> target, v8::Local<v8::Object> module)
Nan::SetMethod(target, "setBackendType", SetBackendType); Nan::SetMethod(target, "setBackendType", SetBackendType);
node_mbgl::NodeMap::Init(target); node_mbgl::NodeMap::Init(target);
node_mbgl::NodeRequest::Init(); node_mbgl::NodeRequest::Init(target);
node_mbgl::NodeExpression::Init(target); node_mbgl::NodeExpression::Init(target);
// Exports Resource constants. // Exports Resource constants.

View File

@@ -26,7 +26,8 @@ NodeRequest::~NodeRequest() {
Nan::Persistent<v8::Function> NodeRequest::constructor; Nan::Persistent<v8::Function> NodeRequest::constructor;
void NodeRequest::Init() { void NodeRequest::Init(v8::Local<v8::Object> target) {
v8::Local<v8::Context> context = target->CreationContext();
v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New); v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
tpl->InstanceTemplate()->SetInternalFieldCount(1); tpl->InstanceTemplate()->SetInternalFieldCount(1);
@@ -34,7 +35,7 @@ void NodeRequest::Init() {
Nan::SetPrototypeMethod(tpl, "respond", HandleCallback); Nan::SetPrototypeMethod(tpl, "respond", HandleCallback);
constructor.Reset(tpl->GetFunction()); constructor.Reset(tpl->GetFunction(context).ToLocalChecked());
} }
void NodeRequest::New(const Nan::FunctionCallbackInfo<v8::Value>& info) { void NodeRequest::New(const Nan::FunctionCallbackInfo<v8::Value>& info) {
@@ -49,7 +50,12 @@ void NodeRequest::New(const Nan::FunctionCallbackInfo<v8::Value>& info) {
Nan::Set(info.This(), Nan::New("url").ToLocalChecked(), info[3]); Nan::Set(info.This(), Nan::New("url").ToLocalChecked(), info[3]);
Nan::Set(info.This(), Nan::New("kind").ToLocalChecked(), info[4]); Nan::Set(info.This(), Nan::New("kind").ToLocalChecked(), info[4]);
v8::Local<v8::Value> argv[] = { info.This() }; v8::Local<v8::Value> argv[] = { info.This() };
request->asyncResource->runInAsyncScope(Nan::To<v8::Object>(target->handle()->GetInternalField(1)).ToLocalChecked(), "request", 1, argv); request->asyncResource->runInAsyncScope(
Nan::To<v8::Object>(target->handle()->GetInternalField(1)).ToLocalChecked(),
"request",
1,
argv
);
info.GetReturnValue().Set(info.This()); info.GetReturnValue().Set(info.This());
} }

View File

@@ -29,7 +29,7 @@ public:
static Nan::Persistent<v8::Function> constructor; static Nan::Persistent<v8::Function> constructor;
static void Init(); static void Init(v8::Local<v8::Object>);
static void New(const Nan::FunctionCallbackInfo<v8::Value>&); static void New(const Nan::FunctionCallbackInfo<v8::Value>&);
static void HandleCallback(const Nan::FunctionCallbackInfo<v8::Value>&); static void HandleCallback(const Nan::FunctionCallbackInfo<v8::Value>&);

View File

@@ -4,7 +4,7 @@ var mockfs = require('../mockfs');
var mbgl = require('../../index'); var mbgl = require('../../index');
var test = require('tape'); var test = require('tape');
[ 'sprite_png', 'sprite_json', 'source_vector', 'glyph' ].forEach(function (resource) { [ 'sprite_png', 'sprite_json', 'source_vector' ].forEach(function (resource) {
test(`render reports an error when the request function responds with an error (${resource})`, function(t) { test(`render reports an error when the request function responds with an error (${resource})`, function(t) {
var map = new mbgl.Map({ var map = new mbgl.Map({
request: function(req, callback) { request: function(req, callback) {
@@ -144,7 +144,12 @@ test(`render does not report an error from rendering a previous style`, function
var map = new mbgl.Map({ var map = new mbgl.Map({
request: function(req, callback) { request: function(req, callback) {
var data = mockfs.dataForRequest(req); var data = mockfs.dataForRequest(req);
if (mockfs.source_vector === data) { if (
mockfs.source_vector === data &&
// By default tiles load sources/v3 as well, which is vector tile,
// messing things up.
req.url !== 'maptiler://sources/v3'
) {
callback(new Error('message')); callback(new Error('message'));
} else { } else {
callback(null, { data: data }); callback(null, { data: data });

View File

@@ -20,19 +20,22 @@ var tile_vector = readFixture('vector.tile');
function dataForRequest(req) { function dataForRequest(req) {
if (req.url === null) { if (req.url === null) {
return null; return null;
} else if (req.url.indexOf('sprite') > -1 && req.url.endsWith('json')) { } else if (req.url.includes('sprite') && req.url.endsWith('.json')) {
return sprite_json; return sprite_json;
} else if (req.url.indexOf('sprite') > -1 && req.url.endsWith('png')) { } else if (req.url.includes('sprite') && req.url.endsWith('.png')) {
return sprite_png; return sprite_png;
} else if (req.url.indexOf('fonts') > -1 && req.url.endsWith('pbf')) { } else if (req.url.includes('fonts/') && req.url.endsWith('.pbf')) {
return glyph; return glyph;
} else if (req.url.endsWith('mapbox.satellite')) { } else if (req.url.includes('sources/satellite')) {
return source_raster; return source_raster;
} else if (req.url.indexOf('satellite') > -1 && (req.url.endsWith('png') || req.url.endsWith('webp'))) { } else if (req.url.includes('tiles/satellite') && (req.url.endsWith('png') || req.url.endsWith('webp') || req.url.endsWith('jpg'))) {
return tile_raster; return tile_raster;
} else if (req.url.endsWith('mapbox.mapbox-streets-v7')) { } else if (
req.url === 'https://api.maptiler.com/tiles/v3/tiles.json?key=uwvyvzaF2P7UWbyOEvjU' ||
req.url === 'maptiler://sources/v3'
) {
return source_vector; return source_vector;
} else if (req.url.indexOf('streets') > -1 && req.url.endsWith('pbf')) { } else if (req.url.includes('tiles/tiles') && req.url.endsWith('pbf')) {
return tile_vector; return tile_vector;
} else { } else {
return null; return null;

View File

@@ -23,7 +23,7 @@ void Mailbox::open(Scheduler& scheduler_) {
if (closed) { if (closed) {
return; return;
} }
if (!queue.empty()) { if (!queue.empty()) {
auto guard = weakScheduler.lock(); auto guard = weakScheduler.lock();
if (weakScheduler) weakScheduler->schedule(makeClosure(shared_from_this())); if (weakScheduler) weakScheduler->schedule(makeClosure(shared_from_this()));

View File

@@ -179,7 +179,7 @@ void Renderer::Impl::render(const RenderTree& renderTree) {
// This guarantees that we have at least one function per tile called. // This guarantees that we have at least one function per tile called.
// When only rendering layers via the stylesheet, it's possible that we don't // When only rendering layers via the stylesheet, it's possible that we don't
// ever visit a tile during rendering. // ever visit a tile during rendering.
for (const RenderItem& renderItem : sourceRenderItems) { for (const RenderItem& renderItem : sourceRenderItems) {
renderItem.render(parameters); renderItem.render(parameters);
} }
} }

View File

@@ -27,15 +27,16 @@ void Tile::setTriedCache() {
void Tile::dumpDebugLogs() const { void Tile::dumpDebugLogs() const {
std::string kindString; std::string kindString;
switch (kind) { switch (kind) {
case Kind::Geometry: kindString = "Geometry"; break; case Kind::Geometry: kindString = "Geometry"; break;
case Kind::Raster: kindString = "Raster"; break; case Kind::Raster: kindString = "Raster"; break;
case Kind::RasterDEM: kindString = "RasterDEM"; break; case Kind::RasterDEM: kindString = "RasterDEM"; break;
default: kindString = "Unknown"; break; default: kindString = "Unknown"; break;
} }
Log::Info(Event::General, "Tile::Kind: %s", kindString.c_str()); Log::Info(Event::General, "Tile::Kind: %s", kindString.c_str());
Log::Info(Event::General, "Tile::id: %s", util::toString(id).c_str()); Log::Info(Event::General, "Tile::id: %s", util::toString(id).c_str());
Log::Info(Event::General, "Tile::renderable: %s", isRenderable() ? "yes" : "no"); Log::Info(Event::General, "Tile::renderable: %s", isRenderable() ? "yes" : "no");
Log::Info(Event::General, "Tile::complete: %s", isComplete() ? "yes" : "no"); Log::Info(Event::General, "Tile::complete: %s", isComplete() ? "yes" : "no");
Log::Info(Event::General, "Tile::loaded: %s", isLoaded() ? "yes" : "no");
} }
void Tile::queryRenderedFeatures(std::unordered_map<std::string, std::vector<Feature>>&, const GeometryCoordinates&, void Tile::queryRenderedFeatures(std::unordered_map<std::string, std::vector<Feature>>&, const GeometryCoordinates&,